Skip to main content
Cross-chain transfers take time to complete. This page explains how to poll the status endpoint, interpret status values, and handle each outcome correctly.

Status Endpoint

GET https://li.quest/v1/status

Parameters

ParameterRequiredDescriptionSource
txHashYesTransaction hash from executionFrom sendTransaction result
fromChainNoSource chain ID (recommended)From quote.action.fromChainId
toChainNoDestination chain IDFrom quote.action.toChainId
bridgeNoBridge/tool identifierFrom quote.tool
Only txHash is required. Providing fromChain speeds up the response significantly.

Example Request

curl "https://li.quest/v1/status?txHash=0xabc123..."
Or with optional parameters for faster response:
curl "https://li.quest/v1/status?txHash=0xabc123...&fromChain=1&toChain=42161&bridge=stargate"

Status Response

{
  "transactionId": "0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
  "sending": {
    "txHash": "0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
    "txLink": "https://etherscan.io/tx/0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
    "chainId": 1,
    "amount": "10000000",
    "token": {
      "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "chainId": 1,
      "symbol": "USDC",
      "decimals": 6,
      "name": "USD Coin",
      "coinKey": "USDC",
      "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
      "priceUSD": "0.999606"
    },
    "amountUSD": "9.9961",
    "gasPrice": "65729222",
    "gasUsed": "220266",
    "gasToken": {
      "address": "0x0000000000000000000000000000000000000000",
      "chainId": 1,
      "symbol": "ETH",
      "decimals": 18,
      "name": "ETH",
      "coinKey": "ETH",
      "priceUSD": "2923.27"
    },
    "gasAmount": "14477912813052",
    "gasAmountUSD": "0.0423",
    "timestamp": 1737625200
  },
  "receiving": {
    "txHash": "0xdef456789abc...",
    "txLink": "https://arbiscan.io/tx/0xdef456789abc...",
    "chainId": 42161,
    "amount": "9965731",
    "token": {
      "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
      "chainId": 42161,
      "symbol": "USDC",
      "decimals": 6,
      "name": "USD Coin",
      "coinKey": "USDC",
      "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
      "priceUSD": "0.999606"
    },
    "amountUSD": "9.9618",
    "timestamp": 1737625204
  },
  "lifiExplorerLink": "https://explorer.li.fi/tx/0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
  "fromAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
  "toAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
  "tool": "across",
  "status": "DONE",
  "substatus": "COMPLETED"
}

Status Values

Primary Status

StatusMeaningAction
NOT_FOUNDTransaction not indexed yetContinue polling
PENDINGTransfer in progressContinue polling
DONETransfer finishedCheck substatus for outcome
FAILEDTransfer failedHandle error

Substatus (when status = DONE)

SubstatusMeaningUser Outcome
COMPLETEDSuccessReceived exact requested token
PARTIALPartial successReceived different token (full value)
REFUNDEDFailed with refundTokens returned to sender

Decision Matrix

IF status === "NOT_FOUND"
  → Wait and poll again (tx may not be indexed yet)

IF status === "PENDING"
  → Wait and poll again (transfer in progress)

IF status === "DONE"
  IF substatus === "COMPLETED"
    → Success! User received toToken on toChain
  
  IF substatus === "PARTIAL"
    → Partial success - see Partial Completion guide
  
  IF substatus === "REFUNDED"
    → Failed - tokens returned to user on fromChain

IF status === "FAILED"
  → Permanent failure - inform user, may need manual recovery

Polling Implementation

AttemptWait TimeTotal Time
1-610 seconds1 minute
7-1230 seconds4 minutes
13-2460 seconds16 minutes
25+120 secondsUntil timeout

JavaScript Implementation

async function pollTransferStatus(params, options = {}) {
  const { txHash, bridge, fromChain, toChain } = params;
  const { 
    maxDuration = 30 * 60 * 1000, // 30 minutes
    onStatusUpdate = () => {} 
  } = options;
  
  const startTime = Date.now();
  let attempt = 0;
  
  while (Date.now() - startTime < maxDuration) {
    attempt++;
    
    try {
      const response = await fetch(
        `https://li.quest/v1/status?txHash=${txHash}&bridge=${bridge}&fromChain=${fromChain}&toChain=${toChain}`
      );
      
      if (!response.ok) {
        throw new Error(`Status API error: ${response.status}`);
      }
      
      const status = await response.json();
      
      // Notify caller of status update
      onStatusUpdate(status, attempt);
      
      // Check for terminal states
      if (status.status === 'DONE') {
        return {
          success: status.substatus === 'COMPLETED',
          partial: status.substatus === 'PARTIAL',
          refunded: status.substatus === 'REFUNDED',
          status
        };
      }
      
      if (status.status === 'FAILED') {
        return {
          success: false,
          failed: true,
          status
        };
      }
      
    } catch (error) {
      console.error(`Polling error (attempt ${attempt}):`, error.message);
      // Continue polling despite errors
    }
    
    // Calculate wait time with backoff
    const waitTime = getWaitTime(attempt);
    await sleep(waitTime);
  }
  
  throw new Error('Status polling timeout');
}

