Contact

What is Idempotency?

Definition

Idempotency is the property of an operation that produces the same effect on a system whether it is applied once or repeated several times. In distributed systems, where timeouts and network failures cause requests to be retried, it ensures a retry does not charge a card twice or create a duplicate record. GET, PUT and DELETE are idempotent by definition in HTTP; operations like POST are made safe to retry with techniques such as idempotency keys.

Also known as: idempotent, idempotence, idempotency key, idempotent request

Idempotency sequence: a payment retried with the same key is not charged twice and the API returns the original result

“Set” versus “add”

The word comes from mathematics: an operation is idempotent if applying it again to its own result changes nothing. In software the intuition is simple. Running “mark order 1042 as shipped” three times leaves the same state as running it once. Running “add £100 to the balance” three times adds £300. The first sets a value, the second increments one, and most idempotency bugs come from an operation of the second kind being repeated without anyone noticing.

What counts is the effect on the system, not the response. A delete may answer “deleted” the first time and “not found” the second, and still be idempotent. How the HTTP methods line up against this property is shown in the table under HTTP method.

Duplicate requests are a fact of life

Any distributed system that assumes “the same request never arrives twice” will eventually be proven wrong. The usual culprits:

  • A client times out and retries, although the server processed the first attempt and only the response was lost.
  • A user double-clicks a slow “Pay now” button.
  • A phone switches from Wi-Fi to mobile data mid-request.
  • A load balancer or SDK retries automatically after an error.
  • Message queues and webhook senders typically guarantee at-least-once delivery, so the same message can arrive again.

Exactly-once delivery over a network is not achievable in practice. What real systems do is combine at-least-once delivery with idempotent processing, so that the effect happens exactly once.

How an idempotency key works

POST is not idempotent by nature, so for critical operations such as payments the client generates a unique key per operation and sends it in a header:

POST /v1/payments HTTP/1.1
Host: api.example.com
Idempotency-Key: "f1c7a2d4-3b9e-4e58-9a61-0d2b7c5e8f13"
Content-Type: application/json

{"order": "A-77812", "amount": "59.00", "currency": "EUR"}
  1. If the key is new, the server records it together with a fingerprint of the request body, performs the operation, then stores the status code and response body against the key.
  2. If the same key arrives with the same body, nothing is executed again; the stored response is replayed.
  3. If the same key arrives with a different body, the client has made a mistake and the request is rejected.
  4. If a duplicate arrives while the original is still in progress, it gets a conflict error instead of waiting.

Payment gateways are the classic users of this pattern. Stripe, for example, saves the status code and body of the first request for a key, even when it failed, allows keys up to 255 characters, and may prune keys once they are at least 24 hours old. An IETF draft aims to standardize the Idempotency-Key header and suggests 400 for a missing key, 422 for a key reused with a different payload and 409 for a duplicate of a request still being processed. As of October 2026 it is an expired draft rather than an RFC, so behaviour varies between providers.

Implementation pitfalls

  • A new key on every attempt. Generate the key once when the operation starts and resend it unchanged on every retry. A client that mints a fresh UUID per attempt gets no protection at all.
  • Check-then-insert races. If “have I seen this key?” and “record the key” are separate steps, two simultaneous duplicates can both pass. Put a unique constraint on the key and write it in the same database transaction as the business change.
  • Unscoped keys. Scope keys per account, so two customers who happen to send the same value never see each other's responses.
  • Undocumented retention. State how long keys are kept; a retry arriving after that is processed as a new request.
  • Personal data as keys. Do not use email addresses or similar identifiers. A random UUID does the job.

Webhook consumers apply the same logic from the receiving end: insert the event ID into a processed-events table with a unique constraint, and if it is already there, acknowledge the delivery without re-running side effects such as sending emails or decrementing stock.

Related terms

← Back to the glossary