Skip to main content
Under load or brief unavailability, the exchange returns a transient failure instead of processing your request. These are distinct from a permanent 4xx (a malformed request that fails the same way every time) and from a 429 rate limit (you exceeded your budget). A transient failure means the request did not get through this time — retry it. The catch on a write: you may not learn whether it applied. Retry with the same request_id so the exchange can deduplicate it.

Transient HTTP responses

Three status families signal that the request did not complete and is worth retrying.
StatuscodeMeaning to the caller
503 Service Unavailableat_capacityThe exchange is at capacity. Not processed.
503 Service Unavailablerequest_droppedThe request was dropped before processing. Not processed.
503 Service Unavailableservice_unavailableThe exchange is briefly unavailable. Not processed.
503 Service Unavailableretry_requiredThe request needs to be retried. Not processed.
504 Timeoutack_timeoutThe exchange did not confirm before the deadline. The write may or may not have applied.
500 Internal Server Errorack_failedThe exchange did not return a confirmation. The write may or may not have applied.
On a 503, the request was rejected before it reached the engine, so a write did not apply. On 500 and 504, the request may have reached the engine before the failure — the outcome is unknown. Treat both the same way: retry with the same request_id. These codes arrive in the application/problem+json body. Branch on code, not on the human-readable detail. The full table is in error codes.

Transient RequestAck statuses

A signed write returns 200 OK with a RequestAck. Two status values there are transient: the request reached the exchange but was not processed.
statusMeaning
request_droppedThe exchange was busy and dropped the request before processing.
retry_requiredThe request needs to be retried.
Both mean the write did not apply. Retry with the same request_id. Aside from duplicate_request_id — the success signal for an already-applied retry — every other non-request_completed status is a permanent rejection (bad price, insufficient margin, market halted), where fixing the request, not retrying it, is the answer.

Retry safely with the same request_id

The request_id is a UUIDv7 and the idempotency key. When you do not know whether a write applied — a 500, 503, 504, a transient RequestAck status, or a dropped connection — resubmit the same request_id:
  • If the original applied, the retry returns duplicate_request_id (a success signal, not an error). Stop.
  • If it did not, the retry is processed once and returns request_completed.
Either way the write happens at most once.
Do not retry a write with a fresh request_id. If the original applied, a new id executes the order a second time. Reuse the original request_id so the exchange deduplicates it.
The embedded v7 timestamp must still be current when the retry lands. If your backoff pushes a retry past the skew window, the resend is rejected 400 request_timestamp_skew — at that point generate a fresh UUIDv7 and accept that you no longer have idempotency protection for that attempt.

Back off, don’t hammer

A retry storm against an exchange that is already at capacity makes the outage worse. Space your retries out.
1

Wait before the first retry

Start from a base delay (for example 200 ms) rather than retrying immediately.
2

Grow the delay each attempt

Double the wait on each successive failure — 200 ms, 400 ms, 800 ms — up to a ceiling (for example a few seconds). Add a small random jitter so concurrent clients do not retry in lockstep.
3

Cap the attempts

Give up after a bounded number of tries and surface the failure. Persistent 503s mean the exchange is shedding load; keep the same request_id ready for when it recovers.

Not the same as a rate limit

A 429 is not a degraded mode. It means you exceeded your own rate limit, not that the exchange is unhealthy. Both warrant backoff, but the cause and the fix differ: a 429 clears when you slow down, a 503 clears when the exchange recovers. As with any write retry, reuse the original request_id on a 429 resend so it is deduplicated.