Retries and idempotency
Idempotency-Key, which writes are safe to retry, and Retry-After.
Idempotency-Key
A network can fail after the API has done a write but before you hear back. To retry safely, send Idempotency-Key: <unique key> on writes the API marks idempotent (x-idempotent: true in the document, “Idempotent” in the reference). A UUID per logical write is right.
- Retry with the same key and the same request (method, path, query and body) and you get the first response again, for 24 hours, with
Idempotent-Replayed: true. The write isn’t done twice. - The same key with a different request answers
422 idempotency_key_reused. - A retry while the first request is still running answers
409 idempotency_in_progress. Wait, then retry. - Only successful responses are kept: after an error, the key is free and a retry runs again.
- Keys belong to the caller, per route. Signed-out requests ignore the header.
import { createCollectorClient } from '@collector/sdk';
const collector = createCollectorClient({ token: process.env.COLLECTOR_TOKEN });
// Keep the key with the job that makes the write: a retry from another process, an hour
// later, replays the first answer instead of creating a second alert.
const idempotencyKey = crypto.randomUUID();
const { data, attempts, requestId } = await collector.withResponse.createPriceAlert({
body: {
issueId: '00000000-0000-4000-8000-000000000000',
editionId: null,
graded: false,
bandId: '9.0-9.6',
direction: 'below',
targetCents: 2500,
currency: 'USD',
},
idempotencyKey,
});
console.log(`alert ${data.alert.id} after ${attempts} attempt(s), request ${requestId}`);Which writes are idempotent
- POSTPublish or schedule an announcement
- POSTMove issues to another series
- POSTMerge two records
- POSTGive someone a plan
- POSTSuggest a correction
- POSTSend a cover photo
- POSTBring a candidate into the catalog
- POSTAdd a book no source knows yet
- POSTPropose a variant
- POSTUndo a change to a copy
- POSTAdd a code to a copy
- POSTChange what a copy is
- POSTChange many copies at once
- POSTAsk a store to hold a book
- POSTCancel a hold
- POSTRecord the sale
- POSTMessage about a hold
- POSTAccept or decline a hold request
- POSTCreate a price alert
- POSTAsk a store about a book
- POSTSet the book aside for the asker
- POSTReply in a question’s thread
- POSTReport something
- POSTCreate a scan
- POSTAdd a scan to the collection
- POSTStart a scanning session
- POSTAct on many scans
- POSTCreate a store
- POSTImport inventory rows
- POSTList a book
- POSTSet many listings’ status
- POSTInvite someone to the team
Other writes aren’t replayed. Before retrying one, check whether it happened (read the record back).
When to retry
- 429 and 503: wait for the
Retry-Afterheader (seconds), then try again. - 500, 502, 504 and dropped connections: retry reads, and idempotent writes with their key, backing off exponentially with some randomness.
- Other 4xx: don’t: the request needs changing. The problem’s
codesays why.
The SDK does all of this: two retries by default, honouring Retry-After, with a fresh Idempotency-Key on each idempotent write, and one X-Request-Id across a call’s retries.
Next: Rate limits