How HTTP Works

Glossary Term

Idempotency Key (Safe POST Retries)

An Idempotency-Key header lets a client safely retry a POST: the server stores the first result and replays it. Learn the protocol, edge cases and conflicts.

Reviewed 2 min readintermediate3 sourcesMarkdown
On this page

TL;DR: POST is not idempotent, so a retry after a timeout can create a duplicate. An idempotency key makes it safe: the client sends a unique key, the server stores the first result under it and replays that result on repeats.

An idempotency key is a unique identifier, sent in an Idempotency-Key header, that lets a server recognize repeated attempts at the same logical operation and execute it only once. It gives non-idempotent methods like POST the retry safety that idempotent methods like PUT have by definition.

The exchange

POST /v1/payments HTTP/1.1
Host: api.example.com
Idempotency-Key: 8e03978e-40d5-43e8-bc93-6894a57f9324
Content-Type: application/json

{"amount": 4200, "currency": "usd"}

The connection drops before the response arrives. The client retries with the same key and the server returns the stored response, not a second charge:

HTTP/1.1 201 Created
Content-Type: application/json
Idempotent-Replayed: true

{"id": "pay_123", "status": "succeeded"}

The Idempotent-Replayed header is a convention some APIs use, not part of the draft.

What the server has to do

  1. Look up the key before doing any work.
  2. If it is new, record the key with a fingerprint of the request, process it, and store the status and body.
  3. If it exists and the request matches, return the stored response.
  4. If it exists but the payload differs, reject it (the draft suggests 422).
  5. If the first request is still running, reject with 409 Conflict rather than running twice.

Non-obvious facts

  • Store the result, including failures. Stripe saves the status and body of the first request for a key, even if it was a 500. Re-running a failed operation under the same key would defeat the point.
  • Keys expire. Stripe prunes keys after at least 24 hours; document your own retention so clients know how long a retry is safe.
  • The check and the write must be atomic. A unique database constraint on the key, not a read-then-write, is what stops two concurrent retries from both proceeding.
  • Scope keys per client. Otherwise one tenant’s key can collide with another’s or leak a stored response.
  • The key identifies the operation, not the attempt. Generating a new key on every retry silently disables the protection.

Go deeper

Frequently asked questions

What is an idempotency key?

A unique value the client generates for one logical operation and sends in an Idempotency-Key header, so the server can recognize a retry and return the original result instead of doing the work twice.

Is Idempotency-Key a standard?

Not yet. It is an IETF Internet-Draft from the HTTPAPI working group and widely used in practice, with Stripe as the best-known implementation. RFC 9110 does not define it.

What should the server return if the same key arrives with a different body?

An error. The draft recommends 422 for a key reused with a different payload and 409 when the original request is still being processed.

Should I use a UUID?

A random UUID v4 is the usual choice. It must be unique per operation, not per attempt, and the client must reuse the same value on every retry of that operation.

Sources

  1. IETF draft: The Idempotency-Key HTTP Header Fielddatatracker.ietf.org
  2. RFC 9110: Idempotent Methodsrfc-editor.org
  3. Stripe API: Idempotent requestsdocs.stripe.com

Keep going

Browse /search