batchClearinghouseStates
Batch query perpetual account states for multiple users in a single request. This is a custom endpoint exclusive to Dwellir's HyperCore REST API — it is not available on standard Hyperliquid nodes.
When to Use This Endpoint#
The batchClearinghouseStates endpoint is designed for applications that need account state for many users at once. It fans out individual clearinghouseState calls concurrently and aggregates the results, so you don't need to manage your own fan-out and error handling.
- Portfolio Dashboards — Fetch account states for all tracked wallets in one call
- Risk Monitoring — Monitor margin health across a fleet of trading accounts
- Fund Management — Aggregate position data across multiple sub-accounts or strategies
- Multi-DEX Analytics — Query all DEXes for each user with a single
ALL_DEXESrequest
Request#
Endpoint#
POST https://api-hyperliquid-mainnet-info.n.dwellir.com/API_KEY/info
Headers#
| Header | Value | Required |
|---|---|---|
Content-Type | application/json | Yes |
Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "batchClearinghouseStates" |
users | string[] | Yes | Non-empty list of wallet addresses |
dex | string | No | DEX name. Omit for native DEX. Set to "ALL_DEXES" to query all DEXes. |
User Limits#
| Mode | Max Users | Description |
|---|---|---|
Single-DEX (no dex or a named DEX) | 1000 (default) | One upstream call per user |
ALL_DEXES | 100 (default) | Lower limit because total calls = users × DEXes |
Example Requests#
Native DEX (no dex field):
{
"type": "batchClearinghouseStates",
"users": [
"0x00f2548cf639e54420e501a35346e8458989e6bd",
"0x010461c14e146ac35fe42271bdc1134ee31c703a",
"0x02159593e155250288a7a1dd5257fc217601ceee"
]
}
Named DEX:
{
"type": "batchClearinghouseStates",
"users": [
"0x00f2548cf639e54420e501a35346e8458989e6bd",
"0x010461c14e146ac35fe42271bdc1134ee31c703a"
],
"dex": "xyz"
}
All DEXes:
{
"type": "batchClearinghouseStates",
"users": [
"0x00f2548cf639e54420e501a35346e8458989e6bd",
"0x010461c14e146ac35fe42271bdc1134ee31c703a"
],
"dex": "ALL_DEXES"
}
Response — Single DEX#
Both fields are always present. Order matches input order.
| Field | Type | Description |
|---|---|---|
successful_states | [address, state][] | Array of two-element tuples. address is the wallet address; state is the raw upstream clearinghouseState object. |
failed_wallets | string[] | Addresses whose upstream call failed (timeout, non-200, etc.). |
Success Response#
{
"successful_states": [
[
"0x00f2548cf639e54420e501a35346e8458989e6bd",
{
"marginSummary": {
"accountValue": "0.0",
"totalNtlPos": "0.0",
"totalRawUsd": "0.0",
"totalMarginUsed": "0.0"
},
"crossMarginSummary": {
"accountValue": "0.0",
"totalNtlPos": "0.0",
"totalRawUsd": "0.0",
"totalMarginUsed": "0.0"
},
"crossMaintenanceMarginUsed": "0.0",
"withdrawable": "0.0",
"assetPositions": [],
"time": 1771587392044
}
],
[
"0x010461c14e146ac35fe42271bdc1134ee31c703a",
{
"marginSummary": {
"accountValue": "0.0",
"totalNtlPos": "0.0",
"totalRawUsd": "0.0",
"totalMarginUsed": "0.0"
},
"crossMarginSummary": {
"accountValue": "0.0",
"totalNtlPos": "0.0",
"totalRawUsd": "0.0",
"totalMarginUsed": "0.0"
},
"crossMaintenanceMarginUsed": "0.0",
"withdrawable": "0.0",
"assetPositions": [],
"time": 1771587392044
}
]
],
"failed_wallets": []
}
Partial Failure Response#
If some calls fail, those addresses appear in failed_wallets:
{
"successful_states": [
[
"0x00f2548cf639e54420e501a35346e8458989e6bd",
{
"marginSummary": { "accountValue": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0", "totalMarginUsed": "0.0" },
"crossMarginSummary": { "accountValue": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0", "totalMarginUsed": "0.0" },
"crossMaintenanceMarginUsed": "0.0",
"withdrawable": "0.0",
"assetPositions": [],
"time": 1771587392044
}
]
],
"failed_wallets": ["0x010461c14e146ac35fe42271bdc1134ee31c703a"]
}
Response — ALL_DEXES#
When dex is set to "ALL_DEXES", the response shape changes. Each user's state is an object keyed by DEX name.
| Field | Type | Description |
|---|---|---|
successful_states | [address, dex_map][] | Array of two-element tuples. dex_map is an object keyed by DEX name, each value being the clearinghouseState for that DEX. |
failed_wallets | string[] | Addresses where all DEX calls failed. |
Key semantics:
- The native DEX appears under the key
"native". - Partial success: if some DEX calls succeed and others fail for a user, the user is included in
successful_stateswith only the DEXes that responded. They do not appear infailed_wallets. - DEX truncation: if the chain reports more DEXes than
max_dexes, the list is silently truncated. The response may not cover every DEX on the chain.
ALL_DEXES Response Example#
{
"successful_states": [
[
"0x00f2548cf639e54420e501a35346e8458989e6bd",
{
"native": {
"marginSummary": { "accountValue": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0", "totalMarginUsed": "0.0" },
"crossMarginSummary": { "accountValue": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0", "totalMarginUsed": "0.0" },
"crossMaintenanceMarginUsed": "0.0",
"withdrawable": "0.0",
"assetPositions": [],
"time": 1771587392044
},
"xyz": {
"marginSummary": { "accountValue": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0", "totalMarginUsed": "0.0" },
"crossMarginSummary": { "accountValue": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0", "totalMarginUsed": "0.0" },
"crossMaintenanceMarginUsed": "0.0",
"withdrawable": "0.0",
"assetPositions": [],
"time": 1771587392044
}
}
]
],
"failed_wallets": ["0x010461c14e146ac35fe42271bdc1134ee31c703a"]
}
Code Examples#
- cURL
- JavaScript
- Python
- Go
curl -X POST 'https://api-hyperliquid-mainnet-info.n.dwellir.com/API_KEY/info' \
-H 'Content-Type: application/json' \
-d '{
"type": "batchClearinghouseStates",
"users": [
"0x00f2548cf639e54420e501a35346e8458989e6bd",
"0x010461c14e146ac35fe42271bdc1134ee31c703a",
"0x02159593e155250288a7a1dd5257fc217601ceee"
]
}'
const ENDPOINT = 'https://api-hyperliquid-mainnet-info.n.dwellir.com/API_KEY/info';
async function getBatchClearinghouseStates(users, dex) {
const payload = {
type: 'batchClearinghouseStates',
users
};
if (dex) payload.dex = dex;
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
// Single-DEX usage
const result = await getBatchClearinghouseStates([
'0x00f2548cf639e54420e501a35346e8458989e6bd',
'0x010461c14e146ac35fe42271bdc1134ee31c703a'
]);
console.log(`Successful: ${result.successful_states.length}`);
console.log(`Failed: ${result.failed_wallets.length}`);
for (const [address, state] of result.successful_states) {
console.log(`${address}: $${state.marginSummary.accountValue}`);
}
// ALL_DEXES usage
const allDexResult = await getBatchClearinghouseStates(
['0x00f2548cf639e54420e501a35346e8458989e6bd'],
'ALL_DEXES'
);
for (const [address, dexMap] of allDexResult.successful_states) {
for (const [dexName, state] of Object.entries(dexMap)) {
console.log(`${address} [${dexName}]: $${state.marginSummary.accountValue}`);
}
}
import requests
from typing import Dict, List, Optional
ENDPOINT = 'https://api-hyperliquid-mainnet-info.n.dwellir.com/API_KEY/info'
def get_batch_clearinghouse_states(
users: List[str],
dex: Optional[str] = None
) -> Dict:
"""Get clearinghouse states for multiple users in one request"""
payload = {
'type': 'batchClearinghouseStates',
'users': users
}
if dex:
payload['dex'] = dex
response = requests.post(
ENDPOINT,
json=payload,
headers={
'Content-Type': 'application/json'
},
timeout=30
)
response.raise_for_status()
return response.json()
# Single-DEX usage
result = get_batch_clearinghouse_states([
'0x00f2548cf639e54420e501a35346e8458989e6bd',
'0x010461c14e146ac35fe42271bdc1134ee31c703a'
])
print(f"Successful: {len(result['successful_states'])}")
print(f"Failed: {len(result['failed_wallets'])}")
for address, state in result['successful_states']:
margin = state['marginSummary']
print(f"{address}: ${margin['accountValue']}")
# ALL_DEXES usage
all_dex_result = get_batch_clearinghouse_states(
['0x00f2548cf639e54420e501a35346e8458989e6bd'],
dex='ALL_DEXES'
)
for address, dex_map in all_dex_result['successful_states']:
for dex_name, state in dex_map.items():
print(f"{address} [{dex_name}]: ${state['marginSummary']['accountValue']}")
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const Endpoint = "https://api-hyperliquid-mainnet-info.n.dwellir.com/API_KEY/info"
type BatchClearinghouseRequest struct {
Type string `json:"type"`
Users []string `json:"users"`
Dex string `json:"dex,omitempty"`
}
type BatchClearinghouseResponse struct {
SuccessfulStates []json.RawMessage `json:"successful_states"`
FailedWallets []string `json:"failed_wallets"`
}
func getBatchClearinghouseStates(users []string, dex string) (*BatchClearinghouseResponse, error) {
reqBody, _ := json.Marshal(BatchClearinghouseRequest{
Type: "batchClearinghouseStates",
Users: users,
Dex: dex,
})
req, _ := http.NewRequest("POST", Endpoint, bytes.NewBuffer(reqBody))
req.Header.Set("Content-Type", "application/json")
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 BatchClearinghouseResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
func main() {
result, err := getBatchClearinghouseStates(
[]string{
"0x00f2548cf639e54420e501a35346e8458989e6bd",
"0x010461c14e146ac35fe42271bdc1134ee31c703a",
},
"", // empty for native DEX
)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Successful: %d\n", len(result.SuccessfulStates))
fmt.Printf("Failed: %d\n", len(result.FailedWallets))
}
Common Use Cases#
1. Multi-Account Dashboard#
Fetch and display account states for a set of tracked wallets:
async function getMultiAccountDashboard(wallets) {
const result = await getBatchClearinghouseStates(wallets);
const dashboard = result.successful_states.map(([address, state]) => ({
address,
accountValue: parseFloat(state.marginSummary.accountValue),
marginUsed: parseFloat(state.marginSummary.totalMarginUsed),
withdrawable: parseFloat(state.marginSummary.withdrawable),
positions: state.assetPositions.length
}));
const totalValue = dashboard.reduce((sum, a) => sum + a.accountValue, 0);
console.log(`Total across ${dashboard.length} accounts: $${totalValue.toFixed(2)}`);
if (result.failed_wallets.length > 0) {
console.warn(`Failed to fetch: ${result.failed_wallets.join(', ')}`);
}
return dashboard;
}
2. Cross-DEX Position Aggregation#
Query all DEXes for a set of users and aggregate positions:
async function getCrossDexPositions(wallets) {
const result = await getBatchClearinghouseStates(wallets, 'ALL_DEXES');
for (const [address, dexMap] of result.successful_states) {
console.log(`\n=== ${address} ===`);
for (const [dexName, state] of Object.entries(dexMap)) {
const value = parseFloat(state.marginSummary.accountValue);
const positions = state.assetPositions.length;
console.log(` ${dexName}: $${value.toFixed(2)} (${positions} positions)`);
}
}
}
3. Batch Risk Monitoring#
Monitor margin utilization across many accounts and flag those at risk:
async function batchRiskCheck(wallets) {
const result = await getBatchClearinghouseStates(wallets);
const alerts = [];
for (const [address, state] of result.successful_states) {
const margin = state.marginSummary;
const accountValue = parseFloat(margin.accountValue);
const marginUsed = parseFloat(margin.totalMarginUsed);
const utilization = accountValue > 0 ? (marginUsed / accountValue) * 100 : 0;
if (utilization > 80) {
alerts.push({ address, utilization: utilization.toFixed(2), accountValue });
}
}
// Also flag wallets that failed to respond
for (const address of result.failed_wallets) {
alerts.push({ address, error: 'Failed to fetch state' });
}
return alerts;
}
Error Handling#
Errors Specific to This Endpoint#
| Condition | Status | Body |
|---|---|---|
| Invalid JSON body | 400 | {"error":"invalid JSON body"} |
Missing or empty users | 400 | {"error":"users must not be empty"} |
| Too many users (single-DEX) | 413 | {"error":"too many users"} |
| Too many users (ALL_DEXES) | 413 | {"error":"too many users for ALL_DEXES"} |
perpDexs upstream timeout | 504 | {"error":"upstream timeout"} |
perpDexs upstream failure | 502 | {"error":"upstream unavailable"} |
Common Errors#
| Error | Cause | Solution |
|---|---|---|
401 Unauthorized | Invalid API key | Verify your API key is correct |
403 Forbidden | Type denied in server config | Contact support — this type may be disabled |
429 Too Many Requests | Rate limit exceeded | Implement request throttling |
Robust Error Handling#
async function safeBatchQuery(users, dex, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await getBatchClearinghouseStates(users, dex);
} catch (error) {
if (error.message.includes('413')) {
// Too many users — split into smaller batches
throw new Error(`Reduce batch size. Limit is ${dex === 'ALL_DEXES' ? 100 : 1000} users.`);
} else if (error.message.includes('429')) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
} else if (i === maxRetries - 1) {
throw error;
}
}
}
}
Related Endpoints#
- clearinghouseState — Get account state for a single user
- spotClearinghouseState — Get spot trading account state
- openOrders — Get user's open orders
- meta — Get trading pair metadata
Batch query Hyperliquid account states with Dwellir's HyperCore Info Endpoint. Get your API key →