Developer Tools

Idempotency Keys & Retry-Safe APIs: Why POST Requests Need Special Handling

Retrying a failed GET is harmless. Retrying a failed POST can double-charge a customer. Idempotency keys are the mechanism that makes retrying a payment request safe.

September 26, 2026 6 min read Toolio Editorial
Idempotency Keys & Retry-Safe APIs: Why POST Requests Need Special Handling
Summarize with:
Share:

A payment request times out on the client side — no response arrives, so the client, following normal retry logic, sends the exact same charge request again. If nothing special is in place, the customer is now charged twice for one purchase, because the server has no way to know the second request is a retry of the first rather than a genuinely new charge. This exact scenario is why idempotency keys exist, and why "just retry on failure" is dangerously incomplete advice for any API that creates or mutates state.

HTTP methods have a formal property called idempotency — meaning making the same request multiple times produces the same result as making it once — but that property is a convention the server has to actually implement, not something HTTP grants automatically. Understanding exactly which methods are safe to retry blindly, and which need explicit help, is essential for any API that handles money, inventory, or anything else where duplication is costly.

Direct Answer: GET, PUT, and DELETE are defined as idempotent by the HTTP specification, meaning repeating them should produce the same end state as doing them once (fetching a resource repeatedly doesn't change it; setting a resource to a specific value repeatedly ends in that same value; deleting an already-deleted resource is still "deleted"). POST is explicitly not idempotent — it's the method for creating something new or triggering a side effect, so retrying a POST can create a second order, a second charge, or send a second email. An idempotency key is a unique identifier (typically a client-generated UUID) sent in a request header — commonly Idempotency-Key — that lets the server recognize "I've already processed this exact request" and return the original result instead of repeating the side effect on a retry.


1. Why GET/PUT/DELETE Are Naturally Safe to Retry, and POST Isn't

GET just reads data — sending it five times reads the same data five times with no side effect. PUT replaces a resource entirely at a known location (PUT /users/482 sets user 482's data to exactly what's in the request body) — sending it twice with identical content leaves the resource in the identical end state both times. DELETE /orders/91 deletes order 91; calling it again finds nothing there to delete, but the end state — "order 91 does not exist" — is unchanged either way.

POST breaks this pattern by design, because its entire purpose is usually "create a new thing" or "trigger an action," and neither of those is naturally safe to repeat:

POST /charges
{"amount": 5000, "currency": "usd", "customer": "cus_9f3d"}

Send this twice — because the first response was lost to a network timeout, not because the charge itself failed — and without any additional protection, the payment processor has no way to distinguish "the client wants to charge $50 twice" from "the client's first request just never got its response."


2. How an Idempotency Key Actually Works

The client generates a unique key (a UUID is the standard choice — see a UUID generator for the format) once, before the first attempt, and sends the same key on every retry of that same logical operation:

POST /charges HTTP/1.1
Content-Type: application/json
Idempotency-Key: 7b3e9f2a-4c81-4e6d-9a02-1f8c6d3b9e77

{"amount": 5000, "currency": "usd", "customer": "cus_9f3d"}

On the server side, the first time it sees a given key, it processes the request normally and stores the key alongside the result. If the exact same key arrives again — because the client retried after a timeout — the server does not reprocess the charge; it simply returns the stored result from the first attempt.

Attempt 1: Idempotency-Key: 7b3e9f2a...  →  processed, charge created, response cached
Attempt 2: Idempotency-Key: 7b3e9f2a...  →  same key seen before, cached response returned,
                                              charge NOT created again

Most implementations also validate that a retried key comes with an identical request body — if the same key arrives with a different body, that's treated as a client error rather than silently processing the new body, since it likely indicates a bug in key reuse.


3. Real-World Example: Payment APIs

Major payment processors popularized this exact pattern because payments are the highest-stakes case of "retrying a POST is dangerous." A typical integration flow:

Step Action
1 Client generates a UUID once, before the first charge attempt
2 Client sends POST /charges with Idempotency-Key header
3 Network fails; client receives no response (timeout)
4 Client retries the same request with the same key
5 Server recognizes the key, returns the original charge result — no second charge created

Without step 5, this flow double-charges the customer on every network hiccup during checkout — which is common enough in real mobile and unstable-connection scenarios that payment APIs treat idempotency keys as mandatory practice, not an edge-case nicety. You can inspect exactly what headers a checkout API actually expects and returns using an API tester, and confirm response codes with an HTTP status checker when debugging a retry flow.


4. Idempotency Keys vs. Simple Response Caching

It's worth distinguishing this from ordinary HTTP caching, because they look superficially similar but solve different problems. Response caching (via Cache-Control, ETag, etc.) is about avoiding re-fetching data that hasn't changed — it's a read-path optimization, and it's driven by the server deciding something is cacheable. Idempotency keys are about preventing a side-effecting write from happening twice — they're a write-path safety mechanism, driven by the client explicitly flagging "this is a retry of a specific prior attempt, not a new request." Caching says "don't bother asking again, here's what I already told you." Idempotency keys say "I recognize this exact write attempt — I already did it, here's what happened."


Frequently Asked Questions

Is POST always unsafe to retry?

Not inherently unsafe — it's simply not guaranteed idempotent by the HTTP spec the way GET/PUT/DELETE are. Whether retrying a specific POST endpoint is actually dangerous depends on what that endpoint does; a POST that just logs an analytics event is low-risk to duplicate, while one that charges a card or creates an order is high-risk.

Where should the idempotency key be generated — client or server?

Always client-generated, and generated once per logical operation before the first attempt — if the server generated it, a retry after a lost response would get a new key and defeat the entire purpose.

How long should a server remember an idempotency key?

Commonly 24 hours is a typical retention window used by payment APIs, balancing realistic retry timeframes against unbounded storage growth — the exact window is an implementation choice based on how long retries might plausibly occur.

What happens if the same idempotency key is reused with a different request body?

Well-designed APIs treat this as a client error rather than silently processing the differing body, since it usually indicates the key was reused incorrectly rather than intentionally retried.

Do idempotency keys make PUT and DELETE requests unnecessary to protect?

No extra protection is typically needed there since those methods are already idempotent by design — the pattern specifically targets POST (and sometimes PATCH, depending on how partial updates are implemented) because those are the methods without a built-in retry-safety guarantee.


References: RFC 9110 (HTTP Semantics — method definitions and idempotency), RFC 7231 (superseded, historical idempotency definitions).

Free Calculator

Put this guide into action

Stop guessing — use our UUID & GUID Generator, Parser & Validator to run real numbers, compare scenarios, and get instant results you can trust.

Use Free UUID & GUID Generator, Parser & Validator
Toolio Editorial

Toolio Editorial Senior Technical Editors & UX Content Engineers

Digital Utilities, Web Engineering & Tool Guides

The Toolio Editorial Board is dedicated to delivering clear, transparent, and accurate technical guides across digital utilities, developer tools, unit conversion standards, date-time algorithms, and decision science. The board maintains rigorous editorial standards, factual accuracy, and step-by-step clarity for every guide published.

Try Calculator UUID & GUID Generator, Parser & Validator
Use UUID & GUID Generator, Parser & Validator

Continue Reading