Docs · API
Five endpoints, no API key.
The SwapsPro quoting surface is five CORS-open endpoints under /api/sdk/v1. No key, no account, no signup. Each one has its own page with a live console: fill the parameters, press Try it, read what came back. Every body on this page was captured from production with curl.
GET/chains
List chains
Every chain SwapsPro can quote, with its CAIP-2 id and whether it can be sold from.
GET/tokens
List tokens on a chain
The curated token list for one chain: natives first, then the ERC-20s SwapsPro resolves by symbol.
GET/prices
Spot prices
USD spot prices for up to 100 assets in one call, keyed by CAIP-19 id.
GET/quote
Get a firm quote
One routed quote across every integrated venue, returned ready to sign: a transaction, a CoW order, or a deposit address.
GET/access
Raise the rate limit
A 24-hour grant that lifts /quote from 60 to 600 requests per minute, paid over x402 or unlocked by a Pro Pass.
Base URL#
base url
https://www.swaps.pro/api/sdk/v1The four read endpoints are GET (and /access also takes POST), all answer OPTIONS with a 204 preflight, and all are safe to call directly from a browser. Responses are always JSON.
GET /chains#
Every chain SwapsPro can price. id is the symbol the other endpoints accept; EVM chains also carry the numeric chainId. No parameters. Cached public, s-maxage=3600, stale-while-revalidate=86400.
request
curl "https://www.swaps.pro/api/sdk/v1/chains"200 OK — real response
{
"chains": [
{
"id": "ETH",
"name": "Ethereum",
"nativeSymbol": "ETH",
"caip2": "eip155:1",
"type": "evm",
"chainId": 1
},
{
"id": "BTC",
"name": "Bitcoin",
"nativeSymbol": "BTC",
"caip2": "bip122:000000000019d6689c085ae165831e93",
"type": "utxo"
},
{
"id": "BASE",
"name": "Base",
"nativeSymbol": "ETH",
"caip2": "eip155:8453",
"type": "evm",
"chainId": 8453
},
{
"id": "BSC",
"name": "BNB Smart Chain",
"nativeSymbol": "BNB",
"caip2": "eip155:56",
"type": "evm",
"chainId": 56
},
{
"id": "AVAX",
"name": "Avalanche",
"nativeSymbol": "AVAX",
"caip2": "eip155:43114",
"type": "evm",
"chainId": 43114
},
{
"id": "ARB",
"name": "Arbitrum",
"nativeSymbol": "ETH",
"caip2": "eip155:42161",
"type": "evm",
"chainId": 42161
},
{
"id": "RHD",
"name": "Robinhood Chain",
"nativeSymbol": "ETH",
"caip2": "eip155:4663",
"type": "evm",
"chainId": 4663
},
{
"id": "BCH",
"name": "Bitcoin Cash",
"nativeSymbol": "BCH",
"caip2": "bip122:000000000000000000651ef99cb9fcbe",
"type": "utxo"
},
{
"id": "LTC",
"name": "Litecoin",
"nativeSymbol": "LTC",
"caip2": "bip122:12a765e31ffd4059bada1e25190f6e98",
"type": "utxo"
},
{
"id": "DOGE",
"name": "Dogecoin",
"nativeSymbol": "DOGE",
"caip2": "bip122:1a91e3dace36e2be3bf030a65679fe82",
"type": "utxo"
},
{
"id": "GAIA",
"name": "Cosmos",
"nativeSymbol": "ATOM",
"caip2": "cosmos:cosmoshub-4",
"type": "cosmos"
},
{
"id": "THOR",
"name": "THORChain",
"nativeSymbol": "RUNE",
"caip2": "cosmos:thorchain-mainnet-v1",
"type": "cosmos"
},
{
"id": "HIVE",
"name": "Hive",
"nativeSymbol": "HIVE",
"caip2": "hive:mainnet",
"type": "hive"
}
]
}Hive is listed but not quotable
HIVE appears here because SwapsPro supports it in the app, where a Hive wallet signs. Quoting a Hive pair through this API returns UNSUPPORTED_PAIR.GET /tokens#
| Param | Required | Accepts |
|---|---|---|
chainId | yes | A numeric EVM chain id (1, 8453) or a chain symbol (ETH, BTC). The alias chain is also accepted. |
request
curl "https://www.swaps.pro/api/sdk/v1/tokens?chainId=8453"200 OK — real response
{
"chain": "BASE",
"chainId": 8453,
"tokens": [
{
"symbol": "ETH",
"name": "Base",
"caip": "eip155:8453/slip44:60",
"chain": "BASE",
"isToken": false,
"decimals": 18
},
{
"symbol": "USDC",
"name": "USD Coin",
"caip": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"chain": "BASE",
"isToken": true,
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"decimals": 6
},
{
"symbol": "WETH",
"name": "Wrapped Ether",
"caip": "eip155:8453/erc20:0x4200000000000000000000000000000000000006",
"chain": "BASE",
"isToken": true,
"contract": "0x4200000000000000000000000000000000000006",
"decimals": 18
}
]
}This is a curated list — natives plus the blue chips SwapsPro ships in its own selector — not an index of every token on the chain. You can still quote any ERC-20 by passing its contract address. decimals is present only where SwapsPro knows it statically; the quote endpoint resolves the rest on-chain. Cached like /chains.
GET /quote#
A firm quote, priced by the same route picker the app's Instant tab uses. Same-chain EVM races 0x, CoW and LI.FI against Pioneer and keeps the better net output; Robinhood Chain prices on-chain via Uniswap v3; cross-chain EVM ⇄ EVM adds LI.FI's bridges alongside Pioneer; everything else goes through Pioneer, with THORChain-style memos rewritten to SwapsPro's multi-affiliate split.
Which venue wins matters to you for two reasons, and both are visible in the response: provider names it, and partnerFee says whether that venue could carry your fee. A CoW route returns an order to sign instead of a tx to send.
Parameters#
| Param | Required | Meaning |
|---|---|---|
sellChain | yes | Numeric EVM chain id or chain symbol. |
sellToken | yes | Symbol (USDC), full asset CAIP, or a bare 0x… contract address on an EVM chain. |
buyChain | yes | Numeric EVM chain id or chain symbol. |
buyToken | yes | Same forms as sellToken. |
amount | yes | Positive decimal in human units — 0.1, not 100000000000000000. Hex, exponents and negatives are rejected. |
address | yes | Sender address on the sell chain. |
recipient | cross-chain | Destination on the buy chain. Defaults to address for same-chain; required otherwise. |
slippage | no | Percent, strictly greater than 0 and at most 50. Anything else is a BAD_REQUEST. |
partner | no | Your integrator id. Truncated to 64 characters, echoed back and logged. |
partnerFeeBps | no | Additive partner fee. Floored, clamped to 100 basis points; invalid values become 0. |
Same-chain, selling a native asset#
request
curl "https://www.swaps.pro/api/sdk/v1/quote\
?sellChain=8453&sellToken=ETH\
&buyChain=8453&buyToken=USDC\
&amount=0.1\
&address=0x21c9a94AF76B59b171b32fD125A4edF0e9A2Ad3e\
&partner=docs-example"200 OK — real response (calldata truncated)
{
"provider": "lifi",
"sellChain": "BASE",
"buyChain": "BASE",
"sellToken": {
"caip": "eip155:8453/slip44:60",
"symbol": "ETH"
},
"buyToken": {
"caip": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"symbol": "USDC"
},
"sellAmount": "0.1",
"buyAmount": "242.141603",
"minBuyAmount": "240.930895",
"rate": 2421.41603,
"tx": {
"chainId": 8453,
"to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
"data": "0x736eac0baf8dc21684c3be1834615d93d2e2c88c53354c0e792e6700d5e473b1788f4c95…",
"value": "0x16345785d8a0000",
"gasLimit": "0x1d157f"
},
"expiresAt": "2026-08-23T15:23:41.337Z",
"partner": "docs-example",
"partnerFee": {
"requestedBps": 0,
"collectedBps": 0,
"collected": false,
"note": "No partner fee was requested."
}
}tx is ready for eth_sendTransaction as-is: value and gasLimit are already 0x-hex quantities. There is no approval because the sell asset is native.
Same-chain, selling an ERC-20#
request
curl "https://www.swaps.pro/api/sdk/v1/quote\
?sellChain=1&sellToken=USDC\
&buyChain=1&buyToken=WETH\
&amount=100\
&address=0x21c9a94AF76B59b171b32fD125A4edF0e9A2Ad3e\
&partner=docs-example&partnerFeeBps=25"200 OK — real response (calldata truncated)
{
"provider": "lifi",
"sellChain": "ETH",
"buyChain": "ETH",
"sellToken": {
"caip": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC"
},
"buyToken": {
"caip": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"symbol": "WETH"
},
"sellAmount": "100",
"buyAmount": "0.040875087699744272",
"minBuyAmount": "0.040670712261245551",
"rate": 0.00040875087699744275,
"tx": {
"chainId": 1,
"to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
"data": "0x5fd9ae2ea5a7cef4eb7f19835002ee9f0c9e5d4f186cb431cd167775b31bff69de3fcdb1…",
"value": "0x0",
"gasLimit": "0xc8aef"
},
"approval": {
"chainId": 1,
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"spender": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
"amountWei": "100000000"
},
"expiresAt": "2026-08-23T15:23:49.669Z",
"partner": "docs-example",
"partnerFeeBps": 25,
"partnerFee": {
"requestedBps": 25,
"recipient": "0xAccF0dB4b6B55Ba692467988D0a1188f26428C2b",
"collectedBps": 25,
"collected": true,
"note": "Collected on-chain: 25 bps added to SwapsPro's 30 bps in the LI.FI integrator fee. LI.FI supports a single fee wallet, so your share is settled to you from the quote log."
}
}Now there is an approval. Grant exactly amountWei (base units, decimal string) of token to spender, wait for it to mine, then send tx. Note that partner and partnerFeeBps are echoed back — that echo is your confirmation the attribution was recorded.
Cross-chain, memo route#
request
curl "https://www.swaps.pro/api/sdk/v1/quote\
?sellChain=BTC&sellToken=BTC\
&buyChain=ETH&buyToken=ETH\
&amount=0.01\
&address=bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq\
&recipient=0x21c9a94AF76B59b171b32fD125A4edF0e9A2Ad3e"200 OK — real response
{
"provider": "thorchain",
"sellChain": "BTC",
"buyChain": "ETH",
"sellToken": {
"caip": "bip122:000000000019d6689c085ae165831e93/slip44:0",
"symbol": "BTC"
},
"buyToken": {
"caip": "eip155:1/slip44:60",
"symbol": "ETH"
},
"sellAmount": "0.01",
"buyAmount": "0.31509635",
"minBuyAmount": "0.30564345",
"rate": 31.509635000000003,
"depositAddress": "bc1q2nfxrvvg67nhey0gk0cc8ke2ea4akge8kskyyq",
"memo": "=:ETH.ETH:0x21c9a94AF76B59b171b32fD125A4edF0e9A2Ad3e:30564345:keep/thor1ujdj4360n835r49yzuvvsyu80hv28k9frlqeuh:15/15",
"expiresAt": "2026-08-23T15:39:02.000Z",
"partner": "docs-example",
"partnerFee": {
"requestedBps": 25,
"collectedBps": 0,
"collected": false,
"note": "NOT collected: this route was priced by Pioneer, whose venues expose no affiliate field we can add a partner fee to (and rewriting a THORChain memo's bps would invalidate the quote's own limit). Quote for a pair CoW, LI.FI or 0x can route to earn on it."
}
}No tx here — this route settles by depositing. Send sellAmount of the sell asset to depositAddress with memo attached, from a wallet for the sell chain. The tail of that memo (keep/thor1ujdj…:15/15) is SwapsPro's affiliate split, rewritten server-side; see Fees.
Response fields#
| Field | Type | Present |
|---|---|---|
provider | string | always — the venue that won the price |
sellChain / buyChain | string | always — SwapsPro chain symbols |
sellToken / buyToken | { caip, symbol } | always |
sellAmount / buyAmount | decimal string | always — human units |
minBuyAmount | decimal string | when the venue states one |
rate | number | always — buyAmount / sellAmount |
tx | { chainId, to, data?, value?, gasLimit? } | same-chain EVM routes |
approval | { chainId, token, spender, amountWei } | EVM routes selling an ERC-20 |
depositAddress | string | deposit/memo routes without a tx |
memo | string | deposit/memo routes |
expiresAt | ISO 8601 | always — 60s default when the venue states none |
order | { protocol, chainId, appDataUrl, appData, typedData, postUrl, body, signingScheme, validTo, note } | CoW routes — sign and post instead of broadcasting. Mutually exclusive with tx. |
partner | string | when you sent one |
partnerFeeBps | number | only when a fee is genuinely collected on this route — never an echo of what you asked for |
partnerFee | { requestedBps, collectedBps, collected, recipient?, note } | whenever partner is set — the full story, including why a route collected nothing |
Same-chain, a CoW route#
CoW settles by signature rather than by transaction, so its quotes carry order and no tx. The route is gasless and asynchronous: solvers fill it in a batch auction, usually inside a minute, and it can expire unfilled. The one-time ERC-20 approval in approval still applies — the spender is CoW's vault relayer rather than a swap target.
executing an order route
// A CoW route settles by SIGNATURE, not by transaction: the response carries
// `order` instead of `tx`. Three steps, in this order.
// 0. One-time: approve order.body.sellToken for the vault relayer named in
// quote.approval.spender.
// 1. Register the fee metadata document, so the appData hash resolves.
await fetch(order.appDataUrl, {
method: "PUT",
headers: { "content-type": "application/json" },
body: JSON.stringify({ fullAppData: order.appData.fullAppData }),
});
// 2. Sign the typed data verbatim, from the same address that asked for the quote.
const signature = await window.ethereum.request({
method: "eth_signTypedData_v4",
params: [account, JSON.stringify(order.typedData)],
});
// 3. Post the order.
const res = await fetch(order.postUrl, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ ...order.body, signature, signingScheme: order.signingScheme }),
});
const orderUid = await res.json();No captured body for this one
SdkOrder type and order.note carries the timing trade-off in plain language at runtime.Partner attribution, per venue#
partnerFeeBps is additive on top of SwapsPro's own fee and capped at 100 basis points. It is collected only where the venue can carry a second fee, and the response says which happened rather than echoing your number back at you.
| provider | Carries a partner fee? | How |
|---|---|---|
0x | Yes | Added to SwapsPro's bps in the 0x swapFee. 0x supports one recipient, so your share is settled to you from the quote log. |
cow | Yes | A genuine second volume policy in the order's appData. CoW caps the TOTAL partner fee at 100 bps and SwapsPro's 30 bps is paid first, so a larger request is clamped — and reported clamped. |
lifi | Yes | Added to the LI.FI integrator fee. One fee wallet, so the same off-chain settlement as 0x. |
pioneer | No | Its same-chain venues expose no affiliate field, and rewriting a THORChain memo's bps would invalidate the quote's own limit. |
uniswap-rh | No | A raw Uniswap v3 router call on Robinhood Chain. There is no fee hook in the call. |
A real quote on a route that cannot collect — note that partnerFeeBps is simply absent:
request
curl "https://www.swaps.pro/api/sdk/v1/quote\
?sellChain=4663&sellToken=USDG\
&buyChain=4663&buyToken=WETH\
&amount=100\
&address=0x21c9a94AF76B59b171b32fD125A4edF0e9A2Ad3e\
&partner=docs-example&partnerFeeBps=25"200 OK — real response (calldata truncated)
{
"provider": "uniswap-rh",
"sellChain": "RHD",
"buyChain": "RHD",
"sellToken": {
"caip": "eip155:4663/erc20:0x5fc5360d0400a0fd4f2af552add042d716f1d168",
"symbol": "USDG"
},
"buyToken": {
"caip": "eip155:4663/erc20:0x0bd7d308f8e1639fab988df18a8011f41eacad73",
"symbol": "WETH"
},
"sellAmount": "100",
"buyAmount": "0.041063585609457798",
"minBuyAmount": "0.04065294975336322",
"rate": 0.00041063585609457795,
"tx": {
"chainId": 4663,
"to": "0xcaf681a66d020601342297493863e78c959e5cb2",
"data": "0x04e45aaf0000000000000000000000005fc5360d0400a0fd4f2af552add042d716f1d168…"
},
"approval": {
"chainId": 4663,
"token": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
"spender": "0xcaf681a66d020601342297493863e78c959e5cb2",
"amountWei": "100000000"
},
"expiresAt": "2026-08-23T15:25:10.855Z",
"partner": "docs-example",
"partnerFee": {
"requestedBps": 25,
"collectedBps": 0,
"collected": false,
"note": "NOT collected: Robinhood Chain is quoted as a direct Uniswap v3 router call, which has no fee hook. No aggregator routes 4663 same-chain yet."
}
}Errors#
Every failure is { "error": "...", "code": "..." } with a matching HTTP status. Real responses:
captured errors
$ curl -s -w "\nHTTP %{http_code}\n" ".../quote?sellChain=1"
{"error":"Missing required params: sellToken, buyChain, buyToken, amount, address","code":"BAD_REQUEST"}
HTTP 400
$ curl -s -w "\nHTTP %{http_code}\n" ".../tokens?chainId=999999"
{"error":"Unsupported chain: 999999","code":"UNKNOWN_CHAIN"}
HTTP 404
$ curl -s -w "\nHTTP %{http_code}\n" ".../quote?...&sellToken=NOPE&..."
{"error":"Unknown sellToken on ETH: NOPE","code":"UNKNOWN_TOKEN"}
HTTP 404
$ curl -s -w "\nHTTP %{http_code}\n" ".../quote?...&sellToken=USDC&buyToken=USDC&..."
{"error":"sellToken and buyToken are the same asset","code":"BAD_REQUEST"}
HTTP 400
$ curl -s -w "\nHTTP %{http_code}\n" ".../quote?...&sellChain=HIVE&sellToken=HIVE&..."
{"error":"Hive swaps need a Hive wallet and are not available in SDK v1","code":"UNSUPPORTED_PAIR"}
HTTP 400| code | HTTP | Cause |
|---|---|---|
BAD_REQUEST | 400 | Missing or malformed parameter; same asset on both sides; missing recipient on a cross-chain quote. |
UNSUPPORTED_PAIR | 400 | The pair cannot be routed at all — a Hive leg, or a Robinhood Chain pair whose other leg is not EVM. |
UNKNOWN_CHAIN | 404 | The chain id or symbol is not in /chains. |
UNKNOWN_TOKEN | 404 | The token could not be resolved on that chain. |
NO_ROUTE | 404 | Every venue declined to price the pair right now. |
RATE_LIMITED | 429 | More than 60 quote requests in a minute from your IP. |
UPSTREAM_ERROR | 502 | A routing provider failed in a way the API could not classify. |
CORS#
Wide open, by design — the SDK runs in the browser. Access-Control-Allow-Origin: *, GET, OPTIONS, Content-Type, preflight cached for 86400 seconds.
preflight
$ curl -s -X OPTIONS -D- -o /dev/null "https://www.swaps.pro/api/sdk/v1/quote"
HTTP/2 204
access-control-allow-headers: Content-Type
access-control-allow-methods: GET, OPTIONS
access-control-allow-origin: *
access-control-max-age: 86400
cache-control: public, max-age=0, must-revalidate
content-security-policy: frame-ancestors 'self' https://app.safe.global https://*.safe.global https://farcaster.xyz https://*.farcaster.xyz https://warpcast.com https://*.warpcast.comRate limits#
/quote: 60 requests per minute, in a fixed 60-second window keyed on the first entry ofX-Forwarded-For. Over the limit you get429/RATE_LIMITED. Free, with no key — that is the whole free tier and it is not going anywhere.- Best-effort on this deployment, and you should read that literally: the count lives in memory inside each serverless instance, so the effective ceiling is 60 times however many are warm, and a
429means “you landed on a busy instance” rather than “you are over the limit”. Measured against production: 80 sequential requests were cut off at 60, while 120 issued in parallel got 92 through. Treat it as a courtesy brake against a runaway loop, not a quota, and do not design around it. - There is no paid tier here. Elevation is only sold where the limit is counted in a shared store — charging for a lift on a number nobody can meter would be charging for nothing — so
/api/sdk/v1/accessanswers503on this deployment. The agents page explains the reasoning. /chainsand/tokensare not rate limited. They are static config, cached for an hour at the edge with a day of stale-while-revalidate — cache them on your side too and you will essentially never call them.
Why the number is not a contract
429 a function of routing rather than of your own request rate. It is a courtesy brake, not a billing meter or a security control.Fees#
Where a route carries a SwapsPro fee, it is stamped into the quote server-side, exactly as it is for the app's own users — a caller can neither remove it nor point it elsewhere. Not every route carries one: provider tells you which venue priced the swap, and the fees page says what each venue costs.
A typed wrapper#
If you are in a JavaScript or TypeScript runtime, the SDK wraps these endpoints endpoints with types, typed errors and EIP-1193 execution. It adds no behaviour the API does not already have.
