An API call is idempotent if making it once and making it many times leaves the system in the same state. This sounds abstract until you consider what happens without it: a client sends a payment request, the network times out before the response arrives, the client retries assuming failure, and now the customer has been charged twice. Idempotency is what makes retries safe, and retries are unavoidable in any distributed system where networks fail.
What changed in 2026
- Idempotency-Key became a de facto standard header across major payment and infrastructure APIs, following the pattern popularized by Stripe, and an IETF draft has been working toward formalizing it as a standard HTTP header.
- More frameworks added built-in idempotency key support, reducing the amount of custom middleware teams had to write to deduplicate retried requests.
- Event-driven architectures pushed idempotency further upstream, since message brokers and queues generally guarantee at-least-once delivery, meaning consumers have to be idempotent regardless of what the API layer does.
- Idempotency discussions expanded beyond payments into AI agent tool-calling, where an agent retrying a failed tool call can trigger duplicate real-world side effects if the underlying API is not idempotent.
Idempotent vs safe vs neither
| HTTP method |
Safe (no side effects) |
Idempotent (repeatable, same result) |
| GET |
Yes |
Yes |
| HEAD |
Yes |
Yes |
| PUT |
No |
Yes, by spec |
| DELETE |
No |
Yes, by spec |
| PATCH |
No |
Not guaranteed |
| POST |
No |
Not guaranteed |
Safety and idempotency are related but distinct. A safe method has no side effects at all — GET should never change server state. An idempotent method can have side effects, but repeating it should not compound them. DELETE /users/42 called twice still results in user 42 being deleted; the second call just finds nothing left to delete, which is a reasonable idempotent outcome even if it returns a 404 the second time.
Why POST is the hard case
POST is defined as non-idempotent because its canonical use, creating a new resource, naturally produces a different result each time by default: two identical POST requests to create an order would normally create two separate orders. This is exactly the scenario that breaks under network retries. A client cannot safely retry a POST without risking duplication, unless the server gives it a way to signal "this is the same request as before."
Implementing idempotency keys
The standard pattern: the client generates a unique key, usually a UUID, and sends it with the request, commonly as an Idempotency-Key header. The server records which keys it has already processed and, on seeing a repeated key, returns the original result instead of performing the action again.
- Generate the key once per logical operation, on the client, before the first attempt — not a new key per retry, which would defeat the purpose.
- Store the key with the response, not just a boolean, so retries can return the original result rather than an error or empty response.
- Set a reasonable expiration on stored keys — indefinite storage is rarely necessary, and a window of 24 hours or so covers realistic retry scenarios.
- Scope keys per client or account, not globally, to avoid collisions between unrelated clients generating similar keys.
Common misunderstandings
"PUT is idempotent, so my PUT endpoint is safe to retry." Only if you actually implemented it that way. Nothing stops a PUT handler from behaving non-idempotently, like appending to a list instead of replacing it — the HTTP method is a contract with the caller, not an automatic guarantee.
"Idempotent means the response is identical every time." Not quite. The response can legitimately differ (a DELETE might return 200 the first time and 404 afterward), as long as the end state of the resource is the same.
FAQ
Is DELETE always idempotent in practice?
It should be by design, though some implementations return an error on the second call instead of a success, which arguably still satisfies the state-based definition of idempotency even if the HTTP status differs.
Do idempotency keys need to be globally unique?
They need to be unique within the scope the server checks them against, typically per API key or account, not necessarily globally across every client of the API.
How is idempotency different from exactly-once delivery?
Idempotency is a property of the operation; exactly-once delivery is a property of a messaging system. Combined, they let you build a system where at-least-once delivery plus an idempotent handler behaves like exactly-once processing from the caller's perspective.
Should GraphQL mutations be idempotent?
The GraphQL spec does not mandate it the way HTTP does for PUT and DELETE, so it is entirely up to the implementation — worth documenting explicitly per mutation if retry safety matters to your clients.
Where to go next