What is Idempotency? HTTP Methods, API Keys, Examples

Idempotency: calling an operation N times has the same effect as once — critical for safe retries in distributed APIs. GET/PUT/DELETE are idempotent.

What is idempotency?

Idempotency is a property of an operation where executing it multiple times produces the same result as executing it once. In mathematical notation: f(x) = f(f(x)) = f(f(f(x))). In API and distributed systems contexts, an idempotent operation can be safely retried without causing unintended side effects — critical when networks are unreliable and you don't know whether your previous request succeeded.

The classic example is an elevator button: pressing it once or pressing it ten times produces the same outcome (the elevator comes once). Compare with charging a credit card: pressing "pay" ten times without idempotency produces ten charges. Idempotency design makes systems safe to retry.

Idempotency in HTTP/REST APIs

The HTTP specification (RFC 7231) defines which methods are idempotent:

HTTP MethodIdempotent?Typical Use
GETYesRead a resource — calling N times returns the same data, no state change
HEADYesLike GET but headers only
PUTYesReplace a resource — calling N times leaves the resource in the same final state
DELETEYesRemove a resource — calling N times: first removes it, subsequent are no-ops
OPTIONSYesDiscover allowed methods
TRACEYesDiagnostic loopback
POSTNoCreate a resource — calling N times typically creates N resources
PATCHNo (by default)Partial update — depends on semantics; some PATCHes are idempotent, others aren't

Note that idempotent does NOT mean "returns the same response". DELETE returns 204 the first time and 404 on subsequent calls — different responses, but the server-side state is identical. The property is about end-state, not response-equivalence.

Examples: idempotent vs non-idempotent

Idempotent operations

  • PUT /users/123 {"name": "Alice"} — sets the user's name to Alice. Calling it 10 times still leaves the name as Alice.
  • DELETE /sessions/abc — removes session abc. Calling it 10 times still results in no session abc.
  • GET /products/42 — reads product 42. Reading it 10 times doesn't change anything.
  • PUT /flags/feature-x {"enabled": true} — feature flag toggle by absolute value.

Non-idempotent operations

  • POST /orders {...} — creates a new order. Calling it 10 times creates 10 orders.
  • POST /charges {...} — charges a credit card. Calling it 10 times charges 10 times.
  • PATCH /counters/x {"increment": 1} — increments by 1 each call. Not idempotent.
  • POST /emails {"to": "..."} — sends an email. 10 calls = 10 emails.

Why idempotency matters

In a single-machine, transactional world, you'd never call the same operation twice. But modern systems are distributed — and that changes everything:

  • Network failures. Your client sends a request, doesn't get a response. Did it succeed? You don't know. If the operation is idempotent, retry safely. If not, retry might cause duplicate state.
  • Timeouts. Same problem. The server may have processed the request after the client gave up.
  • Distributed retries. Service meshes, load balancers, and SDKs auto-retry failed requests. They can only safely do this if the upstream is idempotent.
  • Queue-based architectures. Messages can be delivered more than once (at-least-once delivery). Consumers must handle duplicates idempotently.
  • User behavior. Users double-click submit buttons, refresh pages mid-request. Idempotent endpoints absorb this gracefully.

Idempotency keys

For naturally non-idempotent operations like creating an order or charging a card, the standard pattern is the idempotency key: a client-generated unique identifier (typically UUID) sent with each request. The server records the result keyed by this ID and returns the same result for any subsequent request with the same key.

POST /charges
Idempotency-Key: 8e0a4e6b-1b8e-4e3a-b9f7-5e1d3f9c4b8a
{
  "amount": 5000,
  "currency": "USD"
}

The first call processes the charge and stores the result. Subsequent calls with the same key return the stored result without re-processing. The key typically expires after some window (e.g. 24 hours).

Implementing idempotency keys server-side

  1. Receive request with idempotency key.
  2. Check the key store (Redis, database table) for an existing entry.
  3. If found: return the stored response (no side effects).
  4. If not found: acquire a lock on the key, process the request, store the response, release the lock.
  5. Handle in-flight requests: if a duplicate arrives while the first is processing, either wait for completion or return a 409 Conflict.
  6. Expire keys after the agreed window to avoid unbounded storage.

