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

ErrorAction
Network / timeoutRetry with same key, exponential backoff
5xx server errorRetry with same key, exponential backoff
409 ConflictCheck original request; use new key if needed
Other 4xxDon't retry — fix request data first

On this page