Your first quote in two minutes
The base URL is https://www.chainbridge.trading/api. There is nothing to install and no key to request: send a GET request and read the JSON. The example asks every live source for a quote on 0.1 ETH to USDC on Base.
"color:#5c6370"># Quote 0.1 ETH -> USDC on Base(amounts are in the token's smallest unit) curl "https://www.chainbridge.trading/api/sor/quotes?chainId=8453\ &sellToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\ &buyToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\ &sellAmount=100000000000000000"
Three conventions apply everywhere:
- Amounts are integers in smallest units. 1 ETH is
1000000000000000000, 1 USDC is1000000. Convert withparseUnits/formatUnitsand the token's real decimals. - The native gas token (ETH, BNB, POL, AVAX) uses the address
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. - Responses share one envelope:
{ success: true, data }or{ success: false, error, code }with a 4xx or 5xx status.
Quotes: parameters and response
GET /api/sor/quotes queries the sources enabled on the chain in parallel. Each quote is ranked by what you would actually receive: output minus the platform fee and minus gas converted to the buy token. The winner is bestQuote. Failed sources stay in quotes with an error field, so filter them out before you display a list.
| Parameter | Type | Description |
|---|---|---|
chainId | number | 1, 10, 56, 137, 8453, 42161, 43114 or 59144. Defaults to 1. |
sellToken | address | Token you sell. Native gas token: 0xEeee...EEeE. |
buyToken | address | Token you buy. Must differ from sellToken. |
sellAmount | string | Integer in the sell token’s smallest unit (wei for ETH, 6 decimals for USDC). |
takerAddress | address | Optional on /sor/quotes, required on /sor/swap. Needed for gasless quotes. |
slippageBps | number | 0 to 5000. Defaults to 100 (1%). Sets buyAmountMin. |
aggregators | string | Comma-separated filter: 0x, 0x-gasless, kyber, paraswap. |
preferredAggregator | string | Ask one source first. Quotes are still ranked by net amount. |
sellTokenDecimals, buyTokenDecimals | number | Optional. Used for fee and gas pricing of tokens outside the built-in lists. |
feeRecipient, feeBps | address, number | Partner fee. Only for allowlisted recipients, max 300 bps; an unknown recipient is rejected. |
{
"success": true,
"data": {
"request": { "chainId": 8453, "sellToken": "0xEeee...", "buyToken": "0x8335...", "sellAmount": "100000000000000000", "slippageBps": 100 },
"quotes": [
{ "aggregator": "paraswap", "buyAmount": "252599701", "buyAmountMin": "250073704", "gas": "159640", "gasCostUSD": "0.002469", "feeBps": 5, "netBuyAmount": "252597232", "allowanceTarget": "0x6a00...", "sources": [{ "name": "...", "proportion": 1 }] },
{ "aggregator": "0x", "buyAmount": "0", "buyAmountMin": "0", "error": "no Route matched with those values" }
],
"bestQuote": { "aggregator": "paraswap", "buyAmount": "252599701", "...": "..." },
"feeBps": 5,
"stats": { "totalAggregators": 4, "successfulQuotes": 2, "timestamp": 1791878513443 }
}
}| Field | Meaning |
|---|---|
aggregator | Source of the quote: 0x, 0x-gasless, kyber or paraswap. |
buyAmount / buyAmountMin | Expected output and the minimum after slippage, in buy-token smallest units. |
netBuyAmount | buyAmount minus estimated gas, in buy-token units. Used for ranking when gas can be priced. |
gas, gasPrice, gasCostUSD | Gas estimate for the swap transaction. |
feeBps | Fee actually applied by this quote (0 for a source that carries no fee). |
allowanceTarget | Contract to approve before an ERC-20 sell. |
to, data, value | Transaction to send (on /sor/swap only). |
sources | Liquidity sources and their share of the route, when the aggregator reports them. |
error | Present when that source failed. Its amounts are "0": skip it. |
The top-level feeBps is the platform fee due on this trade: 5 bps under $1,000, 10 bps from $1,000 to $9,999, 20 bps from $10,000, 10 bps when the USD value is unknown and 1 bp on stablecoin pairs. The USD value is computed on the server. Positive slippage is never captured: it stays with the taker. Read smart order routing for how the ranking works.
Build and send a swap
GET /api/sor/swap takes the same parameters, requires takerAddress and returns quotes that include to, data and value. ChainBridge never holds funds: you sign and broadcast the transaction yourself.
Request an executable quote
Pass the wallet that will send the transaction as
takerAddress. Calldata is built for that address only.Approve the exact amount (ERC-20 only)
If the allowance is short, approve
sellAmounttoallowanceTarget. Avoid unlimited approvals.Simulate, sign and send
Run an
eth_callwith the same fields first: a revert costs nothing there. Then sendto,dataandvalueand wait for the receipt.
import { createWalletClient, createPublicClient, http, erc20Abi } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { base } from 'viem/chains' const API = 'https://www.chainbridge.trading/api' const NATIVE = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase() const account = privateKeyToAccount(process.env.PRIVATE_KEY) const wallet = createWalletClient({ account, chain: base, transport: http() }) const client = createPublicClient({ chain: base, transport: http() }) const sellToken = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' "color:#5c6370">// USDC const sellAmount = 25_000_000n "color:#5c6370">// 25 USDC(6 decimals) "color:#5c6370">// 1. Executable quotes: GET needs no CSRF token const params = new URLSearchParams({ chainId: '8453', sellToken, buyToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', sellAmount: sellAmount.toString(), takerAddress: account.address, slippageBps: '50', }) const res = await fetch(`${API}/sor/swap?${params}`) const { success, data, error } = await res.json() if (!success) throw new Error(error) const quote = data.bestQuote if (!quote?.to || !quote?.data) throw new Error('No executable route') "color:#5c6370">// 2. ERC-20 only: approve the exact amount to the quote's allowanceTarget if (sellToken.toLowerCase() !== NATIVE) { const allowance = await client.readContract({ address: sellToken, abi: erc20Abi, functionName: 'allowance', args: [account.address, quote.allowanceTarget], }) if (allowance < sellAmount) { const hash = await wallet.writeContract({ address: sellToken, abi: erc20Abi, functionName: 'approve', args: [quote.allowanceTarget, sellAmount], }) await client.waitForTransactionReceipt({ hash }) } } // 3. Simulate, then send the swap const tx = { account, to: quote.to, data: quote.data, value: BigInt(quote.value ?? '0') } await client.call(tx) "color:#5c6370">// throws if the swap would revert const hash = await wallet.sendTransaction(tx) const receipt = await client.waitForTransactionReceipt({ hash }) console.log(receipt.status, hash)
Gasless quotes. When aggregator is 0x-gasless, data is a JSON string holding a trade (and sometimes an approval) EIP-712 payload. Sign each one and send them to POST /api/gasless/submit as { chainId, trade: { type, eip712, signature }, approval? }, then follow GET /api/gasless/status?tradeHash=&chainId=. Gasless is not available on Linea, and smart contract wallets cannot use it.
POST requests and the CSRF token
Four POST endpoints change state or build transactions: /api/sor/swap, /api/gasless/submit, /api/bridge/transaction and /api/simulation. They reject requests without an x-csrf-token header (403, CSRF_VALIDATION_FAILED). Fetch a token from GET /api/csrf-token; it is signed and valid for two hours.
TOKEN=$(curl -s https://www.chainbridge.trading/api/csrf-token | jq -r .token) curl -X POST https://www.chainbridge.trading/api/sor/swap \ -H "Content-Type: application/json" \ -H "x-csrf-token: $TOKEN" \ -d '{"chainId":8453,"sellToken":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE","buyToken":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","sellAmount":"100000000000000000","takerAddress":"0xYourAddress","preferredAggregator":"kyber"}'
POST /api/sor/swap accepts the same fields as the GET version in a JSON body, plus strictAggregator: true to fail instead of falling back when the preferred source has no route.
Prices and live streams
GET /api/price?chainId=1&tokens=0x...,0x... returns USD prices for up to 100 tokens on one chain, keyed by lowercase address. A token without a price returns null.
GET /api/price/stream is a Server-Sent Events stream for up to 20 tokens. Set interval in milliseconds (2,000 to 60,000, default 5,000). Each message is a JSON object with a type: price_update carries chainId, prices and timestamp; error carries a message; timeout is sent when the stream ends after 30 minutes. A comment line is sent every 30 seconds as a heartbeat. Each IP can hold up to 5 streams open at once.
const params = new URLSearchParams({ chainId: '1', tokens: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', interval: '5000', }) const stream = new EventSource(`https://www.chainbridge.trading/api/price/stream?${params}`) stream.onmessage = (e) => { const msg = JSON.parse(e.data) if (msg.type === 'price_update') render(msg.prices) "color:#5c6370">// { "0xa0b8...": 0.9998, ... } if (msg.type === 'timeout') stream.close() "color:#5c6370">// 30-minute cap: reconnect if you need more }
Cross-chain quotes
GET /api/crosschain/quote compares Relay and LI.FI for EVM-to-EVM transfers and THORChain for native BTC, LTC, DOGE and BCH, ranked by net amount received. Each quote lists the steps to execute in order: an exact-amount approve when needed, then a transaction.
curl "https://www.chainbridge.trading/api/crosschain/quote?fromChainId=8453&toChainId=42161\ &fromToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\ &toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831\ &fromTokenDecimals=6&toTokenDecimals=6\ &amount=100000000&fromAddress=0xYourAddress"
| Parameter | Description |
|---|---|
fromChainId or fromChain | EVM chain id, or BTC / LTC / DOGE / BCH (quote only). |
toChainId or toChain | Destination. Exactly one of the two. |
fromToken, toToken | Token addresses on each EVM side. |
fromTokenDecimals, toTokenDecimals | Required, 0 to 36. |
amount | Integer in smallest units. |
fromAddress, toAddress | Sender (required from an EVM chain) and recipient (defaults to the sender; a native address for BTC and others). |
slippageBps, providers | 1 to 5000, default 100. Optional filter: relay,lifi,thorchain. |
The response is { quotes, best, errors }. Each quote has toAmount, toAmountMin, gasUSD, providerFeeUSD, platformFeeBps, estimatedSeconds, executable, trackingId and warnings. After sending, poll GET /api/crosschain/status with provider, srcTxHash and fromChainId (plus requestId for Relay, and toChain or toChainId for THORChain) until phase is delivered, refunded or failed. THORChain deposits are accepted from Ethereum, Base, BNB Chain and Avalanche.
Rate limits, errors and other endpoints
Limits are counted per IP over a one-minute window. Responses include X-RateLimit-Limit and X-RateLimit-Remaining; a 429 response carries Retry-After in seconds and the code RATE_LIMIT_EXCEEDED.
| Endpoints | Limit / min |
|---|---|
| /sor/quotes, /crosschain/*, most GET routes | 60 |
| /sor/swap, /gasless/* | 20 |
| /bridge/* | 30 |
| /price, /price/history | 100 |
| Status routes (/gasless/status, /bridge/status...) | 120 |
| /mcp | 120 |
| All routes combined, per IP | 300 |
Validation errors return 400 with a readable error such as Invalid chain ID or takerAddress is required for swap quotes. Upstream failures return 5xx; retry after a short delay.
| Endpoint | Use |
|---|---|
GET /sor/aggregators?chainId= | Sources available on a chain and the list of supported chains. |
GET /token-safety?chainId=&addresses= | GoPlus checks for up to 20 tokens: honeypot, sell tax, owner permissions. |
GET /price/history?chainId=&sellToken=&buyToken=&range= | Price points for 24h, 7d or 30d (pair price, or sell token in USD). |
POST /gasless/submit, GET /gasless/status | Submit a signed 0x gasless order and follow it (also /gasless/status/stream). |
GET /bridge/quote, POST /bridge/transaction, GET /bridge/status | Legacy LI.FI / Socket bridge routes, kept for existing clients. |
GET /health | Service status: { status, checks, version, uptime } without the success envelope. |
POST /mcp | Model Context Protocol server with read-only tools for AI agents. |
Building an AI agent? The MCP endpoint (POST /api/mcp) exposes quotes, prices, token lists and unsigned swap transactions as read-only tools. For ready-made scripts, read the DevTools quickstart.