DEX swap volume crossed $250 billion per month in early 2026, and more of that volume is being routed programmatically through aggregator APIs. But integrating a swap API is not the same as calling a REST endpoint that returns JSON. The response contains raw transaction calldata destined for an on-chain router contract, and every field matters. Mishandle one and your transaction reverts, your user loses gas, or worse — they get a terrible price.
These 7 mistakes show up repeatedly in production codebases. Each one has caused real on-chain losses, and each one is straightforward to fix.
1. Not Checking priceImpact Before Executing
The swap API returns a priceImpact field as a decimal. A value of -0.0012 means -0.12% — a normal cost of trading. A value of -0.15 means -15% — your user is about to lose 15% of their trade to thin liquidity.
Chainalysis estimated that price impact losses exceeded $900 million across DeFi in 2025, largely from automated systems executing swaps without checking this field.
const res = await fetch("https://api.swapapi.dev/v1/swap/1?tokenIn=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&tokenOut=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&amount=1000000000000000000&sender=0xYourAddress");
const { data } = await res.json();
if (data.priceImpact < -0.05) throw new Error("Price impact too high");
Set a threshold. For most applications, rejecting swaps with priceImpact worse than -5% is a reasonable default. For large trades, tighten it to -1%.
2. Ignoring the Partial Status
A swap API returning HTTP 200 does not mean you got a full fill. The response can have three status values: Successful, Partial, and NoRoute. All three return HTTP 200.
A Partial status means only part of your requested amount can be filled. The amountIn and expectedAmountOut in the response reflect the partial amount, not what you originally asked for. If you blindly execute without checking, you swap less than intended and may leave your application in an inconsistent state.
if (data.status === "NoRoute") throw new Error("No route found");
if (data.status === "Partial") {
const filled = BigInt(data.amountIn);
const requested = BigInt("1000000000000000000");
console.log(`Partial fill: ${(filled * 100n) / requested}%`);
}
When you get a Partial response, you have three options: execute the partial swap as-is, reduce your amount to match and re-request, or try a different token pair. Do not retry the same amount in a loop — the liquidity cap will not change.
3. Hardcoding Gas Limits
Etherscan data shows that roughly 12% of failed transactions on Ethereum in 2025 ran out of gas. Multi-hop swap routes use significantly more gas than simple transfers. A direct ETH-to-USDC swap might use 150,000 gas, while a three-hop route through intermediate pools can exceed 500,000.
The swap API response does not include a gasLimit field — intentionally. Gas requirements vary by route complexity, network congestion, and contract state. You must estimate it yourself.
const gasEstimate = await provider.estimateGas(data.tx);
const gasLimit = (gasEstimate * 120n) / 100n;
Always call eth_estimateGas with the transaction object and add a 20% buffer. If eth_estimateGas fails, the swap would revert on-chain. Do not submit the transaction.
4. Skipping eth_estimateGas and eth_call Pre-flight
This is related to the gas mistake above but goes further. Many developers skip pre-flight simulation entirely and submit the transaction directly. When it reverts, the user pays gas for nothing.
A 2025 Blocknative study found that 8.4% of DEX transactions on Ethereum reverted, costing users over $42 million in wasted gas fees in a single quarter. Most of these reverts were detectable before submission.
The fix is a two-step simulation using eth_estimateGas and eth_call:
const estimate = await provider.estimateGas(data.tx);
const result = await provider.call(data.tx);
eth_estimateGas tells you whether the transaction will run out of gas. eth_call simulates the full execution and catches reverts from insufficient balance, missing approvals, or expired deadlines. Both calls are free — they consume no gas. If either fails, do not submit.
5. Not Handling ERC-20 Approvals
When swapping native ETH, no approval is needed — the value is sent directly with the transaction. But when swapping an ERC-20 token, the sender must first approve the router contract to spend the input tokens. Skip this step and the transaction reverts immediately.
The router address to approve is data.tx.to from the swap response. The amount must be at least amountIn.
const tokenContract = new ethers.Contract(tokenIn, ["function approve(address,uint256)"], signer);
await tokenContract.approve(data.tx.to, data.amountIn);
Two critical details developers miss. First, USDT on Ethereum requires setting the allowance to 0 before setting a new non-zero allowance — a quirk of its contract implementation. Second, you must wait for the approval transaction to be confirmed before fetching a fresh swap quote. The calldata is time-sensitive (30 seconds), so reuse of a stale quote after a slow approval confirmation will cause a revert.
6. Using Stale Calldata Past the Deadline
Swap calldata includes an on-chain deadline, typically 30 seconds from the time the quote was generated. After that window, the router contract rejects the transaction. This is a safety mechanism — prices shift, and executing a stale quote could mean a worse fill.
Research from Flashbots showed that the median Ethereum block time is 12 seconds, meaning a 30-second deadline gives you roughly 2-3 blocks. In practice, if your application fetches a quote, waits for user confirmation in a UI, then submits, you can easily exceed this window.
The fix is simple: fetch the quote as late as possible in your flow.
const freshQuote = await fetch(`https://api.swapapi.dev/v1/swap/1?tokenIn=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&tokenOut=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&amount=1000000000000000000&sender=0xYourAddress`);
const { data } = await freshQuote.json();
await signer.sendTransaction(data.tx);
If you have a multi-step flow (approval, then swap), fetch a new quote after the approval confirms rather than reusing the original. For automated systems, always re-fetch immediately before submission.
7. Using Wrong Token Decimals for Amount Calculation
This is the most common mistake for developers new to DeFi. Different tokens use different decimal precision. ETH uses 18 decimals, USDC uses 6 on Ethereum, and USDT uses 18 on BSC but 6 on Ethereum. Getting this wrong means you swap 1,000,000x more or less than intended.
The amount parameter in the API expects the raw amount in the token's smallest unit. To swap 1 ETH, you pass 1000000000000000000 (1 * 10^18). To swap 100 USDC on Ethereum, you pass 100000000 (100 * 10^6).
const decimals = 6;
const humanAmount = 100;
const rawAmount = BigInt(humanAmount) * 10n ** BigInt(decimals);
The swap API response includes tokenFrom.decimals and tokenTo.decimals, so you can always verify. Use these response fields to convert expectedAmountOut back to a human-readable number: divide by 10^decimals.
Mistake vs. Fix: Quick Reference
| Mistake | What Goes Wrong | Fix |
|---|---|---|
| Ignoring priceImpact | User loses 10%+ on thin liquidity | Reject if priceImpact < -0.05 |
| Not checking Partial status | Swap less than intended | Check data.status before executing |
| Hardcoding gas limits | Out-of-gas revert, user pays gas for nothing | eth_estimateGas + 20% buffer |
| Skipping pre-flight simulation | Revert on-chain, wasted gas | eth_estimateGas + eth_call before submit |
| Missing ERC-20 approval | Immediate revert | Approve data.tx.to for amountIn first |
| Stale calldata | Deadline expired, transaction rejected | Re-fetch quote within 30s of submission |
| Wrong decimals | Swap wrong amount by orders of magnitude | Use token's decimals field for conversion |
FAQ
What slippage should I set for DEX swaps?
The default 0.5% (maxSlippage=0.005) works for most liquid pairs. For low-liquidity or fee-on-transfer tokens, increase to 1-3%. The minAmountOut in the response is calculated as expectedAmountOut * (1 - maxSlippage) — the transaction reverts if the actual output falls below this.
How do I know if a token needs approval?
If tokenIn is the native gas token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), no approval is needed. For any ERC-20 token, check the current allowance with allowance(sender, data.tx.to) on the token contract. If it is less than amountIn, an approval transaction is required.
What do I do when the API returns NoRoute?
NoRoute means no swap path exists for this token pair at the requested amount. Try a smaller amount, a different token pair, or check if the token contract address is correct. Some tokens have very limited liquidity on certain chains.
How do I handle USDT approvals on Ethereum?
USDT's contract requires setting the allowance to 0 before setting a new non-zero value. Call approve(spender, 0) first, wait for confirmation, then call approve(spender, amount).
Get Started
Swap API is free, requires no API key, and supports 46 EVM chains. A single GET request returns executable swap calldata with price impact, slippage protection, and recommended RPCs included.
curl "https://api.swapapi.dev/v1/swap/1?tokenIn=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&tokenOut=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&amount=1000000000000000000&sender=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
Read the full API documentation or explore the OpenAPI spec.
Top comments (0)