Retry only what’s safe to retry
Branch on the HTTP status and the error envelope’scode, never on the human-readable message.
| Status | Retry? | What to do |
|---|---|---|
429 Too Many Requests | Yes | Honor Retry-After (seconds), then back off. See Rate limits. |
409 idempotency in-flight | Yes | A request with the same X-Idempotency-Key is still running. Honor Retry-After, then retry the same key. |
503 / 504 / 5xx | Yes | Transient server or gateway condition. Back off with jitter and a cap. |
| Network error / timeout | Yes | Connection reset, DNS failure, read timeout. Treat as transient and back off. |
400, 401, 403, 404 | No — fix first | Malformed request, bad/expired/revoked key, missing scope, wrong path. Retrying the identical call won’t change the outcome. |
Other 4xx validation | No — fix first | Correct the request body or parameters, then send once. |
None of these cases call for a new idempotency key. A retry should reuse the same key so the server can deduplicate it — see Idempotency keys are safe to reuse below.
401/403, the fix is in your key or scopes — see Authentication and Scopes. For 429, see Rate limits.
Honor Retry-After when present
Two responses carry a Retry-After header (in seconds), and you should wait at least that long before retrying:
429 Too Many Requests— the per-minute limiter. The response also carriesX-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Reset.409idempotency in-flight — a concurrent request with the sameX-Idempotency-Keyis still executing. Wait the suggested interval, then retry the same key.
Retry-After is present, treat it as a floor: wait at least that long, then apply your normal backoff for any subsequent attempts.
Exponential backoff with jitter
For transient failures without aRetry-After, back off exponentially and add jitter so that many clients retrying after the same outage don’t synchronize into a thundering herd. Cap both the per-attempt delay and the total number of attempts.
@alterscope/sdk, Python alterscope) apply backoff for you; reach for the patterns above when you’re calling the API directly or generating a client from the OpenAPI spec.
Set sensible timeouts
A retry loop is only as good as the timeouts under it. Without them, a single stalled connection blocks the whole loop.- Per-attempt timeout — bound each request so a hung connection fails fast and frees the slot for a retry rather than hanging indefinitely.
- Total deadline — cap the wall-clock time across all attempts. Once the deadline passes, stop retrying and surface the error.
- Connect vs. read — a short connect timeout catches unreachable hosts quickly; a longer read timeout accommodates heavier analytical endpoints.
Idempotency keys are safe to reuse
Mutation endpoints accept anX-Idempotency-Key header. When you retry, send the same key — that’s what makes the retry safe:
- A replay of a completed request returns the original cached response (within a 24-hour window) with an
X-Idempotency-Replayed: trueheader, instead of executing the side effect again. - If the original request is still in flight, the retry receives
409withRetry-After; wait and retry the same key.
X-Idempotency-Key only for a genuinely new operation, never to “force through” a failed one — a new key bypasses deduplication and can double-execute the side effect. See Idempotency for the full contract.
Checklist
- Retry
429,409in-flight,503/504/5xx, and network/timeout errors. Don’t retry400/401/403/404or other validation failures — fix the request first. - Honor
Retry-After(seconds) on429and409before backing off. - Use exponential backoff with full jitter, a per-attempt delay cap, and a capped attempt count.
- Set per-attempt timeouts and a total deadline.
- Retry with the same
X-Idempotency-Key; never mint a new one to force a retry.
Related
Idempotency
Make mutations safe to retry with
X-Idempotency-Key.Errors
The error envelope, status codes, and per-code retry guidance.
Rate limits
Per-tier limits and
429 retry behavior.Authentication
Fix
401/403 failures at the key and scope level.