Overview
By default, the Mobula Swap API automatically finds the best route across all available liquidity sources. However, you may want to target a specific pool or restrict routing to certain protocols — for example, to:
- Execute a swap directly through a specific Aerodrome CL pool on Base
- Restrict routing to only Uniswap V3 pools
- Avoid certain factory contracts
- Force routing through a specific DEX for compliance or analytics
The Swap API provides three parameters for pool targeting:
| Parameter | Routing | Description |
|---|
poolAddress | Direct on-chain | Target a specific pool by address — bypasses aggregators, executes directly via MobulaRouter |
onlyProtocols | Via aggregator | Restrict to specific pool types (e.g., uniswap-v3) — routed through KyberSwap/Jupiter |
excludedProtocols | Via aggregator | Exclude specific factory addresses from routing |
Key difference: poolAddress routes the swap directly on-chain through MobulaRouter’s executeRoute function. onlyProtocols passes the restriction to an external aggregator (KyberSwap, Jupiter) which handles the routing.
Direct Pool Access (poolAddress)
Use poolAddress when you know the exact pool contract address and want to swap directly through it, without going through an external aggregator. The swap is executed on-chain via MobulaRouter’s internal routing engine.
Basic Example
Route a USDC → WETH swap directly through a specific Aerodrome CL pool on Base:
curl -X GET "https://api.mobula.io/api/2/swap/quoting?\
chainId=evm:8453\
&tokenIn=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\
&tokenOut=0x4200000000000000000000000000000000000006\
&amount=100\
&walletAddress=0xYourWallet\
&slippage=1\
&poolAddress=0xb4cb800910b228ed3d0834cf79d697127bbb00e5"
Concrete Example — USDC to TKFG on Aerodrome CL (Base)
This example swaps ~300 USDC (raw units) to TKFG on Base, routed directly through the Aerodrome CL pool:
curl -X GET "https://api.mobula.io/api/2/swap/quoting?\
chainId=evm%3A8453\
&tokenIn=0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\
&tokenOut=0xe9d139df6212fde346ec650e83039f322018f60d\
&amountRaw=300539874\
&walletAddress=0xYourWallet\
&slippage=1\
&poolAddress=0x66fbdb0b14a889b3735da163843289a55d28bb97"
The response transaction will use MobulaRouter’s executeRoute selector (0xa564dfa4), executing the swap directly on-chain without any aggregator intermediary.
You can discover pool addresses using the Token Markets endpoint, which returns all pools for a given token pair along with their addresses, types, and liquidity.
When to Use poolAddress
- You want zero aggregator fees — the swap goes directly through the pool
- You need deterministic routing — always the exact same pool, no fallback
- You’re building a pool-specific UI (e.g., showing swap for a specific market)
- Multiple pools exist for the same pair (e.g., different fee tiers) and you want a specific one
Filtering by Protocol (onlyProtocols)
Use onlyProtocols to restrict the swap to pools of a specific type. The swap is routed through an external aggregator (KyberSwap on EVM, Jupiter on Solana) which applies the filter.
Single Protocol
Route a swap exclusively through Uniswap V3 pools:
curl -X GET "https://api.mobula.io/api/2/swap/quoting?\
chainId=evm:1\
&tokenIn=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\
&tokenOut=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\
&amount=0.5\
&walletAddress=0xYourWallet\
&slippage=1\
&onlyProtocols=uniswap-v3"
Multiple Protocols
Route through either Aerodrome CL or Uniswap V3 pools on Base:
curl -X GET "https://api.mobula.io/api/2/swap/quoting?\
chainId=evm:8453\
&tokenIn=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\
&tokenOut=0x4200000000000000000000000000000000000006\
&amount=100\
&walletAddress=0xYourWallet\
&slippage=1\
&onlyProtocols=aerodrome-cl-2,uniswap-v3"
Excluding Protocols (excludedProtocols)
Use excludedProtocols to exclude specific factory addresses from routing. Unlike onlyProtocols which takes pool type names, this parameter takes factory contract addresses:
curl -X GET "https://api.mobula.io/api/2/swap/quoting?\
chainId=evm:1\
&tokenIn=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\
&tokenOut=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\
&amount=1\
&walletAddress=0xYourWallet\
&slippage=1\
&excludedProtocols=0x1F98431c8aD98523631AE4a59f267346ea31F984"
Common Pool Types
EVM Chains
| Pool Type | Description |
|---|
uniswap-v2 | Uniswap V2 and forks (SushiSwap, PancakeSwap V2, etc.) |
uniswap-v3 | Uniswap V3 concentrated liquidity pools |
uniswap-v4 | Uniswap V4 pools with hooks |
aerodrome | Aerodrome V2 pools (Base) |
aerodrome-cl-2 | Aerodrome CL (concentrated liquidity) pools (Base) |
pancakeswap-v3 | PancakeSwap V3 pools |
solidly-v2 | Solidly V2 pools and forks |
solidly-v3 | Solidly V3 pools (CL) |
trader-joe-v2.1 | Trader Joe Liquidity Book pools |
maverick-v2 | Maverick V2 pools |
Solana
| Pool Type | Description |
|---|
raydium-amm | Raydium AMM pools |
raydium-clmm | Raydium concentrated liquidity pools |
raydium-cpmm | Raydium constant product pools |
orca-whirlpool | Orca Whirlpool concentrated liquidity pools |
meteora-dlmm | Meteora DLMM pools |
pumpfun | PumpFun bonding curve pools |
pumpswap | PumpSwap pools |
Only tradable pool types are accepted by onlyProtocols. Non-tradable types are automatically filtered out. If no valid pool types remain after filtering, the API returns a “No route found” error.
Choosing a Router (onlyRouters)
You can also restrict which aggregator/router handles the swap, independently from pool targeting:
| Router | Chains | Description |
|---|
kyberswap | EVM | KyberSwap aggregator (no extra fees) |
lifi | EVM | LI.FI cross-chain aggregator |
jupiter | Solana | Jupiter aggregator |
curl -X GET "https://api.mobula.io/api/2/swap/quoting?\
chainId=evm:8453\
&tokenIn=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\
&tokenOut=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\
&amount=0.01\
&walletAddress=0xYourWallet\
&slippage=1\
&onlyRouters=kyberswap"
TypeScript Example
// Direct pool access — swap through a specific pool on-chain
async function swapViaSpecificPool() {
const params = new URLSearchParams({
chainId: 'evm:8453',
tokenIn: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
tokenOut: '0x4200000000000000000000000000000000000006', // WETH
amount: '100', // 100 USDC
walletAddress: '0xYourWallet',
slippage: '1',
poolAddress: '0xb4cb800910b228ed3d0834cf79d697127bbb00e5', // Specific pool
});
const response = await fetch(
`https://api.mobula.io/api/2/swap/quoting?${params}`,
{ headers: { Authorization: 'YOUR_API_KEY' } }
);
const { data } = await response.json();
// Transaction uses MobulaRouter's executeRoute (direct on-chain)
console.log('Route:', data.route);
console.log('Estimated output:', data.estimatedAmountOut);
console.log('Transaction:', data.evm?.transaction);
}
// Protocol-filtered — restrict aggregator to specific pool types
async function swapWithProtocolFilter() {
const params = new URLSearchParams({
chainId: 'evm:8453',
tokenIn: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
tokenOut: '0x4200000000000000000000000000000000000006',
amount: '100',
walletAddress: '0xYourWallet',
slippage: '1',
onlyProtocols: 'aerodrome-cl-2', // Only Aerodrome CL pools
});
const response = await fetch(
`https://api.mobula.io/api/2/swap/quoting?${params}`,
{ headers: { Authorization: 'YOUR_API_KEY' } }
);
const { data } = await response.json();
// Transaction uses aggregator (KyberSwap) with protocol filter
console.log('Route:', data.route);
console.log('Estimated output:', data.estimatedAmountOut);
console.log('Transaction:', data.evm?.transaction);
}
Troubleshooting
| Issue | Cause | Solution |
|---|
"No route found" | No pool of the requested type exists for this pair | Check the pool type name is correct, or try without onlyProtocols |
"Pool not found" | The poolAddress doesn’t exist in Mobula’s index | Verify the pool address on a block explorer |
| Swap uses aggregator instead of direct access | Used onlyProtocols instead of poolAddress | Use poolAddress for direct on-chain routing |
"Invalid protocol" | Typo in the pool type name | Check the Common Pool Types table above |