Skip to main content
Beta: The Wallet API is currently in beta. APIs and response formats may change.

Overview

The Token Transfers endpoint retrieves all token transfer activity for a Solana wallet, including detailed sender/recipient information. Unlike the full transaction history, this endpoint focuses specifically on transfers, making it ideal for payment tracking and transfer monitoring.

API Reference

View detailed API documentation for token transfers

When to Use This

Use the Token Transfers API when you need to:
  • Track Payments: Monitor incoming payments for payment processors
  • Build Transfer Feed: Display a simple “sent/received” activity feed
  • Monitor Specific Tokens: Track transfers of a specific token (e.g., USDC payments)
  • Identify Counterparties: See who sent or received tokens
  • Generate Receipts: Create payment receipts with sender/recipient details
  • Detect Suspicious Activity: Monitor unusual transfer patterns

Quickstart

Basic Transfers Query

Get recent incoming and outgoing transfers:
const getWalletTransfers = async (address) => {
  const url = `https://api.helius.xyz/v1/wallet/${address}/transfers?api-key=YOUR_API_KEY`;

  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const data = await response.json();

  console.log(`Found ${data.data.length} transfers`);

  // Display recent transfers
  data.data.forEach(transfer => {
    const date = new Date(transfer.timestamp * 1000).toLocaleString();
    const direction = transfer.direction === 'in' ? 'Received' : 'Sent';
    const counterparty = transfer.counterparty.slice(0, 8) + '...';

    console.log(`\n${direction} - ${date}`);
    console.log(`Amount: ${transfer.amount} ${transfer.symbol || transfer.mint.slice(0, 8) + '...'}`);
    console.log(`${transfer.direction === 'in' ? 'From' : 'To'}: ${counterparty}`);
    console.log(`Signature: ${transfer.signature.slice(0, 20)}...`);
  });

  return data;
};

getWalletTransfers("86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY");

Filter by Direction

Get only incoming or outgoing transfers:
const getIncomingTransfers = async (address) => {
  const data = await getWalletTransfers(address);

  const incoming = data.data.filter(t => t.direction === 'in');

  console.log(`Received ${incoming.length} incoming transfers`);

  incoming.forEach(transfer => {
    console.log(`Received ${transfer.amount} ${transfer.symbol} from ${transfer.counterparty.slice(0, 8)}...`);
  });

  return incoming;
};

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Maximum number of transfers to return (1-100)
cursorstring-Pagination cursor from previous response

Response Format

{
  "data": [
    {
      "signature": "5wHu1qwD7Jsj3xqWjdSEJmYr3Q5f5RjXqjqQJ7jqEj7jqEj7jqEj7jqEj7jqEj7jqE",
      "timestamp": 1704067200,
      "direction": "in",
      "counterparty": "HXsKP7wrBWaQ8T2Vtjry3Nj3oUgwYcqq9vrHDM12G664",
      "mint": "So11111111111111111111111111111111111111112",
      "symbol": "SOL",
      "amount": 1.5,
      "amountRaw": "1500000000",
      "decimals": 9
    },
    {
      "signature": "4aHu2qwD8Jtj4xqWjdSEJmYr3Q5f5RjXqjqQJ7jqEj7jqEj7jqEj7jqEj7jqEj7jqE",
      "timestamp": 1704067100,
      "direction": "out",
      "counterparty": "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S",
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "symbol": "USDC",
      "amount": 100.0,
      "amountRaw": "100000000",
      "decimals": 6
    }
  ],
  "pagination": {
    "hasMore": true,
    "nextCursor": "5wHu1qwD7Jsj3xqWjdSEJmYr3Q5f5RjXqjqQJ7jqEj7jqEj7jqEj7jqEj7jqEj7jqE"
  }
}
  • amount: Human-readable transfer amount (divided by decimals)
  • amountRaw: Raw transfer amount in smallest unit (lamports for SOL, raw token amount for SPL tokens)
  • mint: Token mint address (So11111111111111111111111111111111111111112 for SOL)

Understanding Direction

The direction field is relative to the wallet you’re querying:
  • in: Tokens received by the wallet (incoming payment)
  • out: Tokens sent by the wallet (outgoing payment)
The counterparty field is:
  • For in transfers: The sender (who sent tokens to this wallet)
  • For out transfers: The recipient (who received tokens from this wallet)

Use Cases

Track Payment History for a Merchant

Monitor incoming USDC payments:
const trackMerchantPayments = async (merchantWallet) => {
  const data = await getWalletTransfers(merchantWallet);

  // Filter for incoming USDC transfers
  const usdcPayments = data.data.filter(t =>
    t.direction === 'in' &&
    t.mint === 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
  );

  console.log(`Received ${usdcPayments.length} USDC payments`);

  const totalReceived = usdcPayments.reduce((sum, t) => sum + t.amount, 0);
  console.log(`Total USDC Received: $${totalReceived.toFixed(2)}`);

  // Display each payment
  usdcPayments.forEach(payment => {
    const date = new Date(payment.timestamp * 1000).toLocaleString();
    console.log(`${date}: $${payment.amount} from ${payment.counterparty}`);
  });

  return {
    count: usdcPayments.length,
    total: totalReceived,
    payments: usdcPayments
  };
};

Generate Payment Receipt

Create a detailed receipt for a specific transfer:
const generatePaymentReceipt = async (address, signature) => {
  const data = await getWalletTransfers(address);

  const transfer = data.data.find(t => t.signature === signature);

  if (!transfer) {
    console.log('Transfer not found');
    return null;
  }

  const receipt = {
    receiptId: transfer.signature.slice(0, 16),
    date: new Date(transfer.timestamp * 1000).toISOString(),
    type: transfer.direction === 'in' ? 'Payment Received' : 'Payment Sent',
    amount: `${transfer.amount} ${transfer.symbol || 'tokens'}`,
    from: transfer.direction === 'in' ? transfer.counterparty : address,
    to: transfer.direction === 'out' ? transfer.counterparty : address,
    transactionUrl: `https://orbmarkets.io/tx/${transfer.signature}`
  };

  console.log('--- PAYMENT RECEIPT ---');
  Object.entries(receipt).forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
  });

  return receipt;
};

