An idempotent operation produces the same result no matter how many times it’s applied — a genuinely important property for any API operating over an unreliable network, where a client can never be fully certain whether a request actually reached the server.
# WITHOUT idempotency: retrying a failed payment request can charge the customer TWICE
def charge_customer(amount):
return payment_gateway.charge(amount) # if the response is lost, a naive retry double-charges
# WITH an idempotency key: retries are safe
def charge_customer(amount, idempotency_key):
existing = db.find_by_idempotency_key(idempotency_key)
if existing:
return existing.result # already processed - return the SAME result, don't charge again
result = payment_gateway.charge(amount)
db.save_idempotency_record(idempotency_key, result)
return result
Why This Matters So Much for Payments
A client that sends a payment request but never receives the response (due to a timeout or network drop) has no way to know whether the charge actually succeeded server-side. Without an idempotency key, a “safe” retry risks a double charge; with one, the server recognizes the retry and simply returns the original result, making the retry genuinely safe regardless of what happened to the first attempt.
GET, PUT, and DELETE are naturally idempotent by HTTP convention (repeating them should have the same effect as doing it once) — POST is the one that genuinely needs an explicit idempotency mechanism, since it represents a new action by default.