Skip to main content
Olympex enforces a small set of hard limits. This page lists every one of them, what happens when you cross it, and how to stay inside it. Olympex doesn’t publish per-key rate limits or quotas, so design your client to back off, and tell us about the volume you expect.

Limits at a glance

Timestamp window

The server rejects a request whose timestamp is more than 300 seconds before or after server time. A timestamp exactly 300 seconds old is accepted, and one 301 seconds old is rejected.
  • Use Unix time in seconds. A millisecond timestamp is always outside the window.
  • Keep your server clock synchronized with NTP.
  • Sign immediately before you send. Don’t sign requests ahead of time, queue signed requests or cache headers.

Nonce reuse

The server rejects a nonce it has already seen in the last 5 minutes. Generate a new nonce, 24 hexadecimal characters from 12 cryptographically secure random bytes, for every attempt, retries included. A retry that resends the headers of an earlier attempt fails with 403.

Gateway timeout

The API gateway ends a request that runs longer than about 30 seconds and answers 500 with {"message":"Internal Server Error"} or 503 with {"message":"Service Unavailable"}. Neither response has meta.requestId. Both carry an ID in the apigw-requestid response header: log it on your server, because browsers can’t read it on cross-origin calls.
  • Set your HTTP client timeout a little above 30 seconds, so the gateway’s answer arrives before your client gives up. The TypeScript and Python reference implementations use 35 seconds. With curl, add --max-time 35.
  • Retry timeouts with exponential backoff and jitter, signing every attempt again. See Errors and retries.

Calldata expiry

The calldata that POST /swap returns carries an on-chain expiry: 5 minutes on most routes. Some routes include a market maker’s firm quote that expires sooner, within seconds of the build. The calldata is also bound to the account you passed, and another Olympex swap from the same account can invalidate earlier calldata. Quotes aren’t reserved either: prices move between the quote and the swap. Request the swap right after the quote, call POST /swap right before the user signs, and broadcast the transaction at once. Run eth_estimateGas first: a 200 from POST /swap doesn’t guarantee that the calldata executes. If the estimate reverts, don’t send. Build the swap again with the next aggregatorId in the quote’s aggregatorOrder, or, for a cross-chain transfer, request a new quote. If you miss the window, request a new quote and a new swap.
The calldata is real mainnet calldata. Broadcasting it moves funds, and a transaction that fails on-chain still costs gas.

Integrator fee

The optional fees object on POST /quotes and POST /swap sets your own fee on a route. Send the same fees on both, so the quote you show matches the calldata you send:
  • feeBps is an integer from 0 to 100, in basis points: 100 is 1%.
  • feeRecipient is the EVM address that receives the fee. It is required when feeBps is greater than 0, and the zero address is rejected.
  • Fees apply only to signed requests from API accounts.
Gas and fees explains how the fee appears in integratorFeeBreakdown.

Request bodies and numbers

  • The body of every POST and PATCH request is a JSON object, sent with content-type: application/json. GET and DELETE requests have no body: sign the empty string, send no body, and send the content type anyway. See Requests without a body.
  • Types are strict. A number where the API expects a string, or the reverse, returns 400 VALIDATION_ERROR. For example, chainId is a JSON integer in every request body: "137" returns 400.
  • Quote, swap and limit-order amounts, prices and slippage are decimal strings. DCA’s totalAmount, slippage, minPrice and maxPrice must be JSON numbers.
  • The reference signers canonicalize numbers the way JavaScript does, so fractional numbers sign correctly. If you write your own signer in another language, format numbers exactly as JavaScript’s JSON.stringify does: 1.0 becomes 1, 1e21 becomes 1e+21, and 0.0000001 becomes 1e-7.
  • Keep integers between -9007199254740991 and 9007199254740991, that is ±(253 − 1). The server parses JSON numbers as JavaScript doubles, so a larger integer loses precision and no longer matches the hash you signed.
  • Keep object keys in ASCII and never numeric. See the portability rules.

Order allowances

Limit orders and DCA strategies execute against the maker wallet’s ERC-20 allowance to the Olympex order contract on that chain. The allowance is the on-chain limit on what Olympex can move, so size it to your open orders:
  • A limit order needs amount of inTokenAddress plus the execution gas cost, which Olympex takes in inTokenAddress. Estimate that cost with POST /quotes and "includeGasInfo": true (dataFeeTransaction.transactionFeeInToken), and add a buffer, because gas prices move.
  • A DCA strategy needs totalAmount of tokenAddressFrom, with no extra fee on top.
  • Every open limit order and active DCA strategy that sells the same token on the same chain shares one allowance: approve the sum.
Order signatures and allowances lists the order contract on each chain.

Lists

GET /limit-order, GET /dca-order/strategies, GET /dca-order/strategies/{id}/orders and GET /tokens return every matching item in one response. There is no pagination. Narrow the limit-order list with status, chainId or accountTo, and the strategy list with status or accountTo. These lists ignore any other query parameter, so a misspelt or unsupported filter returns the full list. Send each parameter once: a repeated parameter matches nothing. Cache the token list on your side.

Rate limits and quotas

Olympex doesn’t publish per-key rate limits or quotas, and responses carry no rate-limit headers. That doesn’t mean capacity is unlimited:
  • Contact partners@olympex.io about your expected volume before you launch, and again before a large increase.
  • Retry 500 and 503 responses with exponential backoff and jitter, and cap how many requests you send in parallel.
  • The API gateway can answer 429 with {"message":"Too Many Requests"} when it receives too many requests in a short time. Wait, then retry with exponential backoff and jitter, signing each attempt again, and send fewer requests in parallel.
  • Poll no faster than you need to, whether you follow a cross-chain transfer or an order. For cross-chain status, poll every 15 to 30 seconds, for example.
Cache the results of GET /chains and GET /tokens instead of calling them before every quote. The token list is large and changes rarely: cache it for 24 hours, for example.

What this means for your integration

  • Sign immediately before each send, with a synchronized clock and a new nonce.
  • Set your client timeout a little above 30 seconds, and retry timeouts and 500-class errors with backoff and jitter.
  • Broadcast calldata right after POST /swap, once eth_estimateGas passes, and request a new quote and swap if you miss the window.
  • Send amounts and decimals as strings (JSON numbers in DCA strategies), approve only what your open orders need, and talk to Olympex about your expected volume before launch.

Errors and retries

Which failures to retry, and how.

Sign requests

The timestamp, nonce and body rules in the signing algorithm.

Execute a swap

Quote, build and broadcast inside the calldata window.

Going to production

The checklist before you send real traffic.