Skip to content

Idempotency

A request can time out after the API has already acted on it. To retry a write without doing it twice, send an Idempotency-Key header: any unique string of up to 255 printable ASCII characters, such as a UUID you generate for each operation.

Terminal window
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: 6f1c2e9a-8b3d-4c47-9a51-0d2f7e4b8c13" \
-H "Content-Type: application/json" \
-d '{ … }' \
https://api.limitry.com/v1/…

The first request with a key runs normally. A retry with the same key and the same request gets the first response again, with the same status and body, and doesn’t run the operation a second time. A replayed response carries the header Idempotency-Replayed: true.

What the SDKs, the CLI and the MCP server do

Section titled “What the SDKs, the CLI and the MCP server do”
  • Every write sends a key. If you don’t pass one, the client makes one for that call.
  • The client retries a failed attempt with the same key. That covers a network error, a timeout, a 5xx, a 429, and a 409 with the code idempotency_in_progress. It tries each call up to 3 times in all, waiting for Retry-After when the API sends one. A Retry-After longer than 20 seconds comes back to you as the error instead.
  • Pass your own key when you might run the whole call again yourself, such as a job that reruns or a webhook you receive twice. Send the same key both times:
    • TypeScript: { idempotencyKey: id } as the last argument.
    • Python: request_options={"idempotency_key": id}.
    • CLI: --idempotency-key <key>. A command that makes several writes derives one key per write from yours, so running it again with the same key repeats none of them.
    • MCP: every tool that changes something takes an optional idempotencyKey argument.
  • Turn the retries off with maxRetries: 0 in TypeScript or max_retries=0 in Python.
  • Checks follow the same rules. The same key and the same check get the first decision back, marked with Idempotency-Replayed: true, and count nothing again. That holds for a check across several subjects too. The same key with a different check gets 409 idempotency_key_reused.
  • guard and reserve send a key for you. They send a check that fails fast once more with the same key, within the time guard and reserve wait. If a reservation’s check gets no answer in time, your work runs under the fail mode. Afterwards, reserve asks again with the same key and releases any reservation Limitry had made, so it isn’t left taken until it expires.
  • guard finishes what timed out. When its check gets no answer in time and the limit fails open, guard sends it once more in the background with the same key and limitry-finish-only: 1: Limitry finishes a check it began, and decides nothing afresh (204 when there was none).
  • Committing and releasing are safe to repeat without a key. The same commit or release gets the same answer. Committing again with a different cost gets 400.
  • Writes only. The header applies to POST, PUT, PATCH and DELETE. Reads are safe to repeat anyway, and ignore it.
  • One key per request. Using a key again for a different request, with a different endpoint or body, fails with a 409 and the code idempotency_key_reused. Generate a new key for each operation, and reuse it only to retry that operation.
  • A retry during the first request waits. If the first request is still running, the retry gets 409 idempotency_in_progress and a Retry-After header. Retry after that many seconds.
  • We keep only a final answer. Every retry gets back a success, or an error a retry would get again, such as a 400 or a 404. The errors a retry can get past aren’t kept, so a retry with the same key runs again:
    • a 5xx;
    • a 402, once you’ve upgraded your plan;
    • a 409;
    • a 429, once there’s room again.
  • Per credential, environment and workspace, for an hour on a check and 24 hours on everything else (a commit, a release, a credit, a reset). A key used again after that is a new request, with one exception: a credit’s key is remembered for good, so a new credit sent with a key already used for a credit is ignored, however long after. Never reuse a credit’s key. A key belongs to the API key or token that sent it, so two integrations never collide. A personal access token’s keys are separate in each workspace and each environment.
  • We protect stored answers. We keep a hash of your key, never the key itself, and we encrypt the stored answer with a key derived from yours. Only a retry that sends your key can read it back.

Sending a key yourself is optional: the SDKs, the CLI and the MCP server send one on every write.