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 vs idempotent
Section titled “Safe vs idempotent”- Safe — the request has no observable effect on server state; it only reads.
GET,HEAD, andOPTIONSare safe. - Idempotent — making the request N times leaves the server in the same state as making it once.
GET,HEAD,OPTIONS,PUT, andDELETEare idempotent.POSTand (in general)PATCHare 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] Why it matters: retries
Section titled “Why it matters: retries”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.
Making POST safe to retry
Section titled “Making POST safe to retry”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.
Common mistakes
Section titled “Common mistakes”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\}/balancewith{ "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-Keyheader onPOST /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:
PUTobject 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