Skip to main content
The clients retry a request only when sending it again cannot repeat a change: GET requests, and requests that carry an Idempotency-Key header. Every other request is sent once.

What is retried

500 and other errors are not retried. The Idempotency-Key header can come from the call’s input or from headers configured on the client. The clients retry any request that carries one, so send it only to endpoints that declare an Idempotency-Key header; many endpoints that change data do not. Between attempts, a client waits for the delay in the response’s Retry-After header, given in seconds or as an HTTP date. Without one, it waits a random time between zero and min(2 s, 250 ms × 2^(attempt - 1)): up to 250 ms before the second attempt and up to 500 ms before the third. If Retry-After asks for more than the client’s limit, 60 seconds by default, the client does not wait and does not shorten the delay: it stops retrying and handles that response as the result, which is an error for an error status. When attempts run out, the last response is handled the same way. When a network error or timeout ends the call, TypeScript throws TransportError, Python raises TransportError, and Rust returns Error::Transport or Error::Timeout.

Timeouts

No client limits the whole call: the timeout applies within each attempt, so a call that is retried can take longer than one timeout.
  • TypeScript: timeoutMs aborts an attempt that has not finished in time. To cancel a whole call, pass an AbortSignal as signal in the call’s second argument. Cancelling stops the current attempt and any wait, and the call throws TransportError with the signal’s abort reason as its cause.
  • Python: timeout is passed to httpx for each attempt, which applies it separately to connecting, writing, reading, and waiting for a pooled connection.
  • Rust: timeout limits each attempt from sending to the end of the response body. The default HTTP client also uses it as its connect timeout.

Configure retries and timeouts

Attempts are limited to between 1 and 3; a larger value means 3.

Idempotency keys

Endpoints that create or change something often take an Idempotency-Key header, which identifies one logical change across retries. Most of them require it, and the API answers 400 with IDEMPOTENCY_KEY_REQUIRED when it is missing. The key is 1 to 255 visible ASCII characters, without spaces. When the API answers with a stored result for a key it has already seen, the response has Idempotent-Replayed: true. The clients do not create keys. Pass one in the call’s input:
  • TypeScript: headers: { idempotencyKey }, or headers: { "idempotency-key": key } where the contract spells the header that way, as for uploadAttachment.
  • Python: "headers": { "Idempotency-Key": key } in the input model, with the header name spelled as the contract spells it.
  • Rust: a positional argument when the endpoint requires the key, or the idempotency_key setter of the operation’s Params when it is optional.
Use a new key for each change you make, such as a random UUID, and keep it while you retry that change yourself, for example after a timeout or a restart. A request that carries a key is retried by the client, with the same key on every attempt. Making requests shows a complete call. Do not set Idempotency-Key in the client’s headers option: every call would share one key.