Skip to main content

For Developers

Complete, copy-pasteable examples for integrating token swaps. Each example fetches a swap quote and submits the transaction on-chain.

Base URL: https://api.swapapi.dev


TypeScript (viem)

import { createWalletClient, http, parseEther } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const wallet = createWalletClient({
account,
chain: base,
transport: http(),
});

// Step 1: Get swap quote
const params = new URLSearchParams({
tokenIn: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // Native ETH
tokenOut: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
amount: parseEther("1").toString(), // 1 ETH in wei
sender: account.address,
});

const res = await fetch(
`https://api.swapapi.dev/v1/swap/8453?${params}`
);
const { success, data, error } = await res.json();

if (!success) {
throw new Error(`Swap failed: ${error.code}${error.message}`);
}

console.log(`Swapping 1 ETH for ~${Number(data.expectedAmountOut) / 1e6} USDC`);
console.log(`Price impact: ${(data.priceImpact * 100).toFixed(4)}%`);

// Step 2: Submit the transaction
const hash = await wallet.sendTransaction({
to: data.tx.to as `0x${string}`,
data: data.tx.data as `0x${string}`,
value: BigInt(data.tx.value),
gas: BigInt(data.tx.gas),
});

console.log(`Transaction sent: ${hash}`);

Python (web3.py)

import requests
from web3 import Web3

w3 = Web3(Web3.HTTPProvider("https://mainnet.base.org"))
account = w3.eth.account.from_key("0xYOUR_PRIVATE_KEY")

# Step 1: Get swap quote
response = requests.get(
"https://api.swapapi.dev/v1/swap/8453",
params={
"tokenIn": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"tokenOut": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": str(10**18), # 1 ETH in wei
"sender": account.address,
},
)
result = response.json()

if not result["success"]:
raise Exception(f"Swap failed: {result['error']['message']}")

data = result["data"]
print(f"Expected output: {int(data['expectedAmountOut']) / 1e6:.2f} USDC")

# Step 2: Submit the transaction
tx = {
"from": account.address,
"to": Web3.to_checksum_address(data["tx"]["to"]),
"data": data["tx"]["data"],
"value": int(data["tx"]["value"]),
"gas": int(data["tx"]["gas"]),
"nonce": w3.eth.get_transaction_count(account.address),
"chainId": 8453,
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"Transaction sent: {tx_hash.hex()}")

receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Confirmed in block {receipt['blockNumber']}")

Rust (reqwest + alloy)

use alloy::{
network::EthereumWallet,
primitives::{Address, U256},
providers::ProviderBuilder,
signers::local::PrivateKeySigner,
};
use serde::Deserialize;

#[derive(Deserialize)]
struct ApiResponse {
success: bool,
data: Option<SwapData>,
error: Option<ApiError>,
}

#[derive(Deserialize)]
struct SwapData {
#[serde(rename = "expectedAmountOut")]
expected_amount_out: String,
tx: TxData,
}

#[derive(Deserialize)]
struct TxData {
to: Address,
data: String,
value: String,
gas: String,
}

#[derive(Deserialize)]
struct ApiError {
code: String,
message: String,
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
let signer: PrivateKeySigner = "0xYOUR_PRIVATE_KEY".parse()?;
let wallet = EthereumWallet::from(signer.clone());

let provider = ProviderBuilder::new()
.wallet(wallet)
.on_http("https://mainnet.base.org".parse()?);

// Step 1: Get swap quote
let client = reqwest::Client::new();
let resp: ApiResponse = client
.get("https://api.swapapi.dev/v1/swap/8453")
.query(&[
("tokenIn", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"),
("tokenOut", "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"),
("amount", "1000000000000000000"),
("sender", &signer.address().to_string()),
])
.send()
.await?
.json()
.await?;

if !resp.success {
let err = resp.error.unwrap();
eyre::bail!("Swap failed: {} — {}", err.code, err.message);
}

let data = resp.data.unwrap();
println!("Expected output: {} raw units", data.expected_amount_out);

// Step 2: Submit the transaction
let tx = alloy::rpc::types::TransactionRequest::default()
.to(data.tx.to)
.input(data.tx.data.parse::<alloy::primitives::Bytes>()?.into())
.value(U256::from_str_radix(data.tx.value.trim_start_matches("0x"), 10)?)
.gas_limit(data.tx.gas.parse::<u64>()?);

let pending = provider.send_transaction(tx).await?;
println!("Transaction sent: {:?}", pending.tx_hash());

let receipt = pending.get_receipt().await?;
println!("Confirmed in block {:?}", receipt.block_number);

Ok(())
}

Cargo.toml dependencies:

[dependencies]
alloy = { version = "0.12", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
eyre = "0.6"

ERC-20 approval (when not swapping native ETH)

When swapping an ERC-20 token (not native ETH), the sender must first approve the router contract to spend the input token. The router address is returned in data.tx.to.

TypeScript (viem) approval example

import { erc20Abi, parseUnits } from "viem";

// Approve the router to spend your USDC
const approveHash = await wallet.writeContract({
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC
abi: erc20Abi,
functionName: "approve",
args: [
data.tx.to as `0x${string}`, // Router contract from swap response
parseUnits("100", 6), // Amount to approve
],
});

// Wait for approval, then submit the swap
await publicClient.waitForTransactionReceipt({ hash: approveHash });
const swapHash = await wallet.sendTransaction({ /* ... swap tx ... */ });

Quick reference

Endpoint: GET https://api.swapapi.dev/v1/swap/&#123;chainId&#125;
Required: tokenIn, tokenOut, amount, sender
Auth: None
Rate limit: 60 req/min per IP