Idempotency
Retry safety for API operations
Idempotency ensures that retrying the same request produces the same result. If a request fails due to network issues, you can safely retry it without creating duplicate resources (e.g. duplicate payments or accounts).
How it works
Include an Idempotency-Key header when making POST requests that create or modify resources:
response = await client.post(
"https://api.sandbox.billingrails.com/v1/accounts",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Idempotency-Key": str(uuid.uuid4())
},
json={"name": "John Doe", "email": "john.doe@example.com"}
)The key must be unique and no longer than 180 characters. Billingrails stores the key and response. When a request with a unique idempotency key is successfully processed, the response is cached for 24 hours. On subsequent requests with the same key:
- Same key + same data → Returns the original response (cached)
- Same key + different data → Returns 409 Conflict
- Different key → Creates a new resource
Keys are scoped per API key, so different API keys have independent idempotency namespaces. If two requests with the same idempotency key arrive at the same time and the first is still being processed, the second receives 409 Conflict.
All POST requests support idempotency keys. GET, PUT, and DELETE are inherently idempotent; any idempotency key sent with them is ignored.
Best practices
Do:
- Use UUIDs for idempotency keys (guaranteed unique)
Don't:
- Use timestamps (clock skew can defeat idempotency)
- Reuse keys for different logical operations
- Use the same key across different routes within the cache window (the first response is returned)
When to retry
| Error | Action |
|---|---|
| Network / timeout | Retry with same key, exponential backoff |
| 5xx server error | Retry with same key, exponential backoff |
| 409 Conflict | Check original request; use new key if needed |
| Other 4xx | Don't retry — fix request data first |
Related
- Error handling — Handling conflicts and other errors
- Rate limits — Safe retries after rate limiting