Get started

View as Markdown

Quick link with examples: 0x-examples

About 0x Cross Chain API

The 0x Cross Chain API is a fast, flexible API that lets developers programmatically access tokens across various blockchains, enabling cross-chain swap functionality in any app or wallet.

Whether you’re building a new application, or expanding the offering of your existing product, this API helps you integrate cross-chain token swaps with minimal overhead and maximum efficiency.

Steps to Cross Chain Swap Tokens

This guide will walk you through using the /quotes and /status endpoints on 0x’s Cross Chain API to:

  1. Fetch a quote
  2. Set allowance
  3. Sign and send a transaction
  4. Monitor the status of the cross chain swap

In our example, we will be swapping WETH on Base to USDC on Arbitrum.

0. Prerequisites

Make sure you have:

  • A funded Base wallet
  • 0x API key
  • An RPC Connection (see details below)

Base provides a default public RPC endpoint, but for production use, it’s strongly recommended to run your own or use a third-party provider like QuickNode or Alchemy.

To interact with that RPC, we will use a popular TS library, viem.

import { createWalletClient, http } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const PRIVATE_KEY = 'your_private_key'
const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient(
{
account,
chain: base,
transport: http(configuration.rpcUrls.base),
}
)

1. Fetch a Quote

Start by sending a GET request to the 0x /quotes endpoint to get a quote for a specific tokens and chains pair with selected amount.

const quotesParams = new URLSearchParams({
originChain: '8453', // Base mainnet
destinationChain: '42161', // Arbitrum mainnet
sellToken: '0x4200000000000000000000000000000000000006', // WETH on Base
buyToken: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', // USDC on Arbitrum
sellAmount: '1000000000000000000', // Amount of sellToken in base units
originAddress: '$USER_TAKER_ADDRESS', // Address that will make the trade
sortQuotesBy: 'price', // Prefer the quote that will result in the best price / output
maxNumQuotes: 1 // only the best quote
});
const headers = {
'0x-api-key': '[api-key]', // Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps)
};
const quoteResponse = await fetch('https://api.0x.org/cross-chain/quotes?' + quotesParams.toString(), { headers });
console.log(await quoteResponse.json());
{
"liquidityAvailable": true,
"allowanceTarget": "0x0000000000001ff3684f28c67538d4d072c22734",
"originChainId": 8453,
"originChain": "base",
"destinationChainId": 42161,
"destinationChain": "arbitrum",
"sellToken": "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf",
"buyToken": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
"issues": {
"allowance": {
"actual": "0",
"spender": "0x0000000000001ff3684f28c67538d4d072c22734"
},
"balance": {
"token": "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf",
"actual": "0",
"expected": "2570"
},
"simulationIncomplete": false,
"invalidSwapSourcesPassed": [],
"invalidBridgesPassed": []
},
"zid": "0x0d2804bc7404160d778f0f7f",
"quotes": [
{
"sellAmount": "2570",
"buyAmount": "2016894",
"minBuyAmount": "1996725",
"fees": {
"integratorFee": null,
"integratorFees": null,
"zeroExFee": {
"amount": "24",
"token": "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf",
"type": "volume"
},
"bridgeNativeFee": null
},
"gasCosts": {
"chainType": "evm",
"gasPrice": "7610802",
"gasLimit": "151386",
"totalNetworkFee": "1252356913486"
},
"steps": [
{
"type": "bridge",
"originChainId": 8453,
"destinationChainId": 42161,
"sellToken": "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf",
"buyToken": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
"sellAmount": "2546",
"buyAmount": "2016894",
"minBuyAmount": "1996725",
"provider": "relay",
"estimatedTimeSeconds": 1
}
],
"transaction": {
"chainType": "evm",
"details": {
"to": "0x0000000000001ff3684f28c67538d4d072c22734",
"data": "0x2213bc0b...",
"gas": "151386",
"gasPrice": "7610802",
"value": "0"
}
},
"estimatedTimeSeconds": 1,
"issues": {
"allowance": {
"actual": "0",
"spender": "0x0000000000001ff3684f28c67538d4d072c22734"
},
"balance": {
"token": "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf",
"actual": "0",
"expected": "2570"
},
"simulationIncomplete": false
},
"quoteId": "0x0d2804bc7404160d778f0f7f4ec037a2"
}
]
}

2. Set a token allowance

It is possible, that you will need to set proper token allowance to 0x’s AllowanceHolder contract. In cases where allowance will be needed, it will be reported in issues in the response and will be the same as the allowance target for Swap API (when using AllowanceHolder flow). It may differ across chains, so always use the value from the response rather than hardcoding an address.

