POST /v1/quote
Get a ranked swap route from the Venum router across all major Solana DEXes. Considers direct, 2-hop, and 3-hop routes through every tracked token, ranks them by a freshness-weighted score, and returns the top 5.
For trading bots watching a pair continuously, and for Starter/Pro swap UIs that can hold a second SSE connection, subscribe to GET /v1/quote/stream instead — the server recomputes at 2 Hz and pushes an event only when the output has moved past a threshold.
Request
POST /v1/quote
Content-Type: application/json
x-api-key: YOUR_API_KEYBody
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
inputMint | string | Yes | — | Base58 mint or token symbol (e.g. "SOL", "USDC") |
outputMint | string | Yes | — | Base58 mint or token symbol |
amount | string | Yes | — | Amount in smallest units (e.g. lamports) |
slippageBps | number | No | 100 | Slippage tolerance (1-5000 bps) |
Example
{
"inputMint": "SOL",
"outputMint": "USDC",
"amount": "1000000000",
"slippageBps": 100
}Routing
The router considers three classes of routes in parallel:
- Direct (1 hop) — every confirmed pool that contains both
inputMintandoutputMint. - 2-hop — every tracked token in
core/tokens.tsis tried as an intermediate (A → M → B). Top 5 freshest pools per leg. - 3-hop — every ordered pair of tracked tokens is tried as a chain (
A → M1 → M2 → B). Top 3 freshest pools per leg.
Freshness classification
Every candidate pool is classified into one of three states before it's allowed into the route search:
| Classification | Meaning |
|---|---|
observed | The bot has received an on-chain account update for this pool within the last 60 s — the cached state matches chain state. |
inferred | The bot has not observed this pool in the last 60 s, but it has observed another pool from the same DEX program within that window — the program is alive, so this pool's state is probably current. Used as a fallback when no observed routes exist. |
unobserved | Neither the pool nor any other pool from the same DEX program has been observed within the freshness window. Excluded entirely — the route-finder won't even score it. |
The cutoff (60 s) is the same one /v1/swap/build enforces. Quoting against state older than that returns garbage outputs and the resulting swap reverts.
Ranking
Routes are sorted into two confidence groups and scored within each group. An observed route always beats every inferred route, regardless of output amount — fresh state is more valuable than a marginally better stale quote because the swap actually lands at the price you were shown.
Within each confidence group, the score is freshness-weighted so a slightly older route only wins if its output is meaningfully better:
score = outputAmount × (1 - (cacheAgeMs / 60_000) × 0.05)A route at the 60-s cutoff gets a 5 % penalty; a 0-s route gets none. For multi-hop routes the cache age used is the max across hops (worst-link), and the route's confidence is the weakest across hops (an inferred hop demotes the whole route to inferred). The top 5 routes by score are returned.
Exact-CL re-quote validation
The route-finder's pre-filter pass uses a fast constant-product approximation to score every candidate. That approximation overestimates output for thin concentrated-liquidity pools where the price would walk through multiple ticks. Before returning a quote, the top candidates are re-quoted against the bot's on-chain-accurate tick-walking CL math (getExactMultiHopQuote) using the cached pool/tick state. Candidates whose validation fails (RPC hiccup, exhausted liquidity) are dropped, and the remaining set is re-sorted by their exact output before being returned. This is the same math the bot's arb agents execute against, so the quote you get is the quote the swap will land at — not a fictional best-case from the cheap pre-filter.
Response
200 OK — direct route
{
"routes": [
{
"dex": "orca-whirlpool",
"poolAddress": "HJPjoWUrhoZzkNfRpHuieeFk9AnbVjTk9Gc5SJRqsQTK",
"outputAmount": "134520000",
"priceImpactBps": 0,
"feeBps": 4,
"poolCacheAgeMs": 250,
"confidence": "observed"
}
],
"bestRoute": {
"dex": "orca-whirlpool",
"poolAddress": "HJPjoWUrhoZzkNfRpHuieeFk9AnbVjTk9Gc5SJRqsQTK",
"outputAmount": "134520000",
"priceImpactBps": 0,
"feeBps": 4,
"poolCacheAgeMs": 250,
"confidence": "observed"
},
"inputMint": "So11111111111111111111111111111111111111112",
"outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "1000000000",
"slippageBps": 100,
"poolsScanned": 41
}200 OK — 2-hop route
When there's no direct pool, or when a multi-hop wins on score, the route's hops field is populated. dex and poolAddress become aggregate fields (+-joined for dex, comma-joined for poolAddress):
{
"bestRoute": {
"dex": "orca-whirlpool+raydium-clmm",
"poolAddress": "5hCDEXRC...,B6LL9aCW...",
"outputAmount": "789540000",
"priceImpactBps": 0,
"feeBps": 6,
"poolCacheAgeMs": 530,
"confidence": "observed",
"hops": [
{
"dex": "orca-whirlpool",
"poolAddress": "5hCDEXRCn5YsyP1ydgbnaXT3QN6AAkfshfRkXRQypzr6",
"inputMint": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
"outputMint": "So11111111111111111111111111111111111111112",
"outputAmount": "9876543",
"feeBps": 1
},
{
"dex": "raydium-clmm",
"poolAddress": "B6LL9aCWVuo1tTcJoYvCTDqYrq1vjMfci8uHxsm4UxTR",
"inputMint": "So11111111111111111111111111111111111111112",
"outputMint": "HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3",
"outputAmount": "789540000",
"feeBps": 5
}
]
}
}A 3-hop route follows the same shape with three entries in hops.
Route Object
| Field | Type | Description |
|---|---|---|
dex | string | DEX identifier (orca-whirlpool) for direct, +-joined for multi-hop |
poolAddress | string | Pool address for direct, comma-joined for multi-hop |
outputAmount | string | Final expected output in smallest units |
priceImpactBps | number | Estimated price impact in basis points (currently always 0) |
feeBps | number | Pool fee in basis points; for multi-hop this is the SUM across all hops |
poolCacheAgeMs | number | Pool state freshness in ms; for multi-hop this is the WORST (max) across hops |
confidence | "observed" | "inferred" | Whether the route's pools were directly observed within the 60 s freshness window (observed) or are being trusted via DEX-program liveness fallback (inferred). For multi-hop, equals the weakest hop's confidence. dApps should surface this as a badge — inferred quotes are usable but slightly riskier. |
hops | PoolHop[] | undefined | Ordered hops; undefined for direct routes, populated for multi-hop |
PoolHop Object
| Field | Type | Description |
|---|---|---|
dex | string | DEX identifier for this hop |
poolAddress | string | Base58 pool address for this hop |
inputMint | string | Input mint for this hop |
outputMint | string | Output mint for this hop |
outputAmount | string | Hop output in smallest units (becomes the next hop's input) |
feeBps | number | This hop's pool fee in basis points |
Errors
| Status | Description |
|---|---|
400 | Invalid request body |
401 | Missing or invalid API key |
404 | No valid quotes available across direct, 2-hop, or 3-hop routes |
429 | Rate limit exceeded |
Rate Limit
| Tier | Limit |
|---|---|
| free | 10 / min |
| starter | 60 / min |
| pro | 300 / min |
