Skip to content

Idempotency & Safety

Two properties decide whether a request can be safely repeated. They are easy to mix up, so let’s pin them down precisely.

  • Safe — the request has no observable effect on server state; it only reads. GET, HEAD, and OPTIONS are safe.
  • Idempotent — making the request N times leaves the server in the same state as making it once. GET, HEAD, OPTIONS, PUT, and DELETE are idempotent. POST and (in general) PATCH are not.

Every safe method is idempotent, but not every idempotent method is safe — DELETE changes state yet is idempotent.

flowchart TD
  M{Method} --> GET[GET / HEAD: safe + idempotent]
  M --> PUTDEL[PUT / DELETE: idempotent, not safe]
  M --> POST[POST: neither]
  M --> PATCH[PATCH: usually neither]
Where each method sits on the safe/idempotent grid

Networks drop responses. When a client (or proxy, or load balancer) does not hear back, it may retry. If the method is idempotent, retrying is harmless. If it is not — like a POST that charges a card — a blind retry could charge twice.

JavaScript

When you genuinely need a non-idempotent create to tolerate retries, use an idempotency key: the client sends a unique Idempotency-Key header, and the server remembers the result for that key so a repeat returns the original outcome instead of acting twice. We cover the mechanics in Versioning & Caching.

A non-idempotent POST with no guard Symptom:

  • A user double-clicks submit — two orders get created
  • A network retry charges a card twice
  • Require an idempotency key for sensitive POSTs like payments

A PATCH that’s deliberately not idempotent Symptom:

  • PATCH /accounts/\{id\}/balance with { "increment": 100 } — repeating it adds again
  • A client retry silently inflates the balance
  • Use an absolute value instead: { "balance": 1100 }

💡 Real-world examples

Stripe API:

  • An Idempotency-Key header on POST /charges
  • Send the same key again and Stripe returns the original response instead of charging again
  • The client can store the key and retry safely

AWS S3:

  • PUT object is idempotent — uploading the same file again overwrites it instead of duplicating it
  • Makes retries safe for large file uploads

Sources: Stripe idempotent requests, AWS S3 PutObject

Which statement is true?
Why is retrying a non-idempotent POST risky?
What lets a non-idempotent create tolerate retries safely?