Below you can find an example on how to set proper token allowance for received quote.

// Example assumes that you've set up your walletClient with viem
import { erc20Abi } from "viem";
...
// Code that fetches a valid quotesResponse
...
const approveTxHash = await walletClient.writeContract({
address: quoteResponse.sellToken as `0x${string}`,
abi: erc20Abi,
functionName: "approve",
args: [
quotesResponse.quotes[0].issues.allowance.spender as `0x${string}`,
BigInt(quoteResponse.sellAmount),
],
});
await walletClient.waitForTransactionReceipt({
hash: approveTxHash,
confirmations: 1,
});

Important: Only EVM chains require token allowances. Solana and Tron do not need an approval step and allowanceTarget will be null for those chains. Native tokens (ETH, BNB, etc.) also do not need approvals.

3. Sign and submit a transaction

Now that we have both the approval and the quote, we can construct, sign, and submit the transaction. Optionally, after the approval, you might want to re-fetch a fresh quote.

Code below will construct, sign, and submit the transaction based on the quote response.

// Prepare a transaction request
const txRequest = {
to: quoteResponse.quotes[0].transaction.details.to as `0x${string}`,
data: quoteResponse.quotes[0].transaction.details.data as `0x${string}`,
value: BigInt(quoteResponse.quotes[0].transaction.details.value),
gas: quoteResponse.quotes[0].transaction.details.gas
? BigInt(quoteResponse.quotes[0].transaction.details.gas)
: undefined,
};
// Sign and submit the transaction request
const txHash = await walletClient.sendTransaction(txRequest);
// Wait for confirmation on Base
const receipt = await walletClient.waitForTransactionReceipt({
hash: txHash,
confirmations: 2,
});
console.log(`Transaction confirmed in block: ${receipt.blockNumber}`);
console.log(`View on BaseScan: https://basescan.org/tx/${txHash}`);

4. Monitor the cross chain execution

The last step is to monitor the execution of the cross chain transaction, including the fill on the destination chain. For that, we will use the /status endpoint.

const statusParams = new URLSearchParams({
originChain: 8453, // origin chain
originTxHash, // transaction hash of the origin chain transaction, submitted in previous step
quoteId, // the quote ID of the submitted quote, equivalent to quoteResponse.quotes[0].quoteId
});
const headers = {
'0x-api-key': '[api-key]', // Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps)
};
const statusResponse = await fetch('https://api.0x.org/cross-chain/status?' + statusParams.toString(), { headers });
console.log(await statusResponse.json());

In your application, you might need to monitor the status repeatedly, as bridging operation might take several seconds or minutes to complete.

It is a good practice to always pass quoteId when polling /status. Without it, bundled transactions (e.g. with ERC-4337) cannot be disambiguated, and the first status request will be slower.

For full status lifecycle details, polling intervals, and failure handling, see Tracking Transaction Status and Handling Failures and Recovery.

{
"status": "bridge_filled",
"bridge": "stargate",
"steps": [
{
"bridge": "stargate",
"originChainId": 8453,
"destinationChainId": 42161,
"sellToken": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"buyToken": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
"sellAmount": "997500",
"minBuyAmount": "987525",
"quotedBuyAmount": "997059",
"estimatedTimeSeconds": 20,
"transactions": [
{
"chainId": 8453,
"chain": "base",
"txHash": "0x53287620e5e527c6e11c70dfb1fc9e9f2675fcc27d7c98b2d0c7e82e9ac011d6",
"timestamp": 1762985805
},
{
"chainId": 42161,
"chain": "arbitrum",
"txHash": "0x390ca459f68cc5320d0c8904ba890cb5e4f25484a1a0def62c65852b48ddabd8",
"timestamp": 1762985833
}
],
"type": "bridge"
}
],
"failure": null,
"transactions": [
{
"chainId": 8453,
"chain": "base",
"txHash": "0x53287620e5e527c6e11c70dfb1fc9e9f2675fcc27d7c98b2d0c7e82e9ac011d6",
"timestamp": 1762985805
},
{
"chainId": 42161,
"chain": "arbitrum",
"txHash": "0x390ca459f68cc5320d0c8904ba890cb5e4f25484a1a0def62c65852b48ddabd8",
"timestamp": 1762985833
}
],
"zid": "0xeccee60cd76a5084fdff17d4"
}

Want to collect fees? See Monetize Your App to learn how to add integrator fees to cross-chain swaps.