Quick Start
Get your first swap route in under a minute. This example swaps 1 ETH for USDC on Base.
This example swaps 1 ETH for USDC on Base. Replace the addresses and chain ID for any other token pair on any supported chain.
Step 1 · Request a route
# Swap 1 WETH → USDC on Base (chain 8453)
# To swap native ETH directly, use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE as src
curl 'https://api.nordstern.finance/aggregator/8453?src=0x4200000000000000000000000000000000000006&dst=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&amount=1000000000000000000&from=0xYourWalletAddress&slippage=0.5' \
-H "Referer: https://yourdomain.com"// Swap 1 WETH → USDC on Base (chain 8453)
// To swap native ETH directly, use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE as src
const params = new URLSearchParams({
src: "0x4200000000000000000000000000000000000006", // WETH (or 0xEeee... for native ETH)
dst: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC
amount: "1000000000000000000", // 1 ETH in wei
from: "0xYourWalletAddress",
slippage: "0.5",
});
const response = await fetch(
`https://api.nordstern.finance/aggregator/8453?${params}`,
{ headers: { "Referer": "https://yourdomain.com" } }
);
const route = await response.json();import requests
# Swap 1 WETH → USDC on Base (chain 8453)
# To swap native ETH directly, use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE as src
url = "https://api.nordstern.finance/aggregator/8453"
params = {
"src": "0x4200000000000000000000000000000000000006", # WETH (or 0xEeee... for native ETH)
"dst": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", # USDC
"amount": "1000000000000000000", # 1 ETH in wei
"from": "0xYourWalletAddress",
"slippage": 0.5,
}
route = requests.get(url, params=params, headers={"Referer": "https://yourdomain.com"}).json()Step 2 · Read the response
The response contains the optimal route and a fully encoded tx object ready to pass to your wallet
or provider. Before proceeding, verify that tx is present and toAmount is non-zero. If either
check fails, no valid route is available.
{
"src": "0x4200000000000000000000000000000000000006",
"dst": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"fromAmount": "1000000000000000000",
"toAmount": "2120362157",
"gasEstimate": "1045694",
"tx": {
"from": "0xYourWalletAddress",
"to": "0xC87De04e2EC1F4282dFF2933A2D58199f688fC3d",
"data": "0x3f0bde25...",
"value": "0"
}
}Step 3 · Approve and execute
Before the first swap of an ERC-20 token, approve the Guard Contract (tx.to) to spend your input
token. Once granted, you don't need to repeat it for the same token. Native token swaps
(0xEeee...EEeE) skip this step — the value is passed via tx.value. Then submit tx directly.
import { createPublicClient, createWalletClient, http, parseAbi, maxUint256 } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0xYourPrivateKey");
const transport = http("https://your-rpc-url");
const publicClient = createPublicClient({ chain: base, transport });
const walletClient = createWalletClient({ account, chain: base, transport });
// Guard: no valid route
if (!route.tx || route.toAmount === "0") {
throw new Error("No valid route available");
}
const NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
const isNative = route.src.toLowerCase() === NATIVE.toLowerCase();
// Native input: value is set on tx, no ERC-20 approval needed
if (!isNative) {
const ERC20_ABI = parseAbi([
"function allowance(address,address) view returns (uint256)",
"function approve(address,uint256) returns (bool)",
]);
// 1. Check allowance on the input token
const allowance = await publicClient.readContract({
address: route.src,
abi: ERC20_ABI,
functionName: "allowance",
args: [account.address, route.tx.to],
});
// 2. Approve the router if needed (max approval to avoid repeating)
if (allowance < BigInt(route.fromAmount)) {
await walletClient.writeContract({
address: route.src,
abi: ERC20_ABI,
functionName: "approve",
args: [route.tx.to, maxUint256],
});
}
}
// 3. Submit the swap
const hash = await walletClient.sendTransaction({
to: route.tx.to,
data: route.tx.data,
value: BigInt(route.tx.value),
});import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://your-rpc-url");
const wallet = new ethers.Wallet("0xYourPrivateKey", provider);
// Guard: no valid route
if (!route.tx || route.toAmount === "0") {
throw new Error("No valid route available");
}
const NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
const isNative = route.src.toLowerCase() === NATIVE.toLowerCase();
// Native input: value is set on tx, no ERC-20 approval needed
if (!isNative) {
const ERC20_ABI = [
"function allowance(address owner, address spender) view returns (uint256)",
"function approve(address spender, uint256 amount) returns (bool)",
];
const token = new ethers.Contract(route.src, ERC20_ABI, wallet);
// 1. Check allowance on the input token
const allowance = await token.allowance(wallet.address, route.tx.to);
// 2. Approve the router if needed (max approval to avoid repeating)
if (allowance < BigInt(route.fromAmount)) {
await token.approve(route.tx.to, ethers.MaxUint256);
}
}
// 3. Submit the swap
const tx = await wallet.sendTransaction({
to: route.tx.to,
data: route.tx.data,
value: BigInt(route.tx.value),
});from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://your-rpc-url"))
account = w3.eth.account.from_key("0xYourPrivateKey")
# Guard: no valid route
if "tx" not in route or route["toAmount"] == "0":
raise ValueError("No valid route available")
NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
is_native = route["src"].lower() == NATIVE.lower()
# Native input: value is set on tx, no ERC-20 approval needed
if not is_native:
ERC20_ABI = [
{"name": "allowance", "type": "function", "stateMutability": "view",
"inputs": [{"name": "owner", "type": "address"},
{"name": "spender", "type": "address"}],
"outputs": [{"name": "", "type": "uint256"}]},
{"name": "approve", "type": "function", "stateMutability": "nonpayable",
"inputs": [{"name": "spender", "type": "address"},
{"name": "amount", "type": "uint256"}],
"outputs": [{"name": "", "type": "bool"}]},
]
src_token = w3.eth.contract(address=route["src"], abi=ERC20_ABI)
router_address = route["tx"]["to"]
from_amount = int(route["fromAmount"])
# 1. Check allowance on the input token
allowance = src_token.functions.allowance(account.address, router_address).call()
# 2. Approve the router if needed (max approval to avoid repeating)
if allowance < from_amount:
approve_tx = src_token.functions.approve(router_address, 2**256 - 1).build_transaction({
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
})
signed = account.sign_transaction(approve_tx)
w3.eth.send_raw_transaction(signed.raw_transaction)
# 3. Submit the swap
swap_tx = {
"from": account.address,
"to": route["tx"]["to"],
"data": route["tx"]["data"],
"value": int(route["tx"]["value"]),
"nonce": w3.eth.get_transaction_count(account.address),
"gas": int(int(route["gasEstimate"]) * 1.2), # add 20% buffer
}
signed_swap = account.sign_transaction(swap_tx)
tx_hash = w3.eth.send_raw_transaction(signed_swap.raw_transaction)What's next
- Get Swap Route — full parameter reference and response schema.
- Get Token Price — fetch USD prices for multiple tokens in one call.
- Get Token Liquidity — check on-chain depth before routing large swaps.
- Errors — how the API signals bad requests and missing routes.
- Supported Chains — chain IDs and Guard Contract addresses.