> ## Documentation Index
> Fetch the complete documentation index at: https://developers.zerion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Swap Tokens & Bridge Assets

> Get quotes and ready-to-sign transactions for same-chain swaps and cross-chain bridges using the Zerion API.

**What you'll build:**

* Get swap quotes from multiple liquidity sources in a single call
* Receive ready-to-sign approve and swap transactions — no separate quote/execute steps
* Bridge assets across chains (EVM ↔ EVM, EVM ↔ Solana)

```
Swap 0.05 ETH → DAI on Ethereum

  Quote 1 (Relay):
    Output:           128.45 DAI
    Min after slip:   125.88 DAI
    Net of fees:      $128.10
    ✅ Transaction ready to sign

  Quote 2 (LI.FI):
    Output:           128.12 DAI
    Min after slip:   125.56 DAI
    Net of fees:      $127.78
    ✅ Transaction ready to sign
```

**Time:** \~15 minutes

## Prerequisites

* A Zerion API key ([get one here](https://dashboard.zerion.io))
* A wallet address with tokens to swap

## How it works

Unlike most swap APIs that require separate quote and execute endpoints, the Zerion API combines both into a **single call**:

1. Call `GET /v1/swap/quotes/` with the sender, recipient, input asset, output asset, and amount
2. Receive multiple quotes — each includes the expected output, fees, and (when executable) ready-to-sign `transaction_approve` and `transaction_swap` payloads
3. Pick a quote and sign the transactions with your wallet

Quotes are returned best-first, sorted by the fiat value of `output_amount_after_fees`.

<Steps>
  ### Get swap quotes

  Use the [swap and bridge quotes](/api-reference/swap/get-swap-and-bridge-quotes) endpoint.

  <CodeGroup>
    ```javascript JavaScript theme={null}
    const API_KEY = process.env.ZERION_API_KEY;
    const headers = {
      accept: "application/json",
      authorization: `Basic ${btoa(API_KEY + ":")}`,
    };

    // Swap 0.05 ETH → DAI on Ethereum
    const wallet = "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B";
    const params = new URLSearchParams({
      from: wallet,
      to: wallet,
      "input[chain_id]": "ethereum",
      "input[fungible_id]": "eth",
      "input[amount]": "0.05", // human-readable decimal, not wei
      "output[fungible_id]": "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI
    });

    const response = await fetch(
      `https://api.zerion.io/v1/swap/quotes/?${params}`,
      { headers }
    );
    const { data } = await response.json();

    for (const quote of data) {
      const {
        liquidity_source,
        output_amount,
        minimum_output_amount,
        output_amount_after_fees,
        network_fee,
        error,
      } = quote.attributes;
      console.log(`\nQuote from ${liquidity_source.name}:`);
      console.log(`  Output:         ${output_amount.quantity}`);
      console.log(`  Min after slip: ${minimum_output_amount.quantity}`);
      console.log(`  Net of fees:    ${output_amount_after_fees.value} ${output_amount_after_fees.currency ?? ""}`);
      console.log(`  Network fee:    ${network_fee?.amount?.value ?? "n/a"}`);
      if (error) console.log(`  ⚠️  ${error.code}: ${error.message ?? error.hint}`);
    }
    ```

    ```python Python theme={null}
    import os, requests

    api_key = os.environ["ZERION_API_KEY"]
    wallet = "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"

    # Swap 0.05 ETH → DAI on Ethereum
    response = requests.get(
        "https://api.zerion.io/v1/swap/quotes/",
        params={
            "from": wallet,
            "to": wallet,
            "input[chain_id]": "ethereum",
            "input[fungible_id]": "eth",
            "input[amount]": "0.05",  # human-readable decimal, not wei
            "output[fungible_id]": "0x6B175474E89094C44Da98b954EedeAC495271d0F",  # DAI
        },
        auth=(api_key, ""),
    )

    for quote in response.json()["data"]:
        attrs = quote["attributes"]
        print(f"\nQuote from {attrs['liquidity_source']['name']}:")
        print(f"  Output:         {attrs['output_amount']['quantity']}")
        print(f"  Min after slip: {attrs['minimum_output_amount']['quantity']}")
        after_fees = attrs["output_amount_after_fees"]
        print(f"  Net of fees:    {after_fees.get('value')} {after_fees.get('currency', '')}")
        if "error" in attrs:
            err = attrs["error"]
            print(f"  Note: {err['code']} — {err.get('message') or err['hint']}")
    ```

    ```bash cURL theme={null}
    curl -g -u "YOUR_API_KEY:" \
      "https://api.zerion.io/v1/swap/quotes/?from=0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B&to=0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B&input[chain_id]=ethereum&input[fungible_id]=eth&input[amount]=0.05&output[fungible_id]=0x6B175474E89094C44Da98b954EedeAC495271d0F"
    ```
  </CodeGroup>

  Each quote includes:

  * `liquidity_source` — provider id, display name, and icon
  * `input_amount` / `output_amount` — what you're sending and the estimated amount you'll receive before slippage (`quantity` is the human-readable decimal; `value` / `usd_value` are fiat conversions when available)
  * `minimum_output_amount` — guaranteed output after slippage
  * `output_amount_after_fees` — net output once protocol, bridge, and network fees are subtracted; this is the field quotes are ranked by
  * `slippage_percent` — actual slippage applied (the `slippage_percent` query parameter when provided, otherwise auto-chosen)
  * `protocol_fee` / `bridge_fee` / `network_fee` — fee breakdown; each carries `amount` and an `included_in_rate` flag (whether already deducted from the rate)
  * `transaction_approve` — ERC-20 approve transaction to sign first. Absent when not needed (Solana, EVM native-asset input, or sufficient allowance already granted)
  * `transaction_swap` — swap transaction to sign. Absent when the quote is informational only (e.g. `error` is set)
  * `error` — present when the quote can't be executed as-is (e.g. `not_enough_input_asset_balance`)
  * `estimated_time_seconds` — typically present on bridge routes

  ### Pick a quote and check for errors

  Quotes are returned best-first. Before signing, check whether the chosen quote has an `error` and a `transaction_swap`.

  ```javascript theme={null}
  const quote = data[0]; // best by output_amount_after_fees
  const { error, transaction_approve, transaction_swap } = quote.attributes;

  if (error) {
    console.log(`Cannot execute: ${error.code} (hint: ${error.hint})`);
    process.exit(1);
  }
  if (!transaction_swap) {
    console.log("No transaction payload — informational quote only");
    process.exit(1);
  }
  ```

  <Note>
    `error.hint` is a machine-readable next step (`topup`, `increase_input_amount`, `unspecified`) you can surface in your UI to guide the user.
  </Note>

  ### Sign the approve transaction (if present)

  If `transaction_approve` is present, sign and confirm it before submitting the swap. It's a complete EVM transaction — no manual `approve(spender, amount)` call needed.

  ```javascript theme={null}
  import { ethers } from "ethers";

  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

  async function sendEVM(payload) {
    // EVM numeric fields are hex strings — ethers accepts them directly
    const tx = await signer.sendTransaction({
      type: parseInt(payload.type, 16),
      to: payload.to,
      data: payload.data,
      value: payload.value,
      gasLimit: payload.gas,
      nonce: parseInt(payload.nonce, 16),
      chainId: parseInt(payload.chain_id, 16),
      ...(payload.max_fee
        ? { maxFeePerGas: payload.max_fee, maxPriorityFeePerGas: payload.max_priority_fee }
        : { gasPrice: payload.gas_price }),
    });
    return tx.wait();
  }

  if (transaction_approve?.evm) {
    console.log("Sending approval...");
    await sendEVM(transaction_approve.evm);
    console.log("Approval confirmed");
  }
  ```

  <Note>
    `transaction_approve` is omitted for Solana, for EVM swaps where the input is the chain's native asset (ETH, MATIC, etc.), and when the wallet already has sufficient allowance.
  </Note>

  ### Sign and send the swap transaction

  ```javascript theme={null}
  const receipt = await sendEVM(transaction_swap.evm);
  console.log(`Confirmed in block ${receipt.blockNumber}`);
  ```

  For Solana quotes, `transaction_swap.solana.raw` is a base64-encoded raw transaction that you decode and sign with the Solana wallet of your choice (e.g. `@solana/web3.js`).

  ### Bridge assets across chains

  To bridge tokens between chains, set a different `output[chain_id]`. The `to` parameter is the recipient on the destination chain — set it to your wallet on that chain (use a base58 Solana address when bridging to Solana).

  ```bash theme={null}
  # Bridge 1 USDC from Ethereum to Polygon
  curl -g -u "YOUR_API_KEY:" \
    "https://api.zerion.io/v1/swap/quotes/?from=YOUR_WALLET&to=YOUR_WALLET&input[chain_id]=ethereum&input[fungible_id]=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&input[amount]=1&output[chain_id]=polygon&output[fungible_id]=0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
  ```

  <Tip>
    Use the [List fungibles available for bridging](/api-reference/swap/list-fungibles-available-for-bridging) endpoint to discover which tokens can be bridged between two specific chains before requesting quotes.
  </Tip>
</Steps>

## Key parameters

| Parameter             | Description                                                                                                                                              |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `from`                | Wallet performing the swap. Must match the chain type of `input[chain_id]` (EVM hex or Solana base58). Required.                                         |
| `to`                  | Recipient of the output asset. Must match the chain type of `output[chain_id]`. For a same-chain swap to the same wallet, set equal to `from`. Required. |
| `input[chain_id]`     | Source chain (e.g., `ethereum`, `polygon`, `solana`). Required.                                                                                          |
| `input[fungible_id]`  | Input asset's fungible ID — either a Zerion fungible ID (e.g., `eth`) or the token contract address. Required.                                           |
| `input[amount]`       | Human-readable input amount as a decimal string (e.g., `"0.05"`), not the smallest unit. Required.                                                       |
| `output[chain_id]`    | Destination chain. Defaults to `input[chain_id]` (same-chain swap).                                                                                      |
| `output[fungible_id]` | Output asset's fungible ID or token contract address. Required.                                                                                          |
| `slippage_percent`    | Maximum acceptable slippage in percent. Auto-chosen when omitted.                                                                                        |
| `currency`            | Currency for fiat values in the response (e.g., `usd`). Defaults to `usd`.                                                                               |
