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.

[1]

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

A simplified API that records the operation and recognises the same key on retry. A new intended purchase needs a new key.

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.

Decisions for the same intended purchase. Follow the service's documented retry contract.
SituationKey decisionWhy
Reply lost after placing the orderRetry 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 unresolvedRecover the original operation and key; check its status.Reloading the interface does not create a new intention.
Customer deliberately buys a second lampUse a new operation and key.Identical contents can represent two legitimate purchases.
Quantity or price changedDo 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 expiredCheck the durable order or payment status before resubmitting.The provider may treat the same key as new after its record expires.

[2][3]

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.

[2]

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.

Sources

  1. RFC 9110, section 9.2.2: Idempotent Methods
  2. Stripe API: Idempotent requests
  3. AWS Builders' Library: Making retries safe with idempotent APIs