Skip to main content

Swap

Execute token swaps without the AI agent — same-chain EVM, Solana, and cross-chain. Get a quote first, then execute.

Quote

POST /wallet/swap-quote

Request Body

FieldTypeRequiredDescription
fromChainstringYesSource chain — one of base, mainnet (Ethereum), polygon, unichain, arbitrum, bnb, worldchain, robinhood, solana
fromTokenstringYesSell token — EVM address, or a base58 mint when fromChain is solana
toChainstringYesDestination chain (same list). May differ from fromChain for a cross-chain swap
toTokenstringYesBuy token — EVM address, or a base58 mint when toChain is solana
amountstringYesHuman-readable sell amount (e.g. "0.5")
slippageBpsnumberNoMax slippage tolerance in bps (10–2000). Defaults to 500 (5%). Sets the quote's minBuyAmount; see the note below for how execution applies it
Execution clamps your tolerance on the aggregator path

slippageBps always shapes the quote's minBuyAmount. At execution, only Relay-routed pairs carry your full tolerance through — cross-chain, Solana, and the relay-first chains, except tokenized-stock legs, which take the RFQ path instead. Everywhere else the execution re-quote is clamped to 2% (200 bps), deliberately: the gap between your looser quote tolerance and the tighter execution tolerance preserves headroom for price drift between quote and submit. A 2000 bps quote does not execute at 2000 bps there.

Robinhood tokenized stocks

Executing a swap that involves tokenized stocks on robinhood requires a passed location check; without one /wallet/swap returns 403 with instructions to verify at the Bankr console. The check applies to each leg's chain, so a cross-chain buy into a stock is gated too. Quotes are not gated/wallet/swap-quote will price a stock swap you aren't cleared to execute, so don't treat a successful quote as clearance.

Native gas tokens

For native ETH / POL / BNB, pass the native sentinel address 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee (the zero address works too) as fromToken / toToken — it's normalized automatically.

Solana wallets

Solana legs use the wallet's Solana address. If the wallet has no Solana address provisioned, the endpoint returns 400.

Response (200 OK)

from.amount mirrors the human-readable input (identical to from.formattedAmount), while to.amount is the quoted buy amount in raw base units.

{
"from": {
"chain": "base",
"token": "0x4200000000000000000000000000000000000006",
"amount": "0.5",
"formattedAmount": "0.5",
"symbol": "WETH",
"decimals": 18,
"usdValue": "1622.50"
},
"to": {
"chain": "base",
"token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "1622500000",
"formattedAmount": "1622.50",
"symbol": "USDC",
"decimals": 6,
"usdValue": "1622.50"
},
"minBuyAmount": "1622.50",
"feeBps": 100,
"feeWaivedForEcosystemToken": false,
"slippageBps": 500,
"priceImpactBps": 12,
"swapImpactBps": 12,
"maxPriceImpactBps": 1500,
"sellTokenPriceUsd": 3245,
"buyTokenPriceUsd": 1,
"quoteId": "7c2f9f0e-8f1a-4d1a-9a2b-3c4d5e6f7a8b"
}
FieldTypeDescription
minBuyAmountstringHuman-readable minimum acceptable buy amount — pass it back on execution
feeBpsnumberBankr fee applied to this swap, in bps (0 = fee-free)
feeWaivedForEcosystemTokenbooleantrue when the fee is 0 because a leg is a Bankr ecosystem token
slippageBpsnumberSlippage tolerance baked into minBuyAmount
priceImpactBpsnumber | nullEstimated route price impact for display (pool impact + token taxes, excluding the Bankr fee)
swapImpactBpsnumber | nullFee-exclusive pool impact — the number server-side execution gates on. null = unknown (the server fails open)
networkCostsUsdnumber | nullFixed route costs in USD (gas + bridge/relayer). 0 = none; null = no breakdown available
maxPriceImpactBpsnumber | nullThe wallet's price-impact protection limit. Execution is rejected above it; null = protection disabled
sellTokenPriceUsdnumber | nullUSD unit price of the sell leg
buyTokenPriceUsdnumber | nullUSD unit price of the buy leg
quoteIdstringId of this served quote — echo it on execution to reuse the quote while it's fresh (see the note below)
Every field below to is optional