function getWaitTime(attempt) {
  if (attempt <= 6) return 10000;      // 10 seconds
  if (attempt <= 12) return 30000;     // 30 seconds
  if (attempt <= 24) return 60000;     // 60 seconds
  return 120000;                        // 2 minutes
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Python Implementation

import time
import requests

def poll_transfer_status(tx_hash, bridge, from_chain, to_chain, 
                         max_duration=1800, on_update=None):
    """
    Poll transfer status until completion or timeout.
    
    Args:
        tx_hash: Transaction hash
        bridge: Bridge tool name
        from_chain: Source chain ID
        to_chain: Destination chain ID
        max_duration: Maximum polling duration in seconds (default 30 min)
        on_update: Optional callback for status updates
    
    Returns:
        dict with success, partial, refunded, failed flags and status
    """
    start_time = time.time()
    attempt = 0
    
    while time.time() - start_time < max_duration:
        attempt += 1
        
        try:
            url = (
                f"https://li.quest/v1/status"
                f"?txHash={tx_hash}"
                f"&bridge={bridge}"
                f"&fromChain={from_chain}"
                f"&toChain={to_chain}"
            )
            
            response = requests.get(url)
            response.raise_for_status()
            status = response.json()
            
            # Notify callback
            if on_update:
                on_update(status, attempt)
            
            # Check terminal states
            if status.get('status') == 'DONE':
                substatus = status.get('substatus')
                return {
                    'success': substatus == 'COMPLETED',
                    'partial': substatus == 'PARTIAL',
                    'refunded': substatus == 'REFUNDED',
                    'status': status
                }
            
            if status.get('status') == 'FAILED':
                return {
                    'success': False,
                    'failed': True,
                    'status': status
                }
                
        except Exception as e:
            print(f"Polling error (attempt {attempt}): {e}")
        
        # Wait with backoff
        wait_time = get_wait_time(attempt)
        time.sleep(wait_time)
    
    raise Exception('Status polling timeout')


def get_wait_time(attempt):
    if attempt <= 6:
        return 10
    if attempt <= 12:
        return 30
    if attempt <= 24:
        return 60
    return 120

Handling Each Outcome

COMPLETED - Success

if (result.success) {
  const { receiving } = result.status;
  
  console.log('Transfer successful!');
  console.log(`Received: ${receiving.amount} ${receiving.token.symbol}`);
  console.log(`On chain: ${receiving.chainId}`);
  console.log(`Tx: ${receiving.txHash}`);
  
  // No further action needed
}

PARTIAL - Different Token Received

if (result.partial) {
  const { receiving } = result.status;
  
  console.log('Transfer completed with different token');
  console.log(`Received: ${receiving.amount} ${receiving.token.symbol}`);
  
  // User may want to swap to their intended token
  // See: Partial Completion guide
}

REFUNDED - Tokens Returned

if (result.refunded) {
  const { sending } = result.status;
  
  console.log('Transfer failed - tokens refunded');
  console.log(`Refunded: ${sending.amount} ${sending.token.symbol}`);
  console.log(`On chain: ${sending.chainId}`);
  
  // User's tokens are back on the source chain
  // May retry with different parameters
}

FAILED - Permanent Failure

if (result.failed) {
  console.error('Transfer failed permanently');
  console.error('Error:', result.status.error);
  
  // May need manual intervention
  // Contact support with transaction details
}

Estimated Transfer Times

The quote response includes estimate.executionDuration (in seconds) which gives you the expected transfer time for that specific route. Always use this value rather than general estimates.
Transfer times vary significantly based on the bridge used and network conditions. The executionDuration field in the quote response provides the most accurate estimate for each specific route.

Timeout Handling

If polling times out:
  1. Don’t assume failure - The transfer may still complete
  2. Save the transaction details - txHash, bridge, fromChain, toChain
  3. Provide manual check - User can check status later
  4. Link to explorer - Provide transaction explorer URLs
function getExplorerUrl(chainId, txHash) {
  const explorers = {
    1: 'https://etherscan.io/tx/',
    42161: 'https://arbiscan.io/tx/',
    10: 'https://optimistic.etherscan.io/tx/',
    137: 'https://polygonscan.com/tx/',
    8453: 'https://basescan.org/tx/'
  };
  
  return `${explorers[chainId] || 'https://blockscan.com/tx/'}${txHash}`;
}

// On timeout
console.log('Status check timed out. The transfer may still be processing.');
console.log(`Check source tx: ${getExplorerUrl(fromChain, txHash)}`);
console.log('You can also check status manually at: https://li.fi/');