Idempotency
API Design
Visual Representation
What is it?
🔘 Think of it like an elevator button:
You press the elevator "up" button. Nothing happens for a few seconds. You press it again... and again... and again. Does the elevator come 4 times? NO! Pressing it 1 time or 100 times has the SAME result — the elevator comes once.
That's idempotency — doing something multiple times produces the SAME result as doing it once.
In APIs: an idempotent operation means if you accidentally send the same request 5 times (due to network retries, user double-clicks, etc.), the result is the same as sending it once. Your bank account isn't charged 5 times!
💡 Simple Summary: Idempotent = safe to retry. Doing it 1 time or 100 times gives the same outcome. Critical for preventing duplicate charges, orders, or messages.
How it works — Like you're watching it happen
The nightmare scenario WITHOUT idempotency:
WITH idempotency (how it should work):
❓ But wait — aren't GET requests already idempotent? Why is this mainly about POST?
Exactly! GET, PUT, DELETE are naturally idempotent by design. GET /user/123 returns the same user whether you call it 1 or 100 times. PUT /user/123 sets the user to a specific state regardless of how many times you call it. DELETE /user/123 deletes the user — calling again just returns "already deleted." But POST /orders CREATES a new resource each time — that's why POST needs explicit idempotency protection.
Why should you care? (Interview perspective)
Key Things to Remember
Real Examples You Use Daily
💳 Stripe payments — Every POST to /charges includes an Idempotency-Key header. If your server crashes after charging but before recording it, the retry won't double-charge. Stripe literally built their business on this guarantee.
🛒 Amazon orders — Click "Place Order" twice due to slow network? Amazon's idempotency logic ensures only ONE order is created. They use the cart session + checkout intent as a natural idempotency key.
💬 WhatsApp messages — Send a message on flaky network. Your phone might retry sending. WhatsApp uses message IDs (client-generated UUID) to ensure the same message isn't delivered twice — even if sent multiple times.
🚗 Uber ride requests — Tap "Request Ride" in a tunnel with bad signal. Phone retries. Uber ensures you get ONE ride, not three. The ride request has a unique intent ID that prevents duplicates.
Common Mistakes in Interviews
❌ Forgetting WHY it matters — "Idempotency means same result multiple times." But WHY? Because networks are unreliable, retries happen, and without it, you get duplicate charges/orders. Always connect to the real-world impact.
❌ Thinking server generates the key — If the server generated the idempotency key, you'd need to receive the response to get it. But the problem IS that you didn't receive the response! Client MUST generate it before sending.
❌ Not handling concurrent retries — Two retries arrive at the same millisecond. Without a database UNIQUE constraint, both might process. Use DB uniqueness + check-then-act pattern.
❌ Storing idempotency keys forever — Unbounded storage growth! Set a TTL (24-48 hours). If someone retries after 48 hours, it's not a retry — it's a new request.
❌ Calling GET idempotent "because it returns the same thing" — No! GET /random returns different values each time. It's still idempotent because it has NO SIDE EFFECTS. Idempotency is about not changing state, not about returning identical responses.
🎯 Interview One-Liner
"I'd ensure idempotency for all mutating operations using a client-generated idempotency key stored server-side with a unique constraint — so network retries and duplicate requests are safely handled without double-processing, critical for payment and order flows."
Interview Q&A
Q: How would you implement an idempotent payment API?
Client generates a UUID idempotency key before the request. Sends POST /payments with Idempotency-Key header. Server logic: (1) Check if key exists in DB → if yes, return stored response. (2) If no, start transaction: insert key with "processing" status, process payment, update key with response, commit. (3) Return response. Concurrent duplicates: DB UNIQUE constraint on key ensures only one processes. The other gets a constraint violation and queries for the stored response.
Q: What happens if the server crashes DURING processing (after deducting money but before storing the idempotency response)?
This is the hardest case. Solutions: (1) Use database transactions — payment deduction and idempotency key storage happen in ONE atomic transaction. Both succeed or both fail. (2) Two-phase approach: first mark key as "processing," then process, then mark "complete." On crash recovery: find "processing" keys and check if payment actually went through (reconciliation). (3) In practice, payment gateways (Stripe) handle this by providing their own idempotency.
Q: Client retries a request after 2 days. Should idempotency key still be honored?
No — set a reasonable TTL (24-48 hours). After expiration, treat it as a new request. Why? (1) Storage would grow unbounded. (2) After 2 days, context has changed — it's likely an intentional new action, not a retry. (3) Most retries happen within seconds to minutes. Document the TTL in API docs so clients know the guarantee window.
Q: How do you make DELETE idempotent?
DELETE /users/123 — first call deletes the user, returns 200. Second call: user already deleted. Instead of returning 404 (which suggests an error), return 200 or 204 (the desired state "user doesn't exist" is achieved). The end state is the same regardless of how many times you call it. Some APIs return 404 on subsequent deletes — technically fine but less "idempotent-feeling" for the client.
Q: PUT vs POST — why is PUT naturally idempotent?
PUT /users/123 with body {name: "Prakshay", age: 25} SETS the user to exactly that state. Call it once: user is {name: "Prakshay", age: 25}. Call it 100 times: user is still {name: "Prakshay", age: 25}. Same result. POST /users creates a NEW user each time. Call it 100 times → 100 users created! That's why POST needs explicit idempotency keys.
Quick Quiz
1/5You click "Pay ₹5000" but your network is flaky. Your app retries the payment request 3 times. Without idempotency, what happens?