What is an idempotency key?
What an idempotency key is, why retried API requests need one, and how PostGate's Idempotency-Key header keeps a retried publish request from posting the same content twice.
An idempotency key is a unique value a client attaches to a write request so that sending the same request more than once has the same effect as sending it once. The server maps the key to the first result and returns it on repeats instead of re-executing the write; reusing a key with a different body is rejected as a conflict. PostGate honors keys for 72 hours.
An idempotency key is a unique value a client attaches to a write request so that sending the same request more than once has the same effect as sending it once. It's not specific to social media APIs — payment APIs (Stripe popularized the pattern), order-creation APIs, and any other system where a duplicate write causes real-world harm all use the same idea. The problem it solves is universal: a network can fail after a server has already processed a request but before the client received the response, and the client has no way to know which happened.
PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.
What general problem does it solve?
A client-side timeout only means the client stopped waiting — it says nothing about whether the server finished the work. Without an idempotency key, the only safe options are to retry blindly (risking a duplicate) or not retry at all (risking silently losing a request that actually failed). An idempotency key removes that dilemma: the client generates one value for one logical write, sends it on every attempt of that write, and the server uses it to recognize "I've already done this exact thing" and return the original result instead of doing it again.
How does it work, generally?
The client sends the key — typically as a header, such as Idempotency-Key — alongside the request body. The server stores a mapping from that key to the result of the first request it processed with it. On a repeat request bearing the same key, the server returns the stored result without re-executing the write. Most implementations also reject a key reused with a different request body as a conflict, rather than silently applying the new content under an old key — a resent key should mean "same request, retried," never "different request, same label."
Why does this matter specifically for a publishing API?
Most APIs treat a duplicated write as a minor cleanup problem — an extra row, deleted later. A social publishing API is different: a retried POST /api/posts after a dropped connection can mean the same launch announcement goes out twice on a customer's real Instagram and LinkedIn accounts, visible to their actual audience. There's no clean way to undo that after the fact — some platforms don't even support deleting a published post via API. Idempotency isn't an optimization here; it's the only thing standing between a network hiccup and a customer publicly posting the same thing twice.
How does PostGate implement it?
PostGate accepts an Idempotency-Key header on every POST /api/posts request and honors it for a 72-hour window after first use. Retrying the identical request within that window returns the original post unchanged instead of creating a second one. Reusing the same key with different content — a different body, profiles, or media — is rejected with a 409 conflict rather than silently applying the new content over what a customer already saw published. A good key is derived from your own internal job or queue-message ID (for example launch-post-{internal_job_id}) — stable across retries of the same logical publish, and different for every distinct one. Generating a fresh random value on every retry defeats the purpose entirely: PostGate would treat each attempt as a brand-new request.
What safeguard does it pair with?
Idempotency keys matter most exactly when you're retrying — after a timeout, or after a rate-limited 429 that resolves and you send the request again. Reusing the same key across every retry attempt of one logical request is what makes the exponential backoff schedule that follows a failure safe to run at all — without it, a correctly-implemented retry loop would still risk a duplicate post on every attempt.
Frequently asked questions
Why can't I just retry a timed-out request?
Because a client-side timeout only means the client stopped waiting — it says nothing about whether the server finished the work. A network can fail after a server has processed a request but before the client received the response. Without an idempotency key, the only options are retrying blindly (risking a duplicate) or not retrying at all (risking silently losing a request that actually failed). The key removes that dilemma.
Is the idempotency key pattern specific to social media APIs?
No. Payment APIs — Stripe popularized the pattern — order-creation APIs, and any other system where a duplicate write causes real-world harm all use the same idea. What makes it especially important for a publishing API is that a retried create-post call can put the same announcement on a customer’s real Instagram and LinkedIn twice, visible to their audience, and some platforms do not even support deleting a published post via API.
What makes a good idempotency key?
Derive it from your own internal job or queue-message ID — for example launch-post-{internal_job_id} — so it stays stable across retries of the same logical publish and differs for every distinct one. Generating a fresh random value on every retry defeats the purpose entirely: the server would treat each attempt as a brand-new request and could execute the write more than once.
What happens if I reuse an idempotency key with a different request body?
Most implementations, PostGate included, reject it as a conflict — PostGate returns a 409 — rather than silently applying the new content under an old key. A resent key should mean "same request, retried," never "different request, same label." Within the 72-hour window, retrying the identical request returns the original post unchanged.
