Docs

spotClearinghouseState - HyperCore Info Endpoint

Get spot trading account state and token balances for any user on Hyperliquid, including available and held balances.

Get spot trading account state and token balances for a user on Hyperliquid.

Why Hyperliquid? Build on the trading-focused EVM and HyperCore ecosystem built for onchain perpetuals and market data with HyperCore market structure, sub-second finality, and direct access to trading-focused data services.

Authenticate HyperCore Info requests by sending your Dwellir API key in the x-api-key header to https://api-hyperliquid-mainnet-info.n.dwellir.com/info.

When to Use This Endpoint

The spotClearinghouseState endpoint is essential for spot traders, portfolio managers, and trading platforms who need to:

  • Track Token Balances — Monitor available and held balances across all tokens
  • Portfolio Management — Calculate total portfolio value across spot holdings
  • Trading Validation — Verify sufficient balance before placing spot orders
  • Liquidity Management — Track which funds are locked in open orders

Common Use Cases

1. Check Available Balance

Calculate available balance for a specific token:

JavaScript
async function getAvailableBalance(userAddress, token) {
  const state = await getSpotClearinghouseState(userAddress);
  const balance = state.balances.find(b => b.coin === token);

  if (!balance) {
    return {
      token: token,
      available: 0,
      total: 0,
      hold: 0,
      exists: false
    };
  }

  const total = parseFloat(balance.total);
  const hold = parseFloat(balance.hold);
  const available = total - hold;

  return {
    token: token,
    available: available,
    total: total,
    hold: hold,
    exists: true
  };
}

// Usage
const usdcBalance = await getAvailableBalance(
  '0x63E8c7C149556D5f34F833419A287bb9Ef81487f',
  'USDC'
);

if (usdcBalance.available < 1000) {
  console.log('Insufficient USDC balance for trade');
}

2. Portfolio Value Calculator

Calculate total portfolio value in USD:

JavaScript
async function calculatePortfolioValue(userAddress, prices) {
  const state = await getSpotClearinghouseState(userAddress);

  let totalValue = 0;
  const holdings = [];

  state.balances.forEach(balance => {
    const total = parseFloat(balance.total);
    if (total === 0) return;

    const price = prices[balance.coin] || 0;
    const value = total * price;
    totalValue += value;

    holdings.push({
      token: balance.coin,
      amount: total,
      price: price,
      value: value,
      percentage: 0 // Will calculate after
    });
  });

  // Calculate percentages
  holdings.forEach(h => {
    h.percentage = (h.value / totalValue) * 100;
  });

  // Sort by value
  holdings.sort((a, b) => b.value - a.value);

  return {
    totalValue: totalValue,
    holdings: holdings,
    tokenCount: holdings.length
  };
}

// Usage with example prices
const prices = { USDC: 1, ETH: 2500, BTC: 45000 };
const portfolio = await calculatePortfolioValue(
  '0x63E8c7C149556D5f34F833419A287bb9Ef81487f',
  prices
);

console.log(`Total Portfolio Value: $${portfolio.totalValue.toFixed(2)}`);
portfolio.holdings.forEach(h => {
  console.log(`${h.token}: $${h.value.toFixed(2)} (${h.percentage.toFixed(1)}%)`);
});

3. Validate Trade Feasibility

Check if user has sufficient balance for a trade:

JavaScript
async function canExecuteTrade(userAddress, token, requiredAmount) {
  const balance = await getAvailableBalance(userAddress, token);

  if (!balance.exists) {
    return {
      canTrade: false,
      reason: `No ${token} balance found`,
      available: 0,
      required: requiredAmount
    };
  }

  if (balance.available < requiredAmount) {
    return {
      canTrade: false,
      reason: 'Insufficient balance',
      available: balance.available,
      required: requiredAmount,
      shortfall: requiredAmount - balance.available
    };
  }

  return {
    canTrade: true,
    available: balance.available,
    required: requiredAmount,
    remaining: balance.available - requiredAmount
  };
}

// Usage
const tradeCheck = await canExecuteTrade(
  '0x63E8c7C149556D5f34F833419A287bb9Ef81487f',
  'USDC',
  1000
);

if (!tradeCheck.canTrade) {
  console.error(`Cannot trade: ${tradeCheck.reason}`);
  console.error(`Required: ${tradeCheck.required}, Available: ${tradeCheck.available}`);
}

4. Monitor Locked Balances

Track which tokens have funds locked in orders:

JavaScript
async function getLockedBalances(userAddress) {
  const state = await getSpotClearinghouseState(userAddress);

  const lockedBalances = state.balances
    .filter(b => parseFloat(b.hold) > 0)
    .map(b => ({
      token: b.coin,
      total: parseFloat(b.total),
      hold: parseFloat(b.hold),
      available: parseFloat(b.total) - parseFloat(b.hold),
      percentLocked: (parseFloat(b.hold) / parseFloat(b.total)) * 100
    }));

  return lockedBalances;
}

// Usage
const locked = await getLockedBalances('0x63E8c7C149556D5f34F833419A287bb9Ef81487f');
console.log(`${locked.length} tokens with locked funds:\n`);

locked.forEach(l => {
  console.log(`${l.token}:`);
  console.log(`  Locked: ${l.hold} (${l.percentLocked.toFixed(1)}%)`);
  console.log(`  Available: ${l.available}`);
});

5. Balance Summary Report

Generate a comprehensive balance report:

JavaScript
async function getBalanceSummary(userAddress) {
  const state = await getSpotClearinghouseState(userAddress);

  console.log('\n=== Spot Balance Summary ===\n');

  const nonZeroBalances = state.balances.filter(b => parseFloat(b.total) > 0);
  const lockedBalances = state.balances.filter(b => parseFloat(b.hold) > 0);

  console.log(`Total Tokens: ${nonZeroBalances.length}`);
  console.log(`Tokens with Orders: ${lockedBalances.length}\n`);

  console.log('Balances:');
  nonZeroBalances.forEach(balance => {
    const total = parseFloat(balance.total);
    const hold = parseFloat(balance.hold);
    const available = total - hold;

    console.log(`\n${balance.coin}:`);
    console.log(`  Total: ${balance.total}`);
    console.log(`  Available: ${available.toFixed(8)}`);
    if (hold > 0) {
      console.log(`  On Hold: ${balance.hold} ⚠️`);
    }
  });
}

Best Practices

  1. Poll responsibly — Don't poll more frequently than needed (every 5-10 seconds for active trading)
  2. Handle zero balances — Token may exist with zero balance
  3. Check available balance — Always calculate total - hold before placing orders
  4. Validate addresses — Ensure user addresses are valid Ethereum addresses
  5. Cache when appropriate — Cache balance data briefly to reduce API calls

Access real-time Hyperliquid spot balances with Dwellir's HyperCore Info Endpoint. Get your API key →