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

# API Integration

> Integrate LI.FI Composer via the REST API with same-chain deposits, cross-chain flows, and error handling.

<Note>
  **Prefer to author multi-step flows explicitly?** See the [Composer API](/composer/composer-api/overview), which documents the explicit Flow authoring API.
</Note>

This guide walks you through integrating Composer directly via the LI.FI REST API. This approach gives you full control over the request/response flow and is ideal for backend services, custom frontends, or any environment where you want to manage transactions yourself.

<Note>
  **Already using the LI.FI SDK or Widget?** Composer works automatically. See the [SDK guide](/composer/lifi-api/guides/sdk-integration) or [Widget guide](/composer/lifi-api/guides/widget-integration) instead.
</Note>

***

## Authentication

The LI.FI API is open and requires no API key. You can start making requests immediately.

* **`integrator`** (optional query parameter): A string that identifies your application in analytics and on-chain events. Defaults to `"lifi-api"` when omitted. Must be alphanumeric with hyphens, underscores, or dots, max 23 characters.
* **`x-lifi-api-key`** (optional header): An API key is generated automatically when you create an integration in the [LI.FI partner portal](https://portal.li.fi/). Attach it for higher rate limits. Without a key, unauthenticated limits apply: 75 requests per two hours for `/quote` and `/advanced/routes`, 50 requests per two hours for `/advanced/stepTransaction`, and 100 requests per minute for other endpoints. With an API key, the default is 100 requests per minute across all endpoints.

For full details, see [Authentication](/api-reference/authentication).

***

## Overview

Composer does not require a dedicated endpoint. Set `toToken` to a supported protocol token address and LI.FI returns a Composer route from the standard endpoints.

The integration flow:

1. **Request a quote** via `GET /v1/quote` or `POST /v1/advanced/routes`
2. **Set token allowance** by approving the LI.FI Diamond contract to spend your tokens
3. **Send the transaction** using the `transactionRequest` from the quote response
4. **Track status** by polling `GET /v1/status` for cross-chain transfers

***

## Request a Composer Quote

### Using `GET /quote` (Recommended)

The simplest way to get a Composer transaction. Returns a single best route with transaction data included.

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X GET 'https://li.quest/v1/quote?fromChain=8453&toChain=8453&fromToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&toToken=0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A&fromAddress=0xYOUR_WALLET_ADDRESS&toAddress=0xYOUR_WALLET_ADDRESS&fromAmount=1000000'
  ```

  ```ts TypeScript theme={"system"}
  import axios from 'axios';

  const getQuote = async (params: {
    fromChain: number;
    toChain: number;
    fromToken: string;
    toToken: string;
    fromAmount: string;
    fromAddress: string;
    slippage?: number;
    integrator?: string;
  }) => {
    const result = await axios.get('https://li.quest/v1/quote', {
      params: {
        ...params,
        toAddress: params.fromAddress,
      },
    });
    return result.data;
  };

  const quote = await getQuote({
    fromChain: 8453,                                              // Base
    toChain: 8453,                                                // Base (same-chain)
    fromToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',     // USDC on Base
    toToken: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A',       // Morpho vault token
    fromAmount: '1000000',                                        // 1 USDC (6 decimals)
    fromAddress: '0xYOUR_WALLET_ADDRESS',
    slippage: 0.005,                                              // 0.5% slippage tolerance
    integrator: 'your-app-name',
  });
  ```

  ```python Python theme={"system"}
  import requests

  response = requests.get('https://li.quest/v1/quote', params={
      'fromChain': 8453,
      'toChain': 8453,
      'fromToken': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
      'toToken': '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A',
      'fromAmount': '1000000',
      'fromAddress': '0xYOUR_WALLET_ADDRESS',
      'toAddress': '0xYOUR_WALLET_ADDRESS',
      'slippage': 0.005,
      'integrator': 'your-app-name',
  })

  quote = response.json()
  ```
</CodeGroup>

### Slippage

The `slippage` parameter is a decimal value representing the maximum acceptable price difference. For example, `0.005` means 0.5%. If omitted, the API defaults to `0.005`. Cross-chain routes involve more steps, so consider `0.01` (1%) or higher for cross-chain flows.

| Value   | Meaning        |
| ------- | -------------- |
| `0.005` | 0.5% (default) |
| `0.01`  | 1%             |
| `0.03`  | 3%             |

### Using `POST /advanced/routes`

Returns multiple route options. Useful when you want to present choices to the user or need more control over route selection.

<CodeGroup>
  ```ts TypeScript theme={"system"}
  const getRoutes = async (params: {
    fromChainId: number;
    toChainId: number;
    fromTokenAddress: string;
    toTokenAddress: string;
    fromAmount: string;
    fromAddress: string;
  }) => {
    const result = await axios.post('https://li.quest/v1/advanced/routes', {
      ...params,
      toAddress: params.fromAddress,
    });
    return result.data;
  };

  const routesResponse = await getRoutes({
    fromChainId: 8453,
    toChainId: 8453,
    fromTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    toTokenAddress: '0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A',
    fromAmount: '1000000',
    fromAddress: '0xYOUR_WALLET_ADDRESS',
  });

  // Select the best route
  const route = routesResponse.routes[0];
  ```
</CodeGroup>

<Note>
  When using `/advanced/routes`, transaction data is **not** included in the response. You must call `POST /v1/advanced/stepTransaction` to get the `transactionRequest` for each step. With `/quote`, transaction data is included directly.
</Note>

#### Getting transaction data for a route step

```ts TypeScript theme={"system"}
const getStepTransaction = async (step: any) => {
  const result = await axios.post('https://li.quest/v1/advanced/stepTransaction', step);
  return result.data;
};

const route = routesResponse.routes[0];
const stepWithTx = await getStepTransaction(route.steps[0]);
// stepWithTx.transactionRequest now contains the ready-to-sign transaction
```

For a detailed comparison, see [Difference between Quote and Route](/introduction/user-flows-and-examples/difference-between-quote-and-route).

***

## Quote Response Structure

A Composer quote response contains the route details, estimated output, and a ready-to-sign transaction. Here are the key fields:

<Expandable title="Example response (annotated)">
  ```json theme={"system"}
  {
    "id": "0x...",
    "type": "lifi",
    "tool": "composer",
    "toolDetails": {
      "key": "composer",
      "name": "Composer",
      "logoURI": "https://..."
    },
    "action": {
      "fromToken": {
        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "symbol": "USDC",
        "decimals": 6,
        "chainId": 8453
      },
      "toToken": {
        "address": "0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A",
        "symbol": "sparkUSDC",
        "decimals": 18,
        "chainId": 8453
      },
      "fromAmount": "1000000",
      "fromChainId": 8453,
      "toChainId": 8453,
      "slippage": 0.005
    },
    "estimate": {
      "toAmount": "946832715862427",
      "toAmountMin": "942098552283115",
      "approvalAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
      "executionDuration": 0,
      "feeCosts": [...],
      "gasCosts": [...]
    },
    "integrator": "lifi-api",
    "transactionRequest": {
      "to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
      "data": "0x...",
      "value": "0x0",
      "gasLimit": "0x5f45bc",
      "gasPrice": "0x1b0875",
      "chainId": 8453,
      "from": "0xYOUR_WALLET_ADDRESS"
    },
    "includedSteps": [
      {
        "type": "protocol",
        "tool": "feeCollection",
        "toolDetails": { "key": "feeCollection", "name": "Integrator Fee" },
        "action": {...},
        "estimate": {...}
      },
      {
        "type": "protocol",
        "tool": "composer",
        "toolDetails": { "key": "composer", "name": "Composer" },
        "action": {...},
        "estimate": {...}
      }
    ]
  }
  ```
</Expandable>

### Key fields

| Field                                 | Description                                                                                                                                                                                            |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `tool`                                | `"composer"` for same-chain Composer routes. For cross-chain routes, this is the bridge name (e.g., `"stargateV2"`). Check `includedSteps` for `tool: "composer"` to confirm Composer is in the route. |
| `action.fromToken` / `action.toToken` | Token objects with `address`, `symbol`, `decimals`, and `chainId`.                                                                                                                                     |
| `action.slippage`                     | The slippage tolerance applied to this quote.                                                                                                                                                          |
| `estimate.toAmount`                   | Estimated output in the `toToken`'s smallest unit.                                                                                                                                                     |
| `estimate.toAmountMin`                | Minimum output accounting for slippage.                                                                                                                                                                |
| `estimate.approvalAddress`            | The contract address to approve for token spending (see below).                                                                                                                                        |
| `estimate.executionDuration`          | Estimated execution time in seconds.                                                                                                                                                                   |
| `transactionRequest`                  | Ready-to-sign EVM transaction. Fields (`value`, `gasLimit`, `gasPrice`) are **hex-encoded strings**.                                                                                                   |
| `estimate.feeCosts`                   | Array of fee entries. Every Composer route includes a `feeCollection` step that takes a small percentage fee. Check this array to surface fee details to users.                                        |
| `includedSteps`                       | Ordered list of steps the route will execute. Typically includes a `feeCollection` step followed by the `composer` step.                                                                               |

<Note>
  **Hex-encoded values:** The `transactionRequest` fields `value`, `gasLimit`, and `gasPrice` are hex strings (e.g., `"0x5f45bc"`). The `chainId` field is a plain number. When building transactions manually (e.g., in Python), parse hex fields with `int(value, 16)`.
</Note>

### Contract addresses

The `estimate.approvalAddress` and `transactionRequest.to` both point to the **LI.FI Diamond contract**, a verified, audited smart contract deployed on all supported chains.

| Chain          | LI.FI Diamond Address                                                                                                   |
| -------------- | ----------------------------------------------------------------------------------------------------------------------- |
| All EVM chains | [`0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE`](https://etherscan.io/address/0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE) |

The Composer onchain VM (which executes the compiled bytecode) is a separate contract called internally by the Diamond. For audit reports and contract verification, see [Security and Audits](/introduction/learn-more/security-and-audits).

<Tip>
  Quotes reflect current market conditions and may become stale. If the user reviews a quote for more than 30 seconds, re-fetch before signing to get up-to-date pricing and simulation results.
</Tip>

### Morpho vault naming

Morpho vaults are named after their **curator**, not after Morpho itself. For example, the address `0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A` returns `symbol: "sparkUSDC"` because it is a Spark-curated Morpho vault. This is expected: Morpho provides the vault infrastructure, and curators like Spark create strategies on top.

***

## Set Token Allowance

Before executing, the LI.FI Diamond contract needs approval to spend your tokens. The approval address is returned in the quote response at `estimate.approvalAddress`.

<Note>
  Skip this step if `fromToken` is a native token (e.g., ETH). Native tokens don't require approval.
</Note>

<CodeGroup>
  ```ts TypeScript theme={"system"}
  import { ethers } from 'ethers';

  const ERC20_ABI = [
    'function approve(address spender, uint256 amount) returns (bool)',
    'function allowance(address owner, address spender) view returns (uint256)',
  ];

  const ensureAllowance = async (
    signer: ethers.Signer,
    tokenAddress: string,
    approvalAddress: string,
    amount: string
  ) => {
    if (tokenAddress === ethers.ZeroAddress) return; // Native token

    const erc20 = new ethers.Contract(tokenAddress, ERC20_ABI, signer);
    const owner = await signer.getAddress();
    const currentAllowance = await erc20.allowance(owner, approvalAddress);

    if (currentAllowance < BigInt(amount)) {
      const tx = await erc20.approve(approvalAddress, amount);
      await tx.wait();
    }
  };

  await ensureAllowance(
    signer,
    quote.action.fromToken.address,
    quote.estimate.approvalAddress,
    quote.action.fromAmount
  );
  ```

  ```python Python theme={"system"}
  from web3 import Web3

  ERC20_ABI = [
      {
          "name": "approve",
          "type": "function",
          "inputs": [
              {"name": "spender", "type": "address"},
              {"name": "amount", "type": "uint256"}
          ],
          "outputs": [{"name": "", "type": "bool"}]
      },
      {
          "name": "allowance",
          "type": "function",
          "inputs": [
              {"name": "owner", "type": "address"},
              {"name": "spender", "type": "address"}
          ],
          "outputs": [{"name": "", "type": "uint256"}]
      }
  ]

  def ensure_allowance(w3, account, token_address, approval_address, amount):
      token = w3.eth.contract(address=token_address, abi=ERC20_ABI)
      current = token.functions.allowance(account.address, approval_address).call()

      if current < int(amount):
          tx = token.functions.approve(approval_address, int(amount)).build_transaction({
              'from': account.address,
              'nonce': w3.eth.get_transaction_count(account.address),
          })
          signed = account.sign_transaction(tx)
          tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
          w3.eth.wait_for_transaction_receipt(tx_hash)

  ensure_allowance(
      w3,
      account,
      quote['action']['fromToken']['address'],
      quote['estimate']['approvalAddress'],
      quote['action']['fromAmount']
  )
  ```
</CodeGroup>

<Warning>
  If an approval transaction was sent, re-fetch the quote before executing. The `transactionRequest` contains gas estimates that can become stale by the time the approval confirms. Call `GET /v1/quote` again with the same parameters and use the fresh `transactionRequest`.
</Warning>

***

## Send the Transaction

Submit the `transactionRequest` from the quote response. This is a standard EVM transaction.

<CodeGroup>
  ```ts TypeScript theme={"system"}
  const tx = await signer.sendTransaction(quote.transactionRequest);
  console.log('Transaction hash:', tx.hash);

  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);
  ```

  ```python Python theme={"system"}
  tx = {
      'to': quote['transactionRequest']['to'],
      'data': quote['transactionRequest']['data'],
      'value': int(quote['transactionRequest']['value'], 16),
      'gas': int(quote['transactionRequest']['gasLimit'], 16),
      'gasPrice': int(quote['transactionRequest']['gasPrice'], 16),
      'nonce': w3.eth.get_transaction_count(account.address),
      'chainId': int(quote['transactionRequest']['chainId']),
  }

  signed = account.sign_transaction(tx)
  tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
  receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
  print(f"Confirmed: {receipt['transactionHash'].to_0x_hex()}")
  ```
</CodeGroup>

***

## Track Status

For **same-chain** Composer transactions, the operation is complete once the transaction is confirmed.

For **cross-chain** Composer flows, poll the `/status` endpoint until the transfer completes:

<CodeGroup>
  ```ts TypeScript theme={"system"}
  const getStatus = async (txHash: string, fromChain: number, toChain: number) => {
    const result = await axios.get('https://li.quest/v1/status', {
      params: { txHash, fromChain, toChain },
    });
    return result.data;
  };

  const pollStatus = async (txHash: string, fromChain: number, toChain: number) => {
    let status;
    do {
      status = await getStatus(txHash, fromChain, toChain);
      console.log(`Status: ${status.status} (${status.substatus || ''})`);

      if (status.status !== 'DONE' && status.status !== 'FAILED') {
        await new Promise((r) => setTimeout(r, 5000));
      }
    } while (status.status !== 'DONE' && status.status !== 'FAILED');

    return status;
  };

  // Only needed for cross-chain
  if (quote.action.fromChainId !== quote.action.toChainId) {
    const finalStatus = await pollStatus(
      tx.hash,
      quote.action.fromChainId,
      quote.action.toChainId
    );
    console.log('Final:', finalStatus.status);
  }
  ```
</CodeGroup>

For the full status reference including substatus values, see [Transaction Status Tracking](/introduction/user-flows-and-examples/status-tracking).

***

## Error Handling

Common errors when working with Composer:

| Error                    | Cause                                               | Resolution                                                                                                |
| ------------------------ | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `No routes found`        | Vault token not supported or insufficient liquidity | Verify the vault token address is correct and the protocol is [supported](/composer/protocols-and-chains) |
| `Simulation failed`      | The Composer execution would fail onchain           | Check token balances, allowances, and that the vault is accepting deposits                                |
| `Insufficient allowance` | Token approval not set or too low                   | Call `approve()` with the correct `approvalAddress` and amount                                            |

For the full error reference, see [Error Codes](/api-reference/error-codes).

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Cross-Chain Patterns" icon="bridge" href="/composer/lifi-api/guides/cross-chain-compose">
    Advanced cross-chain Composer flows and patterns
  </Card>

  <Card title="Withdrawals Guide" icon="clock" href="/composer/lifi-api/guides/withdrawals">
    Implement same-chain and cross-chain withdrawals
  </Card>

  <Card title="Vault Deposit Recipes" icon="book" href="/composer/lifi-api/recipes/vault-deposits">
    Copy-paste recipes for Morpho, Aave, Euler, and more
  </Card>

  <Card title="SDK Integration" icon="cube" href="/composer/lifi-api/guides/sdk-integration">
    Managed execution with hooks, events, and automatic retries
  </Card>

  <Card title="Supported Protocols" icon="list" href="/composer/protocols-and-chains">
    Full list of supported protocols and capabilities
  </Card>
</CardGroup>
