Skip to main content
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.
Already using the LI.FI SDK or Widget? Composer works automatically. See the SDK guide or Widget guide instead.

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. 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.

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

The simplest way to get a Composer transaction. Returns a single best route with transaction data included.
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'

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.
ValueMeaning
0.0050.5% (default)
0.011%
0.033%

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.
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];
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.

Getting transaction data for a route step

TypeScript
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.

Quote Response Structure

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

Key fields

FieldDescription
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.toTokenToken objects with address, symbol, decimals, and chainId.
action.slippageThe slippage tolerance applied to this quote.
estimate.toAmountEstimated output in the toToken’s smallest unit.
estimate.toAmountMinMinimum output accounting for slippage.
estimate.approvalAddressThe contract address to approve for token spending (see below).
estimate.executionDurationEstimated execution time in seconds.
transactionRequestReady-to-sign EVM transaction. Fields (value, gasLimit, gasPrice) are hex-encoded strings.
estimate.feeCostsArray 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.
includedStepsOrdered list of steps the route will execute. Typically includes a feeCollection step followed by the composer step.
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).

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.
ChainLI.FI Diamond Address
All EVM chains0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE
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.
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.

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.
Skip this step if fromToken is a native token (e.g., ETH). Native tokens don’t require approval.
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
);
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.

Send the Transaction

Submit the transactionRequest from the quote response. This is a standard EVM transaction.
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);

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:
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);
}
For the full status reference including substatus values, see Transaction Status Tracking.

Error Handling

Common errors when working with Composer:
ErrorCauseResolution
No routes foundVault token not supported or insufficient liquidityVerify the vault token address is correct and the protocol is supported
Simulation failedThe Composer execution would fail onchainCheck token balances, allowances, and that the vault is accepting deposits
Insufficient allowanceToken approval not set or too lowCall approve() with the correct approvalAddress and amount
For the full error reference, see Error Codes.

Next Steps

Cross-Chain Patterns

Advanced cross-chain Composer flows and patterns

Withdrawals Guide

Implement same-chain and cross-chain withdrawals

Vault Deposit Recipes

Copy-paste recipes for Morpho, Aave, Euler, and more

SDK Integration

Managed execution with hooks, events, and automatic retries

Supported Protocols

Full list of supported protocols and capabilities