Skip to main content
POST
POST /swap returns unsigned transaction calldata for a route: a swap on one chain (mode: "single-chain") or a transfer between two chains (mode: "cross-chain"). Olympex never signs or broadcasts your transaction. You approve the input token when needed, then send the transaction from account with your own signer. Get a quote from POST /quotes first and pass its aggregatorId here.
The response is real calldata for mainnet contracts. Broadcasting it from account moves that wallet’s funds, and there is no testnet or sandbox. Start with small amounts.

Request body

The swap takes the same trade as the quote, plus the wallet and the route:
  • aggregatorId is the value POST /quotes returned for the same pair and amount. Cross-chain swaps from signed API accounts can use okx or rango. On single-chain quotes, quote.aggregatorOrder lists every source that quoted, best first: if the swap fails with 422 NO_ROUTE or 500 SWAP_ERROR, or eth_estimateGas of its calldata reverts, build it again with the next one.
  • account is the wallet that sends the transaction and receives the output (on the destination chain, for cross-chain). The calldata is bound to it.
  • Keep the quote’s inputs. Send the same amount, slippage, gasPrice (single-chain) and, if you used it, fees object. amount stays human-readable: "10" is 10 USDT. See Gas and fees for fees.
  • dryRun is accepted on single-chain bodies for compatibility and has no effect, because this endpoint never broadcasts. Cross-chain bodies reject it with 400, like any other unknown top-level key.

Response fields

Prices move between the quote and the swap. Show your user outAmount and minOutAmount from this response, not from the quote.

Estimate gas yourself

gasLimit is estimatedGas × 2, and estimatedGas is unreliable: it’s "1500000" as a placeholder when the source gives no estimate, it can be "0", and on cross-chain routes it can be a wei amount rather than gas units (on okx routes, the gas price). A source’s estimate also covers only its own part of the route, not the Olympex contracts, so the transaction uses more gas than the quote’s estimatedGas. Estimate gas for the exact transaction with eth_estimateGas (from: account, to, data or calldata, value) and add a buffer, for example 20%. Use gasLimit only as a fallback, and never when it’s "0". Gas and fees has more on both estimates.

Check that the calldata executes

A 200 means Olympex built calldata for the route, not that the transaction succeeds: a source can return calldata that reverts. Always run eth_estimateGas on the exact transaction before you send it, and treat a revert as a failed build:
  • Rule out your side first. Check the allowance to contractToApprove, the balance of account and the calldata’s expiry.
  • Fall back to another route. If those are fine, the route can’t execute. For a single-chain swap, build it again with the next aggregatorId in the quote’s aggregatorOrder. For a cross-chain swap, request a new quote and build with its aggregatorId. Show the user the new outAmount and minOutAmount before they sign.
  • Send right after you build. Some routes include a market maker’s firm quote (an RFQ leg) whose price expires within seconds of the build. If the estimate or the transaction reverts because that quote expired, build the swap again and send it at once.

Execute the transaction

1

Approve the exact amount (ERC-20 input only)

Read allowance(account, contractToApprove) on the input token. If it’s below amount in base units, send approve(contractToApprove, amount) for exactly that amount, never an unlimited one, and wait for it to confirm. Some tokens, such as USDT, require you to set the allowance to 0 before you change it: do that first. Native-token input needs no approval.
2

Estimate gas

Call eth_estimateGas from account with to, data (or calldata) and value, then add your buffer. Estimation usually fails until the approval confirms. If it still reverts once the approval has confirmed, don’t send: fall back to another route.
3

Send right away

Send {to, data | calldata, value, gas} from account as soon as you have the estimate. If the approval took a while to confirm, call POST /swap again before you send.
4

Track the result

Poll GET /transactions/{hash} with the chain ID until status is success or reverted, or read the receipt from your RPC. For a cross-chain transfer, then poll POST /tx-status with the source transaction hash, the source chain ID and dexHash.
The calldata carries an on-chain expiry, 5 minutes on most routes. Routes with a market maker’s firm quote expire sooner, within seconds of the build. The calldata is also bound to account: another Olympex swap from the same account can invalidate calldata you built earlier. Call POST /swap right before the user signs, not when you first show the quote, and send at once.
Execute a swap and Cross-chain swap end-to-end cover the full flow, from quote to confirmed transaction. Both 200 examples shorten the calldata with …: a real response carries the full hex string. The cross-chain 200 example goes from 10 USDT on Polygon to USDT on Ethereum through rango. Its gasLimit, "323438069024099968", is derived from a wei amount, not a number of gas units: estimate gas yourself.

Authorizations

x-api-key-id
string
header
required

Your API key ID (UUID). See Sign requests.

x-value-info
string
header
required

Base64 of timestamp + "\n" + nonce + "\n" + bodyHash, where timestamp is Unix seconds, nonce is 24 new hexadecimal characters and bodyHash is the unpadded base64url SHA-256 of the canonical body.

x-passphrase
string
header
required

Your account passphrase. Treat it like the secret key.

x-signature
string
header
required

Lowercase hex HMAC-SHA256 (signature v2), keyed with your secret key string (UTF-8, not decoded), of seven lines joined with \n, with no trailing newline: OLPX-HMAC-SHA256-V2, timestamp, nonce, the uppercase method, path, canonicalQuery and bodyHash. path is the request path including /api/v1, without the query string, for example /api/v1/limit-order/<id>. canonicalQuery is the query parameters decoded (+ is a space), sorted by key and then by value, re-encoded per RFC 3986 and joined with &, or the empty string when there is no query. Headers signed for one request are rejected on any other. See Sign requests.

Body

application/json
mode
enum<string>
required
Available options:
single-chain
params
object
required
dryRun
boolean
default:true

Accepted for compatibility and ignored: POST /swap never broadcasts, whatever the value.

fees
object

Your integrator fee. It appears in integratorFeeBreakdown.integratorMarginBps and integratorMarginAmount.

Response

Transaction calldata. data.mode matches the request.

success
enum<boolean>
required
Available options:
true
data
Single-chain · object
required
meta
object
required