Idempotency Keys Explained: Safe Retries Made Simple
Learn how idempotency keys make APIs safe to retry, with patterns for storage, expiry, conflict handling, and common pitfalls.
What you'll learn
- ✓What idempotency means in HTTP
- ✓How to store and key idempotency records
- ✓Handling conflicts and expirations
- ✓Where idempotency belongs in the stack
- ✓Common mistakes to avoid
Prerequisites
- •Familiar with HTTP and databases
What and Why
Networks fail. Retries happen. Without idempotency, a single user click can become two charges, two reservations, or two messages. Idempotency keys let clients retry safely: the server treats repeated requests with the same key as the same operation.
This is mandatory for any API that touches money, inventory, or external side effects.
Mental Model
An idempotency key is a client-chosen unique string attached to a request. The server remembers the result of that key. If the same key comes back, the server returns the original result instead of running the operation again.
Three things matter: the key, the request fingerprint, and the stored response.
Architecture
On first request, the server records (key, fingerprint, response) after the work is done. On a repeat, it checks: same key plus same fingerprint returns the cached response; same key plus different fingerprint is an error (the client reused the key for something else).
Client -> Server (Idempotency-Key: K1)
|
Lookup K1
| |
found not found
| |
same execute work
body? |
yes -> return cached
no -> 422 conflict
store (K1, fingerprint, response)
return response The storage layer is usually Redis with a TTL, or a Postgres table with an indexed key. Redis is faster; Postgres is more durable and lets you do conflict checks in the same transaction as the business work.
Trade-offs
Storing only the key is fastest but means you cannot detect “same key, different request” misuse. Storing the fingerprint catches client bugs at the cost of more memory.
A short TTL (24 hours) keeps the table small but breaks long-running flows. A long TTL covers more cases but grows storage. Match TTL to the retry window of your clients.
In-flight requests are tricky: two requests with the same key arrive simultaneously. A reservation row with a unique constraint lets the first one win and the second one wait or fail predictably.
Practical Tips
Make keys client-generated UUIDs. Server-generated keys defeat the purpose because retries cannot reuse them.
Reject very long keys and any with weird characters. A 64-character cap on alphanumerics plus dashes is a sensible policy.
Persist the response body alongside the status code so retries return byte-identical responses. Clients sometimes parse subtle fields.
Run the idempotency check inside the same transaction as the work. If you check then write in separate transactions, you have a race that lets two operations slip through.
Wrap-up
Idempotency keys are a small primitive with outsized impact. Done right, they turn flaky networks into a non-issue. Done wrong, they create a false sense of safety. Treat the idempotency table as part of your domain model, not an afterthought, and the rest of your reliability work gets much easier.
Related articles
- REST APIs Designing Idempotency Keys for REST APIs
A practical guide to idempotency keys: what they are, how to store them, how to handle replays and conflicts, and how to make POST endpoints safe to retry without duplicates.
- REST APIs REST API Design Best Practices: A Practical Guide
Apply REST design best practices for resources, naming, status codes, pagination, and versioning to build clean, durable APIs.
- Backend The Circuit Breaker Pattern Explained
Use circuit breakers to stop cascading failures, with state transitions, timeouts, and tuning advice for production microservices.
- Backend CQRS vs Event Sourcing
CQRS and event sourcing are often mentioned together but solve different problems. This post separates them, shows how they combine, and when each is worth the complexity.