Only from, to and minBuyAmount are guaranteed. The rest depend on the venue that priced the quote — the same-chain EVM aggregator path omits networkCostsUsd entirely (hence its absence from the Base example above), and the pUSD unwrap path omits quoteId. Treat a missing field the same as null: unknown, don't hard-block on it.

quoteId only affects same-chain EVM execution

Quote reuse is read on the same-chain EVM path only. Cross-chain and Solana executions route to the Relay executor before the stored-quote lookup, so a quoteId from one of those quotes is accepted and silently ignored. It's an optimization either way — a stale, unknown or param-mismatched id just falls back to a fresh quote.

Errors

StatusCause
400Invalid body, unsupported chain, same-token swap, amount too small, an untradable pair, or no Solana address on the wallet
401Missing or invalid authentication
403Buy token is banned or flagged by the security scan
502No quote available from the routing venue — Relay-routed pairs only (cross-chain, Solana, relay-first chains)
500No quote available on the same-chain EVM aggregator path, or an unexpected quote failure

Execute

POST /wallet/swap

Request Body

All quote fields, plus minBuyAmount:

FieldTypeRequiredDescription
fromChainstringYesSource chain (see quote table above)
fromTokenstringYesSell token address or Solana mint
toChainstringYesDestination chain — may differ from fromChain
toTokenstringYesBuy token address or Solana mint
amountstringYesHuman-readable sell amount
minBuyAmountstringYesMinimum acceptable buy amount (from the quote response)
slippageBpsnumberNoMax slippage tolerance in bps (10–2000). Defaults to 500
quoteIdstringNoquoteId from a fresh quote — reuses it to skip re-pricing. Unknown or stale ids fall back silently
idempotencyKeystringNoUUID duplicate-submit guard. A repeat POST with the same key returns the original result, never a second broadcast
Use an idempotency key

Send an idempotencyKey on every execution. Without one, a retried request after a network timeout can broadcast a second swap. While the original is still in flight, a repeat returns 409.

Response (200 OK)

{
"success": true,
"hash": "0xabc123...",
"amountSold": 0.5,
"amountReceived": 1622.5,
"amountSoldRaw": "500000000000000000",
"amountReceivedRaw": "1622500000"
}
FieldTypeDescription
successbooleanWhether the swap filled. false means it mined and reverted — see below
hashstringOn-chain transaction hash
amountSoldnumberHuman-readable sell amount
amountReceivednumberHuman-readable buy amount
amountSoldRawstringSell amount in base units (bigint as string)
amountReceivedRawstringBuy amount in base units (bigint as string)
A reverted swap is a 200, not an error

If the transaction mines but reverts, the endpoint returns 200 with success: false and the real on-chain hash — no tokens were exchanged, but a transaction exists and you are billed gas for it. Check success; do not treat any 2xx as a filled swap.

Use the hash to show the user their failed transaction on a block explorer.

Before 2026-08, a revert returned 400 on some venues and 200 success:false on others, depending on which venue happened to serve the trade. It is now 200 on every venue.

Errors

StatusCause
400Invalid body, unsupported chain, insufficient balance or gas, venue-side price impact too high, or swap validation failure
401Missing or invalid authentication
403Read-only API key, wallet paused, price impact above your wallet's own protection limit, failed location check, fee beneficiary selling its own fee token, or a Bankr Terminal spend limit would be exceeded
409A swap with the same idempotencyKey is still processing, or a pending/replacement transaction is in the way
429Rate limited — retry in a moment
502No fresh quote at execution time, a Relay fill failed, or a LaunchLab fill was broadcast but couldn't be confirmed
503The swap service is temporarily unavailable — retry shortly
504Submitted, but confirmation is taking longer than expected
500Signer error, swap execution error, or venue failure
Never blind-retry a 504 or a LaunchLab 502

