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

# Code Samples

> Complete runnable examples for Node.js and Python

This page provides complete, runnable code samples for integrating LI.FI into your AI agent or application.

## Node.js (API Only)

A complete example using only HTTP requests - no SDK required.

```javascript theme={"system"}
// lifi-agent.js
// Run with: node lifi-agent.js

const LIFI_API = 'https://li.quest/v1';

// Optional: Set API key for higher rate limits
const API_KEY = process.env.LIFI_API_KEY || '';

const headers = {
  'Content-Type': 'application/json',
  ...(API_KEY && { 'x-lifi-api-key': API_KEY })
};

// Helper: Sleep for exponential backoff
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// Helper: Fetch with retry and backoff
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, { ...options, headers });
      
      if (response.status === 429) {
        // Rate limited - exponential backoff
        const waitTime = Math.pow(2, attempt) * 1000;
        console.log(`Rate limited. Waiting ${waitTime}ms...`);
        await sleep(waitTime);
        continue;
      }
      
      if (!response.ok) {
        const error = await response.json();
        throw new Error(`API Error: ${error.message || response.statusText}`);
      }
      
      return await response.json();
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      await sleep(1000);
    }
  }
}

// Step 1: Get supported chains
async function getChains(chainTypes = 'EVM') {
  const url = `${LIFI_API}/chains?chainTypes=${chainTypes}`;
  const data = await fetchWithRetry(url);
  return data.chains;
}

// Step 2: Get tokens for chains
async function getTokens(chainIds) {
  const url = `${LIFI_API}/tokens?chains=${chainIds.join(',')}`;
  const data = await fetchWithRetry(url);
  return data.tokens;
}

// Step 3: Get a quote
async function getQuote(params) {
  const {
    fromChain,
    toChain,
    fromToken,
    toToken,
    fromAmount,
    fromAddress,
    slippage = 0.005
  } = params;
  
  const url = new URL(`${LIFI_API}/quote`);
  url.searchParams.set('fromChain', fromChain);
  url.searchParams.set('toChain', toChain);
  url.searchParams.set('fromToken', fromToken);
  url.searchParams.set('toToken', toToken);
  url.searchParams.set('fromAmount', fromAmount);
  url.searchParams.set('fromAddress', fromAddress);
  url.searchParams.set('slippage', slippage);
  
  return await fetchWithRetry(url.toString());
}

// Step 4: Check transfer status
async function getStatus(txHash, bridge, fromChain, toChain) {
  const url = `${LIFI_API}/status?txHash=${txHash}&bridge=${bridge}&fromChain=${fromChain}&toChain=${toChain}`;
  return await fetchWithRetry(url);
}

// Step 5: Poll status until complete
async function pollStatus(txHash, bridge, fromChain, toChain, maxAttempts = 60) {
  console.log('Polling transfer status...');
  
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const status = await getStatus(txHash, bridge, fromChain, toChain);
    
    console.log(`Status: ${status.status} (${status.substatus || 'no substatus'})`);
    
    if (status.status === 'DONE') {
      if (status.substatus === 'COMPLETED') {
        console.log('Transfer completed successfully!');
        return { success: true, status };
      }
      if (status.substatus === 'PARTIAL') {
        console.log('Transfer completed with different token');
        return { success: true, partial: true, status };
      }
      if (status.substatus === 'REFUNDED') {
        console.log('Transfer failed - tokens refunded');
        return { success: false, refunded: true, status };
      }
    }
    
    if (status.status === 'FAILED') {
      console.log('Transfer failed');
      return { success: false, status };
    }
    
    // Wait 10 seconds before next poll
    await sleep(10000);
  }
  
  throw new Error('Status polling timeout');
}

// Main execution flow
async function executeTransfer(params) {
  console.log('Getting quote...');
  const quote = await getQuote(params);
  
  console.log(`Quote received: ${quote.tool}`);
  console.log(`Expected output: ${quote.estimate.toAmount}`);
  console.log(`Minimum output: ${quote.estimate.toAmountMin}`);
  
  // Here you would:
  // 1. Check if approval is needed (for ERC20 tokens)
  // 2. Send approval transaction if needed
  // 3. Send the main transaction using quote.transactionRequest
  // 4. Get the txHash from the transaction receipt
  
  console.log('\nTransaction request ready:');
  console.log(JSON.stringify(quote.transactionRequest, null, 2));
  
  return quote;
}

// Example usage
async function main() {
  try {
    // Get available chains
    const chains = await getChains();
    console.log(`Found ${chains.length} chains`);
    
    // Get quote for 10 USDC from Ethereum to Arbitrum
    const quote = await executeTransfer({
      fromChain: 1,
      toChain: 42161,
      fromToken: 'USDC',
      toToken: 'USDC',
      fromAmount: '10000000', // 10 USDC (6 decimals)
      fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0' // Replace with actual address
    });
    
    // After executing the transaction, poll for status:
    // const result = await pollStatus(txHash, quote.tool, 1, 42161);
    
  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();
```