Real-world examples

  • Stripe. The reference implementation. Every state-changing API endpoint accepts an Idempotency-Key header. Documented behavior: same key + same parameters = same result, different parameters with same key = error.
  • AWS SDK. Many AWS APIs use ClientToken parameters (idempotency keys) — EC2 RunInstances, RDS CreateDBInstance, etc. Auto-injected by SDK clients.
  • PayPal, Square, Adyen. All payment processors use idempotency keys to prevent duplicate charges from network retries.
  • HTTP/2 + retry-aware clients. Modern HTTP clients (curl, axios with retry plugins, gRPC) automatically retry idempotent methods (GET, PUT, DELETE) on connection failures.

Common pitfalls

  • Treating PATCH as always idempotent. Depends on semantics. PATCH {operation: "set", value: 5} is idempotent; PATCH {operation: "increment"} is not.
  • Side effects beyond the database. If your "idempotent" endpoint sends an email, calling it twice sends two emails — the email side-effect breaks idempotency. Use idempotency keys to deduplicate side effects too.
  • Idempotency keys without expiry. Storing every key forever causes unbounded growth. Set TTLs (24h, 7d, etc.).
  • Race conditions on concurrent retries. If two requests with the same key arrive simultaneously, naive implementations can process both. Use locks or database constraints (UNIQUE on idempotency_key column).
  • Confusing idempotency with safety. Safe = no side effects (GET, HEAD). Idempotent = N calls = 1 call's effect. PUT and DELETE are idempotent but not safe.

Idempotency in load testing

Load tests that hit non-idempotent endpoints can have surprising failure modes:

  • POST endpoints accumulate state. A 1,000-VU load test against POST /orders creates 1,000 × duration × rps orders. Cleanup after.
  • Idempotency key reuse caches the response. If your script reuses the same idempotency key across VUs, all VUs get the cached first response — your "load test" only actually loaded the first request.
  • Generate unique idempotency keys per VU/iteration. Use UUIDs or VU-id-plus-iteration patterns.
  • Test the idempotency mechanism itself. Send the same request twice — assert the second response equals the first and no duplicate state was created.

FAQ: Idempotency

Is GET always idempotent?

By the HTTP spec, yes — GET should never modify state. In practice, some APIs misuse GET for state changes (often for tracking pixels or analytics), which violates the spec. Properly designed APIs treat GET as read-only.

Is PATCH idempotent?

It depends on the operation. PATCH {set: x} is idempotent. PATCH {increment: 1} is not. The HTTP spec doesn't classify PATCH as inherently idempotent because it depends on semantics.

What's the difference between idempotency and safety?

Safe means no observable side effects on the server (GET, HEAD, OPTIONS). Idempotent means N calls = 1 call's end-state. PUT and DELETE are idempotent but not safe (they modify state).

How do I make POST idempotent?

Use an idempotency key — a client-generated unique ID (UUID) sent with each request. Server stores the result keyed by the ID; duplicate requests return the stored result without re-processing.

What's a good idempotency key TTL?

Stripe uses 24 hours. For high-throughput APIs, 1 hour might be enough. For low-throughput payment APIs, 7-30 days gives more retry tolerance. Match the key TTL to your client retry window.

Does idempotency apply to gRPC and GraphQL?

Yes. gRPC method definitions can specify idempotency level (NO_SIDE_EFFECTS, IDEMPOTENT). GraphQL Query operations are idempotent by convention; Mutations need explicit idempotency design (idempotency keys in input).

Try LoadFocus to test idempotency at scale

If you're designing an idempotency mechanism and want to verify it under load (concurrent retries, race conditions, key expiry), LoadFocus runs JMeter and k6 scripts from 25+ cloud regions with up to 12,500 VUs. Sign up for a free tier at loadfocus.com/signup — no credit card — and run your first idempotency stress test in under 5 minutes.

How fast is your website?

Elevate its speed and SEO seamlessly with our Free Speed Test.

Free Website Speed Test

Analyze your website's load speed and improve its performance with our free page speed checker.

×