Both mean the transaction may already be on-chain — it was broadcast and only the confirmation is missing. Check the wallet's Activity for the hash before retrying; an automatic retry can execute a second swap. Everything else in the table is either pre-broadcast (safe to retry, ideally with the same idempotencyKey) or a terminal failure; 429 and 503 are always safe to retry.

Two different price-impact rejections

400 is the venue refusing the trade before broadcast — the pool can't absorb it; retry smaller. (A trade that broadcast and then reverted is a 200 with success: false, not a 400.) 403 is your wallet's own price-impact protection (maxPriceImpactBps) rejecting the fresh execution quote; a smaller amount may help, or raise the limit in Security settings. Don't read a 403 here as an auth or location problem.

Access Control

  • Authentication: API key (X-API-Key) or Privy JWT
  • Wallet API (walletApiEnabled): required for both endpoints
  • Read-only keys: allowed for quotes, rejected for execution
  • Allowed recipients: not enforced — swap output always returns to the caller's wallet
  • Spend limits: enforced on execution — the sell-side USD value is priced and checked against your Bankr Terminal per-transaction and daily limits, and a successful swap counts toward your rolling 24h spend total
  • Price-impact protection: execution is rejected when the quote's fee-exclusive pool impact exceeds your wallet's maxPriceImpactBps
  • Token security: buys into banned or scan-flagged tokens are refused at both quote and execution. Sells are essentially never gated, so you can exit a position you hold — the one exception is a fee beneficiary selling its own fee token, which must go through a Glidepath instead

Routing

Bankr picks the venue for you; there's nothing to configure on the request.

  • Same-chain EVM — routed through the DEX aggregator, with a direct-pool venue preferred on thin-liquidity chains
  • Cross-chain, and any Solana leg — routed through the bridge/swap aggregator
  • Robinhood Chain tokenized stocks — quoted and filled through the DEX aggregator's RFQ makers, settling against USDG. Non-stock Robinhood pairs keep the thin-pool protections
  • pUSD → USDC.e on Polygon — not a DEX swap. It's quoted and executed as the 1:1 on-chain Offramp unwrap: no fee, no slippage, no price impact. The reverse direction (USDC.e → pUSD) is not a special route and is quoted like any other pair

Solana LaunchLab fallback

Brand-new Solana launches with no aggregator route can still fill against their Raydium LaunchLab bonding curve, but the fallback is narrow. All of the following must hold:

  • Both legs are on Solana — a cross-chain leg into Solana doesn't qualify
  • The pair is SOL ↔ token, and the token has an active (un-migrated) LaunchLab curve
  • The aggregator returned a genuine no-route, not some other failure
  • The wallet has no price-impact limit set. The bonding-curve venue exposes no impact figure, so it can't be checked against maxPriceImpactBps. Rather than fill an unguardable venue, Bankr fails closed and surfaces the clean no-route instead — so with price-impact protection enabled, these swaps are rejected by design. The minBuyAmount floor is still enforced inside the fill.

Constraints

  • Same-token swaps are rejectedfromToken and toToken must differ when fromChain and toChain match
  • Solana legs require a provisioned Solana address on the wallet

Migration from Legacy Endpoints

POST /leaderboard/swap-quote and POST /leaderboard/swap are deprecated aliases that forward to these endpoints. They now return Deprecation and Sunset headers. Migrate to /wallet/swap-quote and /wallet/swap.

Examples

curl

# 1. Get a quote
curl -X POST https://api.bankr.bot/wallet/swap-quote \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"fromChain": "base",
"fromToken": "0x4200000000000000000000000000000000000006",
"toChain": "base",
"toToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "0.5"
}'

# 2. Execute the swap (use minBuyAmount from quote response)
curl -X POST https://api.bankr.bot/wallet/swap \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"fromChain": "base",
"fromToken": "0x4200000000000000000000000000000000000006",
"toChain": "base",
"toToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "0.5",
"minBuyAmount": "1622.50"
}'