Skip to main content
Every response from Olympex uses the same JSON envelope, with a stable error.code on failures and a meta.requestId on every response. Some failures happen earlier, at the API gateway, and come back with a smaller body and no meta.requestId. Their ID is in the apigw-requestid response header instead. Your client needs to handle both, retry only the failures a second attempt can fix, and sign every attempt again.

The response envelope

Every success is HTTP 200, including creates (POST /limit-order, POST /dca-order/strategies) and cancels (DELETE /limit-order/{id}). New error codes can be added. Treat a code you don’t recognize according to its HTTP status.

Error codes

Gateway responses

The API gateway sits in front of Olympex. It authenticates signed requests, enforces a timeout of about 30 seconds and answers some requests itself. Its responses carry only message: no success, error or meta, so no meta.requestId. They carry an ID in the apigw-requestid response header, which browsers can’t read on cross-origin calls. The OpenAPI spec names their body GatewayError. To tell the two kinds of response apart, check the body for success. Every Olympex response has it and no gateway response does. Three statuses can come from either:
  • 401. The gateway’s {"message":"Unauthorized"} means a signing header is missing. Olympex’s UNAUTHORIZED is a problem on the Olympex side.
  • 403. The gateway’s {"message":"Forbidden"} means authentication failed. Olympex’s FORBIDDEN means the body doesn’t match the signed hash.
  • 500. From Olympex, with an error code; from the gateway, with only message.
Every 404 comes from Olympex, with the code NOT_FOUND. For an unknown path or method, its message names them, for example "No route for GET /api/v1/quote". Sign requests maps each signing mistake to the 401, 403 or 400 it produces.

Which errors to retry

Requests that create a resource

GET, PATCH and DELETE requests are safe to repeat. So are POST /quotes, POST /swap, POST /support-chain and POST /tx-status, which change nothing: POST /swap only returns calldata and never broadcasts. A repeated DELETE /limit-order/{id} whose first attempt went through returns 409 CONFLICT, because the order is already cancelled: read the order with GET /limit-order/{id} instead of treating the 409 as a failure. Three endpoints create a new resource on every successful call. Olympex ignores any id you send, so you can’t use one to make a repeated call recognizable: After a timeout, a network error or a 5xx, you can’t tell whether a create succeeded. Don’t retry it blindly: list what exists and match on your own fields first.
  • Limit order. Call GET /limit-order?accountTo=<maker> and look for an order with the same chain, tokens, amount and priceTrigger.
  • DCA strategy. Call GET /dca-order/strategies?accountTo=<maker>, which lists strategies newest first, and look for one with the same tokens, totalAmount, iterations and frequency.
  • API account. No endpoint lists accounts. If you never received the response, you never saw its secret key, so create another account.
?accountTo= matches case-sensitively. Send the maker address in the same EIP-55 checksummed form you used to create the order.
Don’t retry POST /accounts, POST /limit-order or POST /dca-order/strategies automatically. A retry after a timeout can leave you with two accounts and a secret key you never saw, or with two orders that Olympex can both execute against the maker wallet’s allowance.

Back off with jitter

Retry with exponential backoff and full jitter: before each retry, wait a random time between zero and a ceiling that doubles after every failure, up to a cap, and stop after a few attempts. Jitter spreads retries out, so many clients that failed together don’t retry together. Route every call through one helper instead of retrying at each call site. Handle errors and retries builds one on the reference implementations: olympexRequestWithRetry in TypeScript and olympex_request_with_retry in Python. Both take (method, path, body), like the reference helpers, and never retry the three creates automatically. Their defaults (4 attempts, a 1-second base delay, a 20-second cap) are starting points: tune them to your latency budget.

Sign every attempt again

The server rejects a nonce it has already seen in the last 5 minutes, and any timestamp more than 300 seconds from server time. Resending the headers of an earlier attempt fails with 403 if that attempt reached Olympex, even if it ended in a 500 or a timeout. After a client timeout you can’t tell whether it did, so sign again. Call your signer for every attempt, so each one gets a new timestamp and a new nonce. olympexRequest and olympex_request sign on every call, so a retry helper that calls them for each attempt already does this. Never cache, queue or reuse signed headers: each set authenticates one request, once.

Keep the request ID

meta.requestId identifies a request. Log it for every response, next to your own correlation ID. When a failure persists, send it to partners@olympex.io: it is the fastest way for Olympex to find your request. The error types in the reference implementations carry it as requestId (TypeScript) and request_id (Python). Gateway responses have no meta.requestId. Log their apigw-requestid response header instead, with the UTC time, the endpoint, the status and your API key ID. The reference helpers read the request ID from the body only, so for a gateway 401 or 403 their error has no requestId or request_id: read the header where your code receives the HTTP response, for example in your copy of the helper, and log it there. Browsers can’t read that header on cross-origin calls, so log it on your server. From a terminal, add -i (or -D -) to a curl command to print the response headers, including apigw-requestid. Never log or send the secret key, the passphrase or the signed headers.

Polling transaction status

A 500 TX_STATUS_ERROR from POST /tx-status right after you broadcast, or for a transfer the provider marks failed, means the status is unknown. It doesn’t tell you whether the transfer succeeded or failed.
Keep polling with backoff, for example every 15 to 30 seconds, instead of treating the error as final. If the status stays unknown, check the transaction on a block explorer, or contact partners@olympex.io with its meta.requestId. Track a swap to finality has the full polling loop. GET /transactions/{hash} returns TX_STATUS_ERROR when the chain’s RPC fails: retry it with backoff too. A hash the chain doesn’t know yet isn’t an error there: the response is a 200 with the status not_found, so you can poll it right after you broadcast.

What this means for your integration

  • Branch on success, error.code and the HTTP status, never on error.message.
  • Retry a gateway 429, 500-class responses, 422 NO_ROUTE and network errors with exponential backoff and jitter, except the three creates: list and match before you create again.
  • Sign again once after a gateway 401 or 403, then stop and check your signer, credentials and clock.
  • Never retry 400, 404 or 409, and fix the cause before you resend after UNAUTHORIZED or FORBIDDEN.
  • Sign every attempt again, with a new timestamp and nonce.
  • Log meta.requestId, or the apigw-requestid header for gateway responses, for every failure and include it when you contact support.

Handle errors and retries

A step-by-step guide to a production error-handling layer.

Sign requests

The signing algorithm and the troubleshooting table.

Limits

The timestamp window, nonce reuse and the gateway timeout.

Track a swap to finality

Poll a cross-chain transfer until it completes.