Monitor Suspicious Transfer Patterns

Detect unusual transfer activity:
const detectSuspiciousActivity = async (address) => {
  const data = await getWalletTransfers(address);

  const recentTransfers = data.data.filter(t => {
    const hourAgo = Date.now() / 1000 - 3600;
    return t.timestamp > hourAgo;
  });

  // Check for high frequency
  if (recentTransfers.length > 100) {
    console.log(`Warning: ${recentTransfers.length} transfers in the last hour`);
  }

  // Check for large amounts
  const largeTransfers = recentTransfers.filter(t => {
    // Assuming USDC/stablecoins
    return t.amount > 10000 && t.decimals === 6;
  });

  if (largeTransfers.length > 0) {
    console.log(`Warning: ${largeTransfers.length} large transfers (>$10k) in the last hour`);
  }

  // Check for transfers to same address
  const counterparties = recentTransfers.map(t => t.counterparty);
  const duplicates = counterparties.filter((item, index) => counterparties.indexOf(item) !== index);

  if (duplicates.length > 5) {
    console.log(`Warning: Multiple transfers to the same address`);
  }

  return {
    recentCount: recentTransfers.length,
    largeTransfers: largeTransfers.length,
    suspiciousPatterns: duplicates.length > 5
  };
};

Build Transfer Activity Feed

Create a user-friendly activity feed:
const buildTransferFeed = async (address) => {
  const data = await getWalletTransfers(address);

  const feed = data.data.map(transfer => {
    const date = new Date(transfer.timestamp * 1000);
    const timeAgo = getTimeAgo(date);

    return {
      id: transfer.signature,
      icon: transfer.direction === 'in' ? '📥' : '📤',
      title: transfer.direction === 'in' ? 'Received' : 'Sent',
      subtitle: `${transfer.amount} ${transfer.symbol || 'tokens'}`,
      description: transfer.direction === 'in'
        ? `from ${transfer.counterparty.slice(0, 8)}...`
        : `to ${transfer.counterparty.slice(0, 8)}...`,
      timeAgo,
      explorerUrl: `https://orbmarkets.io/tx/${transfer.signature}`
    };
  });

  return feed;
};

function getTimeAgo(date) {
  const seconds = Math.floor((new Date() - date) / 1000);

  if (seconds < 60) return 'Just now';
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
  return `${Math.floor(seconds / 86400)}d ago`;
}

Reconcile Payments

Match transfers against expected payments:
const reconcilePayments = async (address, expectedPayments) => {
  const data = await getWalletTransfers(address);

  const recentTransfers = data.data.filter(t =>
    t.direction === 'in' &&
    t.mint === 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC
  );

  const reconciliation = expectedPayments.map(expected => {
    const match = recentTransfers.find(t =>
      Math.abs(t.amount - expected.amount) < 0.01 &&
      t.counterparty === expected.from
    );

    return {
      orderId: expected.orderId,
      expectedAmount: expected.amount,
      status: match ? 'Received' : 'Pending',
      receivedAmount: match?.amount,
      signature: match?.signature,
      timestamp: match?.timestamp
    };
  });

  console.log('Payment Reconciliation:');
  reconciliation.forEach(r => {
    console.log(`Order ${r.orderId}: ${r.status}`);
  });

  return reconciliation;
};

// Example usage
const expected = [
  { orderId: 'ORDER-001', amount: 100.00, from: 'ABC...' },
  { orderId: 'ORDER-002', amount: 250.50, from: 'XYZ...' }
];

reconcilePayments("86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY", expected);

Pagination

For wallets with many transfers, use pagination to fetch all results:
const getAllTransfers = async (address) => {
  let allTransfers = [];
  let cursor = null;

  do {
    const url = cursor
      ? `https://api.helius.xyz/v1/wallet/${address}/transfers?api-key=YOUR_API_KEY&cursor=${cursor}`
      : `https://api.helius.xyz/v1/wallet/${address}/transfers?api-key=YOUR_API_KEY`;

    const response = await fetch(url);
    const data = await response.json();

    allTransfers = allTransfers.concat(data.data);
    cursor = data.pagination.hasMore ? data.pagination.nextCursor : null;

    console.log(`Fetched ${allTransfers.length} transfers so far...`);

  } while (cursor);

  console.log(`\nTotal transfers: ${allTransfers.length}`);
  return allTransfers;
};

Best Practices

The API returns all token transfers. Filter by mint address client-side to track specific tokens like USDC or SOL.
Use the Identity API to show human-readable names for known counterparties (exchanges, protocols, etc.).
Transfer data doesn’t change. Cache results and only fetch new transfers since your last query.
Implement pagination to handle wallets with thousands of transfers efficiently.
Not all tokens have a symbol field. Fall back to displaying the mint address when symbol is null.

Transfers vs Transaction History

FeatureTransfersTransaction History
FocusOnly token transfersAll transaction types
DataSender/recipient infoBalance changes for all tokens
Use CasePayment trackingComplete activity log
PerformanceFaster, simplerMore comprehensive
Use Transfers when you only care about payments. Use Transaction History when you need complete balance change data.

Common Errors

Error CodeDescriptionSolution
400Invalid wallet address formatVerify the address is a valid base58 Solana address
401Missing or invalid API keyCheck your API key is included in the request
429Rate limit exceededReduce request frequency or upgrade your plan

Need Help?