Why POST Needs Idempotency Keys
You send a request. The server processes it. But the response is lost in the network — a timeout, a dropped connection, a flaky mobile network. So your client retries. The server processes it again. Now you have two short links, or two charges, or two emails.
This is the classic duplicate-problem, and the fix is an idempotency key: a unique string your client sends with the request so the server can recognize and deduplicate retries. This guide explains how they work, when you need them, and how to implement them on the create-links pattern.
The problem: retries are inevitable
Networks drop things. It's not a bug — it's physics. A request can succeed at the server and still "fail" for the client because the response never arrived. The client's only sane behavior is to retry. But a retry of a non-idempotent operation creates a duplicate.
- Retrying a create → duplicate resource.
- Retrying a charge → double charge.
- Retrying a send → double email.
Without a way to tell "this is the same request as before," the server can't know it should return the original result rather than create another one.
What an idempotency key is
An idempotency key is a unique, client-generated string sent as a header (or body field) on a request:
POST /api/v1/links
Idempotency-Key: 4f8a9c2e-...-unique
The contract:
- The client generates a fresh key for each new logical operation.
- The same key is reused on every retry of that operation.
- The server stores the key with the operation's result.
- If a request with an already-seen key arrives, the server returns the stored result instead of running the operation again.
The key only needs to be unique per client; the server treats it as opaque.
How the server handles it
A minimal implementation:
key = request.header("Idempotency-Key")
if key is present:
existing = store.lookup(key)
if existing:
return existing.result # deduplicate — no second create
result = create_link(payload) # do the real work
store.save(key, result) # remember for retries
return result
Two things matter:
- Check the key before doing the work. The lookup must happen before the side effect, so a retry never triggers a second create.
- Store the result atomically with the work, so a race between two identical requests can't both create.
A real example: creating a link
Say a mobile app shortens a URL. The user taps once, the network blips, the app retries. Without a key:
- Request 1 creates
ab3x7. - Request 2 creates
cd5y9. - The user now has two links for the same destination.
With an idempotency key:
- Request 1 (
Idempotency-Key: abc) createsab3x7and stores the result underabc. - Request 2 (
Idempotency-Key: abc) finds the stored result and returnsab3x7. - The user gets one link, and the client gets a clean success.
This is exactly how a robust create-links API should behave — and why a good
shortener API supports idempotency keys on its POST /links endpoint.
When you need it vs when you don't
Need it for any non-safe operation with a side effect you don't want duplicated: creating resources, charging payments, sending emails, registering, uploading.
Don't strictly need it for GET (already idempotent) or for operations you're
willing to let run twice. But even "harmless" duplicates create confusion and
data-quality problems, so the key pattern is cheap insurance.
Common mistakes
- Checking the key after the work. If you create first and check later, retries still duplicate.
- Reusing one key for everything. The key identifies a specific operation; reuse breaks dedup.
- Ignoring the key on some clients. If only some clients send keys, retries from the others still duplicate. Enforce it on the critical paths.
- Not storing enough to return the original result. If you don't persist the result, you can't replay it.
The takeaway
Idempotency keys turn an unsafe retry into a safe one. Generate a unique key per operation, reuse it on retries, and have the server check it before doing work and store the result. It's a small header that prevents the most embarrassing class of bugs — duplicate links, duplicate charges, duplicate emails.
When you're building on yas.sh's API, send an Idempotency-Key on link creation to
guarantee your retries never create duplicates.
