You say what you hold, what you want, and how much — from=SOL, to=BTC, amountIn=100. Everything else is the router's job. It works out which market that conversion lives in, which side of it you are on, which venue or venues out of roughly 60 exchanges streamed live over WebSocket give you the best price for that size, how much to send to each, and whether going through an intermediary asset beats trading the pair directly. It answers with the venues, the sizes, the expected average price, the fees, and how much worse that price is than the best price available anywhere. The computation takes about 0.3 ms, so the answer is as current as the last book update it received.
There is no side parameter, on purpose. Direction follows from the two assets: from=USDT&to=BTC is a buy of BTC/USDT, from=BTC&to=USDT is a sell of the same market. That is the step callers most often get backwards, so the router derives it rather than asking.
It does not place the orders. Nothing here touches your funds, your accounts, or your exchange credentials. It is a venue-and-price recommendation; you execute it. The last section spells out exactly where the line is.
The best bid and best ask on your screen are the price for the next infinitesimal unit. They are not the price for your order, and the gap between the two grows with your size. A router that ranks venues on top of book is answering a question nobody asked.
So this one does not. For every venue quoting the pair, it walks that venue's book down to your actual size, computes the volume-weighted average you would really pay, and adds that venue's taker fee to every level before anything is compared. Only then are the venues ranked.
Buying 5 BTC, two venues, both charging 10 bps taker. The numbers are illustrative; the mechanics are exactly what the router does.
| Venue | Level | Ask | Size there |
|---|---|---|---|
| Alpha | 1 | 104,780 | 0.4 BTC |
| Alpha | 2 | 104,940 | 1.1 BTC |
| Alpha | 3 | 105,080 | 3.5 BTC |
| Beta | 1 | 104,805 | 3.5 BTC |
| Beta | 2 | 104,880 | 6.0 BTC |
Alpha has the better ask — 104,780 against Beta's 104,805, about 2.4 bps cheaper. Anyone ranking on top of book sends the order to Alpha. But Alpha only has 0.4 BTC at that price; the other 4.6 BTC comes off levels that are worse than anything Beta is showing.
Alpha alone, 5 BTC VWAP 105,025.20 cost 525,126.00 USDT
Beta alone, 5 BTC VWAP 104,827.50 cost 524,137.50 USDT -18.9 bps
Both, merged VWAP 104,819.50 cost 524,097.50 USDT -19.6 bps
The venue with the best headline price is the more expensive fill by 988 USDT — about 19 bps. That entire gap is depth. It has nothing to do with fees, and no amount of clever splitting recovers it. Choosing the right venue for the size does.
Fees are folded into each level before the books meet, not netted off the total afterwards. That matters when venues charge differently: a venue 2 bps better on price and 5 bps worse on fees is the worse venue, and the comparison should say so at every level rather than at the end. It is also what makes the merged split below cost-minimal rather than a heuristic that usually works — once every level carries its true cost, taking them cheapest-first is provably the cheapest fill available across those books.
Every hop of every route reports two numbers that answer "how much did size cost me here":
referencePrice — the best fee-adjusted price on any fresh venue the router is permitted to use, for an infinitesimally small order. The frictionless price. In the example above, Alpha's 104,780 plus its fee.impactBps — how much worse your actual size executed than that. The merged 5 BTC fill above lands 3.8 bps above the reference. Putting all 5 on Alpha because it had the best ask would have been 23 bps.Positive always means worse, on both sides. Buying, positive means you paid above the benchmark; selling, positive means you received below it. There is no sign to flip depending on which way you are going.
The benchmark is deliberately the best price anywhere, not the chosen venue's own top of book. Measuring against the venue you ended up on would report zero impact for an order that demonstrably paid more than the market's best price, simply because the cheapest venue was too thin to use — a number that only ever measures a venue against itself. Fees and the staleness adjustment sit in both halves of the comparison and cancel, so what remains is purely the cost of consuming depth.
On a bridged route the same comparison runs end to end: the reference rate chains each hop's frictionless price and impactBps measures the realised rate against it, so both hops' depth costs land in one number rather than being left for you to combine.
Impact is only meaningful next to your expected move. Ten basis points is nothing on a position you expect to hold for a week and fatal on a two-day carry trade. Once you have compared it to your edge, there are four responses and they are not interchangeable:
| Response | What it changes | When it is the right call |
|---|---|---|
| Trade smaller | Fewer levels consumed, so a lower average per unit. Halving the size often more than halves the impact, because the levels you drop are the worst ones. | The impact came from clearing several levels rather than from a wide spread. |
| Split across venues | Fills the cheapest levels wherever they sit instead of only where you started. Measured gain: 0.19–2.2 bps. | Several venues quote the pair and none of them is deep enough alone. |
| Wait | Books refill. Nothing in the router models how fast — that judgement is yours. | Impact dwarfs your edge and the trade is not urgent. |
| Accept it | Nothing. You pay the impact and get the fill now. | Impact is small relative to the move you are expecting. |
Splitting will not rescue a large impact number. The measured gain from splitting is 0.19–2.2 bps. If impactBps reads 30, splitting recovers a rounding error of it. A big impact is telling you the order is large relative to the visible book, and the honest answers to that are a smaller order, more time, or a different venue — not a cleverer allocation.
By default the router puts the whole order on one venue: best_single, the venue that fills your full size most cheaply after fees. Splitting is something you ask for, in one of two shapes.
split_optimal — every fresh venue's book merged into one price-ordered ladder and walked. Because each level was fee-adjusted before the merge, walking it cheapest-first is the lowest-cost fill available across those books at that instant.split_capped with maxVenues (3 by default) — the same walk, then restricted to the venues carrying the most size and re-solved. Three venues capture about 95% of the unconstrained gain.The honest size of the prize is 0.19–2.2 bps, depending on how the books happen to line up. On a 500,000 USDT order that is roughly 10 to 110 USDT. Set that against the 19 bps that venue selection was worth in the example above — nearly ten times the entire splitting range. Splitting is the last few basis points after the venue choice has done the real work, and it should be sized in your head accordingly.
What splitting costs you does not appear in any of those numbers:
minLegNotional is the control: set it to the smallest leg worth placing and anything under it is dropped and the rest re-solved.On single-hop routes the response reports what the split actually beat the best single venue by, in basis points. Read that before you decide the extra orders are worth placing. When it comes back at 0.3, they usually are not.
If no SOL/BTC book is being streamed, the conversion still exists — through an intermediary. The router builds every candidate it can: the direct market, plus one two-hop route per bridge asset that has both legs listed (USDT, USDC, BTC and ETH by default). Then it solves all of them and takes the best. It does not assume USDT is deepest and stop looking.
That distinction earns its keep. Live, USDC→TRY routes through ETH and beats the obvious USDT path by about 9 bps. A thin direct pair can likewise lose to going around it. Every candidate comes back with its output and its score, so "why this market?" is auditable in the same way "why this venue?" is.
A longer path must beat a shorter one by more than a hop penalty — 5 bps per extra hop by default — before it is allowed to win. A second order is a second chance for the price to move between fills, and that risk is in no order book. A bridge that wins by a hair is not actually the better trade. If you want the paths ranked purely on rate, hopPenaltyBps=0 does that, and you own the consequence.
If the first hop can consume everything you offered but the second cannot absorb what it produces, the router cuts the first hop back to what the second will actually take. Without that it would recommend selling 10 SOL to buy 0.01 BTC and leaving the other 99 USDT parked in the bridge — while reporting a complete fill, because only the side you pinned was ever measured. Instead you get a proposal for the 0.1 SOL the route can genuinely complete, a fill ratio of 0.01, and a partial-fill warning whose percentage matches the numbers beside it.
One limitation to know before you ask: a bridged route can only be requested by what you spend (amountIn), not by what you want to receive (amountOut). Solving backwards from the destination across a bridge is a different traversal, and the router refuses it rather than approximating. The refusal is reported distinctly, so you know to retry with amountIn rather than concluding the two assets are unreachable.
fillRatio is the fraction of what you asked for that the route actually covers. A route that covers 62% of your size still reports an effective rate, and that rate is entirely real — for the 62%.
requested 5 BTC
fillRatio 0.62 -> the route covers 3.1 BTC
effectiveRate ... -> the average rate for those 3.1 BTC, not for 5
unfilledAmount 1.9 BTC -> and this part would cost MORE than the rate above
The trap is arithmetic, not wording. The portion that filled is the cheap part of the book by construction; the levels that would have covered the remaining 1.9 BTC sit beyond everything already priced in. Multiplying the effective rate by your full size therefore understates the cost of the trade — always, and in the direction that flatters it. That is why the response carries an explicit partial-fill warning with the percentage in it, and why the rate is the second thing to look at rather than the first.
Two different situations both surface as a fill ratio below 1: a single market that simply ran out of depth, and a bridged route that was trimmed back to what its second hop could absorb. In both cases the rate you are looking at prices only what the route can actually complete.
If a partial fill is worse than no trade — you are closing a position, or the leftover leaves you unhedged — ask for requireFullFill=true and you get a clean refusal instead of a partial quote.
"Not fully fillable" can mean the router cannot see far enough, not that the liquidity is absent. Some venues stream capped depth — Kraken 10 levels, Gate 50 — so a large order can exhaust the cached book while real depth sits below it. On those venues, treat a partial fill on a large order as "unknown past this point" rather than "the market ends here", and check the venue directly before concluding your size is unfillable.
A response with no route and a stated reason is not the service failing. It is the service declining to hand you a price it does not believe. This matters more than it sounds: a quote built on a book that stopped updating four minutes ago is indistinguishable, on screen, from a good one. Books older than the freshness window — 5 seconds by default — are therefore excluded from ranking outright rather than quoted with a caveat.
| Reason | What actually happened |
|---|---|
no_market | Neither direction of the pair is streamed, and no bridge asset has both legs. There is nothing to route through. |
all_books_stale | Venues quote this pair, but every one of their books is older than the freshness window. Common on the illiquid tail. Retry, or widen the window deliberately. |
no_liquidity | Fresh books, but nothing resting on the side you need to trade against. |
insufficient_depth | You asked for all-or-nothing and the visible depth could not cover the size. Ask again without it to see how far it would have got. |
no_venues_matched_filter | Your own venue filter left nothing standing. Widen it. |
exact_out_multi_hop_unsupported | You asked by the amount you want to receive, on a conversion that needs a bridge. Ask by the amount you want to spend. |
You do not configure any of this, and that is deliberate. Two things happen automatically. Books past the freshness cutoff are excluded outright. Books inside it are marked down as they age — the markdown grows with the square root of the book's age, which is how far a price is expected to have drifted since it was captured, so a four-second-old quote has to be genuinely better to win rather than merely look better.
Deciding whether a price is still real is the router's job. You cannot see the update rates that judgment is made against, and a millisecond threshold would be the wrong shape for the question anyway. What you do get is transparency: every response echoes the cutoff and the markdown that were applied, and every venue carries the age of the book it was priced from.
A refusal comes back as a normal, successful response with an empty route and a reason — not as an error. Anything you build on top of this should read the reason, not the status code.
It is not an exchange, a broker, or a custodian. Precisely:
What you get is a recommendation: trade these venues, in these sizes, expect roughly this average price, and here is how much worse than the best available price that is. Placing the orders is yours — and so is everything that happens between the quote and the fill.
minLegNotional is the control you have, and setting it is your call.