spotClearinghouseState
Get spot trading account state and token balances for a user on Hyperliquid.
Why Hyperliquid? Build on the dominant perpetuals DEX with 70% market share, $2.7T+ lifetime volume, and $2B TVL with 200K orders/second throughput, zero gas fees, sub-second finality, and fully onchain Central Limit Order Book (CLOB).
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
Request#
Endpoint#
POST https://api-hyperliquid-mainnet-info.n.dwellir.com/info
Headers#
| Header | Value | Required |
|---|---|---|
Content-Type | application/json | Yes |
X-Api-Key | Your API key | Yes |
Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "spotClearinghouseState" |
user | string | Yes | User's Ethereum wallet address |
Example Request#
{
"type": "spotClearinghouseState",
"user": "0x63E8c7C149556D5f34F833419A287bb9Ef81487f"
}
Response#
Success Response#
{
"balances": [
{
"coin": "USDC",
"token": 0,
"total": "0.01261784",
"hold": "0.0",
"entryNtl": "0.0"
},
{
"coin": "HYPE",
"token": 150,
"total": "0.00674734",
"hold": "0.0",
"entryNtl": "0.24186439"
}
]
}
Response Fields#
| Field | Type | Description |
|---|---|---|
balances | array | Array of token balance objects |
Balance Object#
| Field | Type | Description |
|---|---|---|
coin | string | Token symbol (e.g., "USDC", "HYPE", "ETH") |
token | integer | Token index/ID |
total | string | Total balance for this token |
hold | string | Amount currently held in open spot orders |
entryNtl | string | Entry notional value |
Note: Available balance = total - hold
Code Examples#
- cURL
- JavaScript
- Python
- Go
curl -X POST 'https://api-hyperliquid-mainnet-info.n.dwellir.com/info' \
-H 'X-Api-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"type": "spotClearinghouseState",
"user": "0x63E8c7C149556D5f34F833419A287bb9Ef81487f"
}'
const ENDPOINT = 'https://api-hyperliquid-mainnet-info.n.dwellir.com/info';
const API_KEY = 'your-api-key-here';
async function getSpotClearinghouseState(userAddress) {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': API_KEY
},
body: JSON.stringify({
type: 'spotClearinghouseState',
user: userAddress
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
// Usage
const state = await getSpotClearinghouseState('0x63E8c7C149556D5f34F833419A287bb9Ef81487f');
console.log(`Total balances: ${state.balances.length} tokens`);
// Display balances with available amounts
state.balances.forEach(balance => {
const total = parseFloat(balance.total);
const hold = parseFloat(balance.hold);
const available = total - hold;
console.log(`${balance.coin}:`);
console.log(` Total: ${balance.total}`);
console.log(` Available: ${available.toFixed(8)}`);
console.log(` On Hold: ${balance.hold}`);
});
// Find specific token
const usdcBalance = state.balances.find(b => b.coin === 'USDC');
if (usdcBalance) {
console.log(`USDC Balance: ${usdcBalance.total}`);
}
import requests
from typing import Dict
from decimal import Decimal
ENDPOINT = 'https://api-hyperliquid-mainnet-info.n.dwellir.com/info'
API_KEY = 'your-api-key-here'
def get_spot_clearinghouse_state(user_address: str) -> Dict:
"""Get user's spot trading account state"""
response = requests.post(
ENDPOINT,
json={
'type': 'spotClearinghouseState',
'user': user_address
},
headers={
'Content-Type': 'application/json',
'X-Api-Key': API_KEY
},
timeout=10
)
response.raise_for_status()
return response.json()
# Usage
state = get_spot_clearinghouse_state('0x63E8c7C149556D5f34F833419A287bb9Ef81487f')
print(f"Total balances: {len(state['balances'])} tokens\n")
# Display balances
for balance in state['balances']:
total = Decimal(balance['total'])
hold = Decimal(balance['hold'])
available = total - hold
print(f"{balance['coin']}:")
print(f" Total: {balance['total']}")
print(f" Available: {available}")
print(f" On Hold: {balance['hold']}")
print()
# Find tokens with non-zero balances
non_zero = [b for b in state['balances'] if Decimal(b['total']) > 0]
print(f"\nTokens with balance: {len(non_zero)}")
# Calculate tokens locked in orders
locked_tokens = [b for b in state['balances'] if Decimal(b['hold']) > 0]
print(f"Tokens with orders: {len(locked_tokens)}")
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const (
Endpoint = "https://api-hyperliquid-mainnet-info.n.dwellir.com/info"
APIKey = "your-api-key-here"
)
type SpotClearinghouseRequest struct {
Type string `json:"type"`
User string `json:"user"`
}
type Balance struct {
Coin string `json:"coin"`
Total string `json:"total"`
Hold string `json:"hold"`
}
type SpotClearinghouseState struct {
Balances []Balance `json:"balances"`
}
func getSpotClearinghouseState(userAddress string) (*SpotClearinghouseState, error) {
reqBody, _ := json.Marshal(SpotClearinghouseRequest{
Type: "spotClearinghouseState",
User: userAddress,
})
req, _ := http.NewRequest("POST", Endpoint, bytes.NewBuffer(reqBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", APIKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result SpotClearinghouseState
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
func main() {
state, err := getSpotClearinghouseState("0x63E8c7C149556D5f34F833419A287bb9Ef81487f")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Total balances: %d tokens\n\n", len(state.Balances))
for _, balance := range state.Balances {
fmt.Printf("%s:\n", balance.Coin)
fmt.Printf(" Total: %s\n", balance.Total)
fmt.Printf(" On Hold: %s\n\n", balance.Hold)
}
}
Common Use Cases#
1. Check Available Balance#
Calculate available balance for a specific token:
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:
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:
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:
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:
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} ⚠️`);
}
});
}
Error Handling#
Common Errors#
| Error | Cause | Solution |
|---|---|---|
401 Unauthorized | Invalid API key | Verify your API key is correct |
400 Bad Request | Missing or invalid user address | Ensure valid Ethereum address format |
429 Too Many Requests | Rate limit exceeded | Implement request throttling |
500 Internal Server Error | Server issue | Retry with exponential backoff |
Error Response Example#
{
"error": "Missing required parameter: user",
"code": "MISSING_PARAMETER"
}
Robust Error Handling#
async function safeGetSpotClearinghouseState(userAddress, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await getSpotClearinghouseState(userAddress);
} catch (error) {
if (error.response?.status === 429) {
// Rate limit - exponential backoff
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
} else if (error.response?.status === 400) {
throw new Error('Invalid user address');
} else if (i === maxRetries - 1) {
throw error;
}
}
}
}
Best Practices#
- Poll responsibly — Don't poll more frequently than needed (every 5-10 seconds for active trading)
- Handle zero balances — Token may exist with zero balance
- Check available balance — Always calculate
total - holdbefore placing orders - Validate addresses — Ensure user addresses are valid Ethereum addresses
- Cache when appropriate — Cache balance data briefly to reduce API calls
Related Endpoints#
- clearinghouseState — Get perpetual account state
- openOrders — See which orders are holding funds
- spotMeta — Get spot trading asset metadata
- userFees — Get user fee rates for trading
Access real-time Hyperliquid spot balances with Dwellir's HyperCore Info Endpoint. Get your API key →