What is idempotency? API retries explained with examples
By Unlearnt
Idempotency means that repeating the same operation has the same intended effect as doing it once. For an API, that matters when a client cannot tell whether its first request succeeded. A timeout can mean the reply was lost, not that the operation failed.
Same effect, even when the response changes
Compare ‘set the delivery address to 12 Oak Street’ with ‘add another delivery address’. Repeating the first instruction leaves the intended address unchanged. Repeating the second may create another record. Idempotency is about the effect of the operation, not whether a function happens to return the same text.
HTTP defines PUT and DELETE as idempotent, along with safe methods such as GET. A second DELETE might return ‘not found’ after the first removed the resource; the intended outcome is still that the resource is absent. Logging each request separately does not make the intended deletion non-idempotent. POST does not provide the same promise by default.
Worked example: an order succeeds, but the reply disappears
Imagine an order API. A customer submits one order for a £40 lamp. The server creates order 731, but the connection closes before the browser receives the confirmation. The customer sees a timeout. Creating a new order on every retry could turn one purchase into two.
Instead, suppose this API accepts an idempotency key identifying the intended purchase. The browser retries with the same key and the same order details. The server recognises the already recorded operation and returns its result instead of creating order 732. If the customer deliberately buys another lamp later, that is a new purchase and needs a different key.
The important boundary is the purchase attempt, not simply identical order contents. Two intentional purchases of the same lamp should remain possible. In this hypothetical design, one key means ‘this particular operation’, not ‘all orders that look alike’.
Two requests. One purchase.
Browser
Order API
Send order
Purchase key: lamp-A
Create order 731
No reply received
The browser times out
Confirmation is lost
Retry same order
Same key: lamp-A
Return order 731
When should you reuse an idempotency key?
Keep the key tied to the intended operation, not to each network attempt. If the browser creates a fresh key every time the customer presses retry, the server may see a new purchase rather than another attempt to finish the original one. A request ID used for tracing may change on each attempt; an idempotency key should remain stable across retries of the same operation.
In the lamp example, preserve or recover the purchase identity when the page reloads. How you do that depends on the app: an existing order or checkout record may help. Neither a disabled button nor identical cart contents is enough to identify the same purchase reliably.
| Situation | Key decision | Why |
|---|---|---|
| Reply lost after placing the order | Retry with the same key and payload, within the supported retention period. | The outcome is uncertain; this is not a new purchase. |
| Page reloaded while purchase is unresolved | Recover the original operation and key; check its status. | Reloading the interface does not create a new intention. |
| Customer deliberately buys a second lamp | Use a new operation and key. | Identical contents can represent two legitimate purchases. |
| Quantity or price changed | Do not silently replay changed details under the old key. Resolve the original operation first. | A new key alone does not cancel an earlier successful order. |
| Key may have expired | Check the durable order or payment status before resubmitting. | The provider may treat the same key as new after its record expires. |
A key only helps if the server enforces it
A client-generated key is an identifier, not protection by itself. The server needs to coordinate claiming the key and recording the effect so concurrent retries cannot both proceed as new work. It also needs a defined policy for a reused key with different parameters, an operation still in progress, and expired records.
Stripe's API is one concrete implementation: it stores a result for an idempotency key, checks that later parameters match, and documents when keys may be removed. Those are product-specific semantics. Check the API's contract rather than assuming every service handles retries or retains keys forever.
Misconception: idempotent means the code runs only once
A request can arrive multiple times, enter a handler multiple times, and generate several log lines while still having one intended business effect. Conversely, returning the same cached ‘success’ response proves little if the system has already created two orders.
For the lamp example, inspect the order records and any downstream effects, not just the HTTP status. If the order service suppresses duplicates but sends a new fulfilment message on every retry, the retry boundary may still be wrong. A local idempotency mechanism does not automatically provide exactly-once behaviour across every other service.
Check your understanding
Optional: apply the idea to a new scenario.
A warehouse receives a ‘reduce stock by one’ message twice because delivery was retried. What identifier should it use to recognise the duplicate, and why would deduplicating only by product ID block legitimate sales?
Answer with the tutor →Free to start, no account needed.