Swap and Send

View as Markdown

By default, swapped tokens are returned to the address that initiates the trade (the taker). With the recipient parameter, you can send the output tokens directly to any wallet address — in a single transaction.

This unlocks use cases like:

  • Swapping and gifting tokens to another wallet
  • Purchasing tokens on behalf of a user
  • Automating treasury distributions in a different token
  • Powering “pay with any token” checkout flows

The recipient parameter is supported on both the Swap API and Gasless API.


How it works

When you include recipient in your request, the buyToken is delivered to that address at settlement. The taker (the address signing and submitting the transaction) still pays and authorizes the swap — they just don’t receive the output.

Taker ──pays sellToken──► 0x Settler / AllowanceHolder ──sends buyToken──► Recipient

If recipient is omitted, it defaults to the taker’s address, so existing integrations are unaffected.


API Reference

recipient

PropertyValue
Typestring
RequiredNo
Format^0x[a-fA-F0-9]{40}$
DefaultTaker address
SupportedSwap API (price, quote), Gasless API (price, quote)

recipient is not supported for wrap and unwrap operations (e.g. ETH ↔ WETH). Passing it for those trade types will result in an error. Check for native/wrapped token pairs and omit recipient before calling the API.


Swap API Example

Include recipient as a query parameter when fetching a quote and submitting the swap.

1const params = new URLSearchParams({
2 sellToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH
3 buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
4 sellAmount: "1000000000000000000", // 1 ETH in wei
5 taker: "0xYourTakerAddress",
6 recipient: "0xRecipientAddress", // buyToken sent here
7});
8
9const response = await fetch(
10 `https://api.0x.org/swap/permit2/quote?${params}`,
11 {
12 headers: {
13 "0x-api-key": process.env.ZEROX_API_KEY!,
14 "0x-version": "v2",
15 },
16 },
17);
18
19const quote = await response.json();

Gasless API Example

The recipient parameter works the same way with the Gasless API. Include it in the quote request body — the relayer will deliver buyToken to the recipient address on settlement.

1const params = new URLSearchParams({
2 sellToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
3 buyToken: "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI
4 sellAmount: "1000000000", // 1000 USDC (6 decimals)
5 taker: "0xYourTakerAddress",
6 recipient: "0xRecipientAddress", // buyToken sent here
7});
8
9const response = await fetch(`https://api.0x.org/gasless/quote?${params}`, {
10 headers: {
11 "0x-api-key": process.env.ZEROX_API_KEY!,
12 "0x-version": "v2",
13 },
14});
15
16const quote = await response.json();

Errors

The API validates recipient at the price and quote stage — before any transaction is submitted — so no gas is wasted. There are two common error cases: an invalid recipient address and passing recipient on a wrap/unwrap operation.

Invalid recipient address

The recipient value must be a valid Ethereum address — a 42-character hex string starting with 0x. This error is triggered by things like a truncated address, a non-hex character, or passing a raw ENS name instead of a resolved address.

Always resolve ENS names to addresses client-side before passing them to the API. The recipient field does not support ENS.

1{
2 "name": "INPUT_INVALID",
3 "message": "The input is invalid",
4 "data": {
5 "zid": "0x8843f11733fbf965f90ef10a",
6 "details": [
7 {
8 "field": "recipient",
9 "reason": "Invalid ethereum address"
10 }
11 ]
12 }
13}

To catch this before hitting the API, validate the address client-side:

1import { isAddress, getAddress } from "viem";
2
3const raw = userInputAddress;
4
5if (!isAddress(raw)) {
6 throw new Error("Invalid recipient address");
7}
8
9const recipient = getAddress(raw); // normalizes to EIP-55 checksum

Wrap/unwrap operations

recipient is not supported when sellToken and buyToken are a native/wrapped pair (e.g. ETH ↔ WETH, MATIC ↔ WMATIC). These operations don’t route through the swap protocol, so the recipient field has no effect and is rejected.

1{
2 "name": "RECIPIENT_NOT_SUPPORTED",
3 "message": "The recipient parameter is not supported for wrap/unwrap operations",
4 "data": {
5 "zid": "0x05e4e8147d97597183b471ca"
6 }
7}

Guard against this by detecting wrap/unwrap pairs before building your request and omitting recipient in those cases:

1const WRAP_UNWRAP_PAIRS: [string, string][] = [
2 ["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"], // ETH ↔ WETH
3 // add other pairs as needed
4];
5
6const isWrapUnwrap = (sellToken: string, buyToken: string) =>
7 WRAP_UNWRAP_PAIRS.some(
8 ([a, b]) =>
9 (sellToken.toLowerCase() === a.toLowerCase() && buyToken.toLowerCase() === b.toLowerCase()) ||
10 (sellToken.toLowerCase() === b.toLowerCase() && buyToken.toLowerCase() === a.toLowerCase())
11 );
12
13const params = {
14 sellToken,
15 buyToken,
16 sellAmount,
17 taker,
18 ...(!isWrapUnwrap(sellToken, buyToken) && { recipient }),
19};

The zid in the error response is a trace ID — include it when contacting support to help diagnose the issue.


Key considerations

  • Taker still pays. The taker signs and funds the transaction. Only the output destination changes.
  • Omitting recipient is safe, it falls back to the taker address, so no changes are needed for existing flows.
  • Validate before sending. Always validate recipient client-side to avoid wasted gas on failed transactions.
  • Wrap/unwrap is not supported. Check for native/wrapped token pairs and strip recipient before calling the API.