Jupiter Legacy Swap v1/v6 Guide: Quote and Route Migration Notes
Understand legacy Jupiter v6 quote and route responses, then migrate new Solana integrations to the currently supported Jupiter Swap API.
Current API note (August 2026): Jupiter describes Metis Swap API as superseded by Swap V2. The historical v6 examples below explain the response model, but new integrations should start with the current official documentation at https://developers.jup.ag/.
Jupiter v6 Routing API is the practical entry point for any Solana bot that needs to swap tokens at the best available price across the fragmented DEX landscape. The upgrade from v4/v5 was not cosmetic — versioned transactions, dynamic slippage, and the platform fee hook all require explicit wiring if you want the aggregator to do what you actually expect in production.
What Changed in v6 and Why It Matters for Bots
The biggest structural change is versioned transactions (v0). Jupiter's Quote API now returns a routePlan and the Swap API returns a base64-encoded swapTransaction that is already a VersionedTransaction, not a legacy Transaction. Deserializing it with the old Transaction.from() will throw. You need VersionedTransaction.deserialize(Buffer.from(swapTransaction, 'base64')).
Two other changes affect execution quality:
- Dynamic slippage: v6 can compute a per-route slippage estimate from recent liquidity depth and set
slippageBpson your behalf if you passdynamicSlippage: { maxBps: 300 }. For bots trading illiquid pairs this is meaningfully better than a static 100 bps that either leaves money on the table or causes unnecessary reverts. - Platform fees: you can embed a fee account and take a cut of the output token. This is primarily useful for products charging users, but it also exposes a pattern worth knowing — Jupiter enforces the fee on-chain, so a misconfigured
feeAccountwill cause the swap instruction to fail silently on simulation.
Calling the Quote Endpoint
The Quote endpoint is a GET request to https://quote-api.jup.ag/v6/quote. The parameters that matter most for a bot:
inputMint, outputMint, amount (in lamports or smallest unit)
slippageBps | dynamicSlippage
onlyDirectRoutes=false // allow multi-hop
maxAccounts=64 // hard cap on total accounts; reduce if you're composing the swap into a larger tx
swapMode=ExactIn | ExactOut
The response includes routePlan — an array of swap steps, each with swapInfo containing the AMM label (Orca, Raydium CLMM, Meteora DLMM, etc.), the input/output amounts, and the fee taken by that pool. Parse this to understand where slippage is actually occurring. If a three-hop route passes through a thin Meteora pool in the middle leg, you will see it here before you commit.
Relevant fields to log in production:
priceImpactPct— anything above ~0.5% on a route you expect to be deep is a warning sign that liquidity shifted between quote and execution.otherAmountThreshold— this is the minimum output enforced on-chain. Confirm it matches your intent before signing.contextSlot— the Solana slot the quote was computed at. A quote that is more than ~10 slots old (roughly 4 seconds) in a fast-moving market should be re-fetched, not reused.
Calling the Swap Endpoint and Signing
The Swap endpoint is a POST to https://quote-api.jup.ag/v6/swap. You pass the full quoteResponse from the previous step plus your userPublicKey. Key optional fields:
{
"quoteResponse": { ... },
"userPublicKey": "...",
"wrapAndUnwrapSol": true,
"dynamicComputeUnitLimit": true,
"prioritizationFeeLamports": "auto"
}
dynamicComputeUnitLimit: true tells Jupiter to simulate the transaction and set the compute unit limit to the actual CU consumption plus a small buffer, rather than the default 200,000. This matters because an over-allocated CU limit raises your effective priority fee cost; an under-allocated one causes ComputeBudget exceeded failures. Passing "auto" for prioritizationFeeLamports uses Jupiter's fee oracle, which is fine for most bots — but if you are running a latency-sensitive strategy alongside Jito bundles, you will want to set this yourself and coordinate with your bundle tip.
Signing is two lines:
const swapTx = VersionedTransaction.deserialize(
Buffer.from(swapTransaction, 'base64')
);
swapTx.sign([wallet]);
Send via connection.sendRawTransaction(swapTx.serialize(), { skipPreflight: false, maxRetries: 2 }). Keep skipPreflight: false unless you have a specific reason; preflight simulation on a Solana RPC catches most failures before they consume priority fees.
Parsing the Route Plan Across Orca, Raydium, and Meteora
Jupiter routes through a heterogeneous set of AMMs, and not all of them behave the same way under stress. Understanding the routePlan lets you react intelligently:
- Orca Whirlpools (CLMM): concentrated liquidity, tighter spreads on major pairs, but price impact can cliff sharply at tick boundaries. If your route goes through Orca and
priceImpactPctis high, the pool is likely near a tick transition. - Raydium CLMM / CPMM: similar CLMM behavior on CLMM pools; the older CPMM pools (constant-product) are deeper for some mids but have predictable
k-curve impact you can estimate offline. - Meteora DLMM: dynamic liquidity bins mean spreads can compress or widen faster than CLMMs. Great for execution on stable pairs; less predictable on volatile mids.
For a bot that does significant daily volume — like a Solana MEV and arbitrage engine scanning these same pools — you want to parse the ammKey on each step of routePlan and maintain a local price cache per pool, so you can validate the quote against your own state before submitting.
Handling Failed Transactions Without Burning Compute Budget
The failure modes to handle explicitly:
Slippage exceeded: The swap instruction reverts with 0x1771 (slippage tolerance exceeded). This is not an error in your code; it is a market condition. The correct response is to re-quote — not retry the same transaction. Retrying a stale transaction repeatedly is the fastest way to build up wasted priority-fee spend.
Simulation failure before send: If sendRawTransaction returns a simulation error, parse the logs. Jupiter transactions typically include a Program log: Error: ... line that tells you exactly which AMM instruction failed. A missing account error almost always means a pool was closed or migrated between quote and swap.
Transaction timeout (not confirmed in ~30s): This usually means your priority fee was too low for the block's fee market. Increase prioritizationFeeLamports and re-quote. Do not rebroadcast the same signed transaction — resigning a fresh quote is cleaner and avoids nonce confusion.
A production bot should track a per-swap state machine: quoted → signed → submitted → confirmed | failed. Failed states should trigger re-quote with a backoff, not blind retries. For market-making strategies on Solana DEXes, where you are placing many small swaps continuously, a leaky-bucket limiter on retry attempts prevents runaway fee spend during periods of network congestion.
Composing Jupiter Swaps Into Larger Transactions
If you need to compose the Jupiter swap instruction with your own program instructions — for example, logging a trade to an on-chain program, or managing vault accounting — you can extract the instructions from the swapTransaction using TransactionMessage.decompile():
const msg = TransactionMessage.decompile(swapTx.message, {
addressLookupTableAccounts
});
// msg.instructions contains the compute budget + Jupiter swap instructions
// Prepend or append your own instructions here
The critical constraint is the 64-account limit on versioned transactions. Jupiter uses address lookup tables (ALTs) to compress multi-hop routes, but complex routes can still push you close to the limit. Pass maxAccounts=40 in the quote request to leave headroom for your additional instructions, and always check msg.accountKeys.length after decompile before signing.
Pulling the address lookup tables referenced in swapTransaction.message.addressTableLookups requires fetching each AddressLookupTableAccount from the cluster. Cache these aggressively — they change infrequently, and fetching them on every swap adds latency that compounds under load. The full Solana trading services catalog covers deeper infrastructure patterns for bots where this kind of latency discipline matters.
If you are building a production Solana bot that routes through Jupiter or any Solana DEX aggregator and want it done right the first time, reach out via the contact page — this is exactly the kind of system we build and run every day.
Building this for production?
We turn this architecture into tested, non-custodial software with monitoring, documentation and deployment support.
Related technical guides
Best Solana RPC for Trading Bots: Reproducible Latency Benchmark
Benchmark Helius, QuickNode, Triton and self-hosted Solana RPC endpoints with a reproducible Node.js harness for p50/p95/p99 latency, errors and slot freshness.
Read articleSanctum and LST Arbitrage: Trading Solana Liquid Staking Tokens
Build a Solana LST arbitrage bot around Sanctum: capture jitoSOL, mSOL and INF peg deviations against SOL via Infinity pool routes and unstake tickets.
Read articleJito Bundle Landing Reconciliation: From Submission ID to Confirmed Trades
Block Engine receipt proves submission, not atomic landing or economic success.
Read article