All articles
Solana·June 6, 2026·6 min read

How Jupiter's Routing Engine Finds the Best Solana Swap Price

Blog article delivered via JSON as requested.

{"excerpt": "How Jupiter's routing engine splits and hops Solana swaps across DEXs to cut price impact, and what it means for arb bots and swap integrations.", "tags": ["Solana", "Jupiter", "DEX Aggregation", "MEV", "Trading Bots"], "cover": "nodes", "content": "Jupiter doesn't trade on its own liquidity. It reads the state of every AMM pool it can index on Solana, treats them as one giant graph, and figures out which path (or combination of paths) gets you the most output tokens for a given input. That sounds simple until you realize "best price" changes with trade size, pool state changes every slot, and the search space includes thousands of pools across Orca, Raydium, Meteora, Phoenix, OpenBook, Lifinity, and a long tail of smaller AMMs. This is the part most integrators skip past, and it's the part that actually determines whether your swap or arb bot performs.\n\n## The graph model, and why it's not a static shortest path\n\nModel it like this: every token mint is a node. Every pool that trades between two tokens is a directed edge. A naive router would run Dijkstra or Bellman-Ford over that graph and call it done. Jupiter can't, because an edge's "weight" (the price you get) isn't fixed — it's a function of how much you push through it. A constant-product pool with $3M in depth gives you a very different price at 10 SOL than at 5,000 SOL. So the routing problem is really: find the path or set of paths, and the allocation across them, that maximizes output for a specific trade size. That's a constrained optimization problem, not a graph traversal, even though the graph structure is what defines the candidate paths.\n\nJupiter's engine (the current iteration descends from what was internally called Metis) precomputes route candidates from indexed pool state, then scores them against the actual AMM curve — constant product, concentrated liquidity, or stable-swap, depending on the pool type — to estimate real output before ever touching the chain.\n\n## Why splitting a trade across pools beats a single hop\n\nHere's the part that's easy to state abstractly and easy to verify with real numbers. Say SOL is trading at $150 and you want to sell 1,000 SOL for USDC. Two pools exist:\n\n- Pool A (Orca): 20,000 SOL / 3,000,000 USDC\n- Pool B (Raydium): 15,000 SOL / 2,250,000 USDC\n\nRoute everything through Pool A alone, using the constant-product formula dy = y * dx / (x + dx):\n\n\ndy = 3,000,000 * 1000 / (20000 + 1000) = 142,857.14 USDC\n\n\nThat's an effective price of 142.86 USDC/SOL against a 150 spot — a 4.76% price impact on a single pool. Now split the order 500/500 across both pools:\n\n\nPool A: dy = 3,000,000 * 500 / 20500 = 73,170.73 USDC\nPool B: dy = 2,250,000 * 500 / 15500 = 72,580.65 USDC\nTotal = 145,751.38 USDC\n\n\nThat's roughly 2,894 USDC more — about 2% better — for the exact same input, just by not concentrating the whole order in one place. Jupiter's route plan output literally shows this as percentage splits (e.g., 54% via Orca, 46% via Raydium) in the routePlan array of a quote response. For anything above a few thousand dollars of notional, single-pool execution is leaving money on the table, and this is the whole reason an aggregator exists instead of everyone just hitting Raydium directly.\n\n## Multi-hop paths and intermediate tokens\n\nMost tokens don't have deep direct pools against every other token. A swap from a mid-cap SPL token to another mid-cap token usually routes through an intermediate — almost always USDC or SOL — because that's where the liquidity actually sits. So a route might be TOKEN_A → USDC → TOKEN_B, sometimes with a further split at each leg. This is where the restrictIntermediateTokens parameter on the Quote API matters: leaving it unset gives Jupiter freedom to route through whatever intermediate token produces the best price, but it also increases the chance of routing through a thin or freshly-deployed pool with real rug risk. For anything programmatic — a sniper or copy-trading flow — you generally want to constrain that set to majors you trust.\n\n## What changed with versioned transactions\n\nA three- or four-hop split route can touch a dozen or more accounts once you count pool vaults, oracle accounts, and program state. That blew past the 1,232-byte legacy transaction limit years ago, which is why Jupiter swap transactions are versioned transactions built against Address Lookup Tables (ALTs) — compressing 32-byte account keys down to 1-byte indices. If you're building your own transaction from a Jupiter quote rather than using their swap endpoint directly, you need to resolve and include the right ALTs or the transaction will fail account resolution, not fail gracefully. You also need to budget compute units correctly with setComputeUnitLimit; multi-hop routes are compute-hungry, and underestimating CU is a silent failure mode that looks like a routing bug but isn't.\n\n## Building on top of it: integration and arb bot notes\n\nA few things that matter in practice, not in the docs:\n\n- Quotes go stale fast. Pool state moves every slot. Fetch a quote, build the transaction, and submit within a tight window — anything more than a second or two on a volatile pair and your slippage tolerance is doing real work, not acting as a safety net.\n- onlyDirectRoutes is a real tool for latency-sensitive strategies. More hops means more accounts, more compute, and more surface area for one leg to fail mid-route. If you're running a sniper or arb bot where speed beats an extra 0.3% of price, force direct routes.\n- The public API rate-limits you. Anything running at production frequency — a live arbitrage bot or a sniper — needs either a paid tier or your own hosted instance of the routing logic against indexed pool state you control.

  • Landing the transaction is a separate problem from finding the price. A perfect route that lands three slots late because you used a flat priority fee during a congestion spike will fill at a worse price than a mediocre route that lands on time — this is covered in more depth in our comparison of Jito bundles versus priority fees.
  • Freshness of pool state is the actual bottleneck for competitive routing. If you're computing your own route scoring rather than trusting the public API's cache, you want direct account subscriptions rather than polling — see our breakdown of Yellowstone gRPC for how that pipeline is built.
  • Validator client choice affects how fast your transaction actually gets included, which compounds on top of routing quality — we go through the practical differences in Firedancer versus Agave.

For teams building infrastructure that needs low-latency access to this kind of pool and route data at scale rather than calling a public API, that's squarely infrastructure and data pipeline work — and it's also the layer that determines whether a market-making strategy can actually compete on inventory turnover.\n\nA quick example of what a raw quote call looks like against the v6 API:\n\nbash\ncurl \"https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=1000000000&slippageBps=50&onlyDirectRoutes=false\"\n\n\nThe response's routePlan array is where you see the actual split and hop structure Jupiter chose — worth logging on every trade if you're running anything automated, because it's the fastest way to tell whether your slippage settings and intermediate-token restrictions are actually helping or just adding friction.\n\nIf you're building an arbitrage or execution bot that needs to out-route the public aggregator rather than just call it, talk to us about the routing and execution layer."}

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#content#blog#solana#jupiter