***

## Node.js (with SDK)

Using the LI.FI SDK for full execution support.

```javascript theme={"system"}
// lifi-sdk-agent.js
// Install: npm install @lifi/sdk viem
// Run with: node lifi-sdk-agent.js

import { createConfig, getQuote, executeRoute, convertQuoteToRoute } from '@lifi/sdk';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { mainnet, arbitrum } from 'viem/chains';

// Configuration
const PRIVATE_KEY = process.env.PRIVATE_KEY; // Never hardcode!
const INTEGRATOR_NAME = 'your-agent-name';

// Initialize SDK
createConfig({
  integrator: INTEGRATOR_NAME,
  // Optional: API key for higher rate limits
  // apiKey: process.env.LIFI_API_KEY,
});

// Create wallet client
const account = privateKeyToAccount(PRIVATE_KEY);
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
});

// Get a quote
async function fetchQuote(params) {
  const quote = await getQuote({
    fromChain: params.fromChain,
    toChain: params.toChain,
    fromToken: params.fromToken,
    toToken: params.toToken,
    fromAmount: params.fromAmount,
    fromAddress: account.address,
  });
  
  return quote;
}

// Execute the transfer
async function executeTransfer(quote) {
  console.log('Executing transfer...');
  
  // Convert quote to route format for execution
  const route = convertQuoteToRoute(quote);
  
  const result = await executeRoute(route, {
    // Update hook called on each status change
    updateRouteHook: (updatedRoute) => {
      console.log('Route updated:', updatedRoute.id);
      const step = updatedRoute.steps[0];
      if (step?.execution) {
        console.log(`Execution status: ${step.execution.status}`);
      }
    },
  });
  
  return result;
}

// Main flow
async function main() {
  try {
    console.log(`Wallet address: ${account.address}`);
    
    // Get quote
    const quote = await fetchQuote({
      fromChain: 1,        // Ethereum
      toChain: 42161,      // Arbitrum
      fromToken: 'USDC',
      toToken: 'USDC',
      fromAmount: '10000000', // 10 USDC
    });
    
    console.log('Quote received:');
    console.log(`- Tool: ${quote.tool}`);
    console.log(`- Expected: ${quote.estimate.toAmount}`);
    console.log(`- Minimum: ${quote.estimate.toAmountMin}`);
    
    // Execute (uncomment to run actual transfer)
    // const result = await executeTransfer(quote);
    // console.log('Transfer complete:', result);
    
  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();
```

***

## Python (API Only)

Complete Python example using only HTTP requests.

