Handle API errors like a pipeline, not a script
Most API integration bugs are retry bugs. An answer API has an extra wrinkle: some calls cost money, so retrying the wrong thing can bill twice. Here’s the policy that gets it right — and what the SDKs already do for you.
The error taxonomy
- 429 rate-limited → always retry, after
Retry-After. The concurrency slot frees; the call is safe. - 5xx / timeouts / connection refused on reads → safe to retry: GET/DELETE don’t mutate.
- 5xx on task creation → safe only with an idempotency key. Without one, a timed-out create that actually succeeded plus your retry = two tasks, two charges.
- Sync monitor calls → retry only when nothing was sent (connection refused, DNS failure). A sent request may have run — retrying could start a second collection.
- 4xx → don’t retry; fix the request. 401 is auth, 402 is credits, 422 is a bad payload.
What the SDKs implement
// The TypeScript client's policy, for reference:
// - 429: always retry with capped full-jitter backoff
// - 5xx/timeout/conn-error: retry GET/DELETE and keyed task creation only
// - sync monitor calls: retry only on unsent (refused/DNS)
// - per-attempt 30s; sync monitor calls allow 330s
# The Python client mirrors it — Client and AsyncClient share the policy.
If you hand-roll HTTP instead, encode exactly this. Details in the auth and errors guide and rate limits; the idempotency mechanics are in the keys post.
The last line of defense
Webhooks mean a lost response isn’t lost data: even if your sync call dies mid-flight, a queued task’s result still arrives at your endpoint. For anything you can’t afford to lose, create it async with a key — then retries are bookkeeping, not gambling.