```python theme={"system"}
# lifi_agent.py
# Install: pip install requests
# Run with: python lifi_agent.py

import os
import time
import requests
from typing import Optional, Dict, Any, List

LIFI_API = "https://li.quest/v1"
API_KEY = os.environ.get("LIFI_API_KEY", "")

def get_headers() -> Dict[str, str]:
    headers = {"Content-Type": "application/json"}
    if API_KEY:
        headers["x-lifi-api-key"] = API_KEY
    return headers


def fetch_with_retry(url: str, max_retries: int = 3) -> Dict[str, Any]:
    """Fetch with exponential backoff for rate limiting."""
    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=get_headers())
            
            if response.status_code == 429:
                wait_time = (2 ** attempt)
                print(f"Rate limited. Waiting {wait_time}s...")
                time.sleep(wait_time)
                continue
            
            response.raise_for_status()
            return response.json()
            
        except requests.exceptions.RequestException as e:
            if attempt == max_retries - 1:
                raise
            time.sleep(1)
    
    raise Exception("Max retries exceeded")


def get_chains(chain_types: str = "EVM") -> List[Dict]:
    """Get supported chains."""
    url = f"{LIFI_API}/chains?chainTypes={chain_types}"
    data = fetch_with_retry(url)
    return data["chains"]


def get_tokens(chain_ids: List[int]) -> Dict[str, List[Dict]]:
    """Get tokens for specified chains."""
    chains_param = ",".join(str(c) for c in chain_ids)
    url = f"{LIFI_API}/tokens?chains={chains_param}"
    data = fetch_with_retry(url)
    return data["tokens"]


def get_quote(
    from_chain: int,
    to_chain: int,
    from_token: str,
    to_token: str,
    from_amount: str,
    from_address: str,
    slippage: float = 0.005
) -> Dict[str, Any]:
    """Get a quote for token transfer."""
    params = {
        "fromChain": from_chain,
        "toChain": to_chain,
        "fromToken": from_token,
        "toToken": to_token,
        "fromAmount": from_amount,
        "fromAddress": from_address,
        "slippage": slippage
    }
    
    url = f"{LIFI_API}/quote?" + "&".join(f"{k}={v}" for k, v in params.items())
    return fetch_with_retry(url)


def get_status(
    tx_hash: str,
    bridge: str,
    from_chain: int,
    to_chain: int
) -> Dict[str, Any]:
    """Check transfer status."""
    url = f"{LIFI_API}/status?txHash={tx_hash}&bridge={bridge}&fromChain={from_chain}&toChain={to_chain}"
    return fetch_with_retry(url)


def poll_status(
    tx_hash: str,
    bridge: str,
    from_chain: int,
    to_chain: int,
    max_attempts: int = 60,
    poll_interval: int = 10
) -> Dict[str, Any]:
    """Poll status until transfer completes."""
    print("Polling transfer status...")
    
    for attempt in range(max_attempts):
        status = get_status(tx_hash, bridge, from_chain, to_chain)
        
        status_str = status.get("status", "UNKNOWN")
        substatus_str = status.get("substatus", "")
        print(f"Status: {status_str} ({substatus_str})")
        
        if status_str == "DONE":
            if substatus_str == "COMPLETED":
                print("Transfer completed successfully!")
                return {"success": True, "status": status}
            elif substatus_str == "PARTIAL":
                print("Transfer completed with different token")
                return {"success": True, "partial": True, "status": status}
            elif substatus_str == "REFUNDED":
                print("Transfer failed - tokens refunded")
                return {"success": False, "refunded": True, "status": status}
        
        if status_str == "FAILED":
            print("Transfer failed")
            return {"success": False, "status": status}
        
        time.sleep(poll_interval)
    
    raise Exception("Status polling timeout")


def execute_transfer(
    from_chain: int,
    to_chain: int,
    from_token: str,
    to_token: str,
    from_amount: str,
    from_address: str
) -> Dict[str, Any]:
    """Get quote and prepare for execution."""
    print("Getting quote...")
    quote = get_quote(
        from_chain=from_chain,
        to_chain=to_chain,
        from_token=from_token,
        to_token=to_token,
        from_amount=from_amount,
        from_address=from_address
    )
    
    print(f"Quote received: {quote['tool']}")
    print(f"Expected output: {quote['estimate']['toAmount']}")
    print(f"Minimum output: {quote['estimate']['toAmountMin']}")
    
    # Transaction request is ready for signing
    print("\nTransaction request:")
    tx_request = quote["transactionRequest"]
    print(f"  To: {tx_request['to']}")
    print(f"  Value: {tx_request['value']}")
    print(f"  Gas Limit: {tx_request.get('gasLimit', 'auto')}")
    
    return quote


def main():
    try:
        # Get available chains
        chains = get_chains()
        print(f"Found {len(chains)} chains")
        
        # Get quote for 10 USDC from Ethereum to Arbitrum
        quote = execute_transfer(
            from_chain=1,
            to_chain=42161,
            from_token="USDC",
            to_token="USDC",
            from_amount="10000000",  # 10 USDC (6 decimals)
            from_address="0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0"  # Replace
        )
        
        # After executing the transaction, poll for status:
        # result = poll_status(tx_hash, quote["tool"], 1, 42161)
        
    except Exception as e:
        print(f"Error: {e}")


if __name__ == "__main__":
    main()
```

***

## Environment Variables

Both examples support these environment variables:

```bash theme={"system"}
# Optional: API key for higher rate limits
export LIFI_API_KEY="your-api-key"

# For SDK example: Private key for signing (never commit!)
export PRIVATE_KEY="0x..."
```

***

## Quick Reference

### Node.js (API)

```bash theme={"system"}
# No dependencies required (uses native fetch)
node lifi-agent.js
```

### Node.js (SDK)

```bash theme={"system"}
npm install @lifi/sdk viem
node lifi-sdk-agent.js
```

### Python

```bash theme={"system"}
pip install requests
python lifi_agent.py
```

***

## Error Handling Patterns

Both examples include these error handling patterns:

1. **Exponential backoff** for rate limits (429)
2. **Retry logic** for transient failures
3. **Status polling** with timeout
4. **Graceful error messages** for debugging

For more details on error handling, see [Error Playbooks](/agents/reference/error-playbooks).
