Balances & Holders Cubes
Balances and Holders are derived views that answer "what is true now", so there is no
underlying event to push. A subscription against either is a valid document and the socket
stays open, but no message is ever delivered — on EVM or Tron.
For live balances, read the balance once and then keep it current from a stream that does
fire: EVM.TransactionBalances or EVM.Transfers (and Tron.Transfers on Tron). See
which cubes support subscriptions.
Two cubes answer balance questions, and picking the right one matters more than the fields you select:
| Cube | Question it answers | Shape |
|---|---|---|
Balances | What does this address hold? | Current balance per address, live — no snapshot date |
Holders | Who holds this token? | A token's holders ranked by balance, as a dated snapshot |
Both read from aggregate-state tables (balances_by_address / balances_by_currency), so they return the current state directly instead of replaying every change.
Balances and Holders exist on EVM networks (EVM(network: …)) and Tron (Tron).
They do not exist on Solana. Solana balance changes are queried with Solana.BalanceUpdates and Solana.InstructionBalanceUpdates.
Balances — what an address holds
Balances returns one row per address/currency pair with the current amount.
Balance fields: Address, Amount, AmountInUSD, UpdateCount, FirstChangeTime, LastChangeTime.
Its where filter accepts Balance.Address plus Currency and Block. Note there is no amount filter on Balances — see Holders if you need to filter or rank by balance size.
Token balance for one or more addresses
query TokenBalances($addresses: [String!], $token: String!) {
EVM(network: eth, dataset: combined) {
Balances(
where: {
Balance: { Address: { in: $addresses } }
Currency: { SmartContract: { is: $token } }
}
orderBy: { descending: Balance_Amount }
) {
Balance {
Address
Amount
AmountInUSD
UpdateCount
LastChangeTime
}
Currency {
Symbol
Name
SmartContract
}
}
}
}
{
"addresses": [
"0x28C6c06298d514Db089934071355E5743bf21d60",
"0x21a31Ee1afC51d94C2eFcCAa2092aD1028285549"
],
"token": "0xdac17f958d2ee523a2206206994597c13d831ec7"
}
To exclude zero balances, apply the filter on the field rather than in where:
Amount(selectWhere: { gt: "0" }).
Native balance
Filter on Currency: { Native: true } to get the chain's native asset instead of a token:
{
EVM(network: eth, dataset: combined) {
Balances(
where: {
Balance: { Address: { is: "0x28C6c06298d514Db089934071355E5743bf21d60" } }
Currency: { Native: true }
}
) {
Balance {
Address
Amount
AmountInUSD
}
Currency {
Symbol
Native
}
}
}
}
The same query works on Tron by swapping the selector for Tron and using a Tron address.
Classifying an address from its balance metadata
UpdateCount with LastChangeTime separates wallet types without any labelling data:
- Very high
UpdateCount,LastChangeTimeseconds ago — exchange hot wallet or payment processor; the balance churns continuously. - Single-digit
UpdateCounton a large balance — cold storage, treasury or custody. Funded once, rarely touched.
FirstChangeTime is not the address's first-ever activityFirstChangeTime reflects the earliest change within the balances table's retention window, not the first time the address ever moved funds. For true first-activity, query Transfers with an ascending time order instead.
Holders — who holds a token
Holders lists a token's holders, ranked. It takes a date argument for the snapshot day, and unlike Balances it does accept a balance filter — which is what makes large tokens tractable.
query TopHolders($token: String!, $floor: String!, $date: String!) {
EVM(network: eth) {
Holders(
limit: { count: 100 }
orderBy: { descending: Balance_Amount }
date: $date
where: {
Currency: { SmartContract: { is: $token } }
Balance: { Amount: { ge: $floor } }
}
) {
Holder {
Address
}
Balance {
Amount
}
Currency {
Symbol
Name
}
}
}
}
{
"token": "0x514910771AF9Ca656af840dff83E8264EcF986CA",
"floor": "1000000",
"date": "2026-07-29"
}
An unbounded top-N ranking across every holder of a very large token times out server-side — this is a dataset-size limit, not a syntax error. Ethereum USDT times out even with a 50,000,000 floor; Tron USDT behaves the same way.
The fix is a Balance: { Amount: { ge: … } } floor high enough to shrink the working set. For a complete holder distribution rather than the top of it, use Data Store exports or Kafka streams rather than a synchronous GraphQL query.
Choosing a dataset
| Dataset | When to use |
|---|---|
combined | Latest balances. Queries realtime and archive and merges the results. |
archive | Historical snapshots, and balances for addresses that have not been active recently. |
realtime | Recent state only. Some aggregates are unavailable here — if you see "no table can query … consider use archive dataset", switch to combined or archive. |
Daily balance history
Balances in the archive dataset gives you one row per address per day, exposed as Block.Date.
A 30-day balance history is one query returning 30 rows.
query DailyBalanceHistory {
EVM(network: eth, dataset: archive) {
Balances(
where: { Balance: { Address: { is: "0x28c6c06298d514db089934071355e5743bf21d60" } } }
orderBy: { descending: Block_Date }
limit: { count: 30 }
) {
Block {
Date
}
Balance {
Amount
AmountInUSD
}
Currency {
Symbol
}
}
}
}
Always order by Block_Date. Without it you get an arbitrary day and the query still succeeds,
which makes the mistake silent.
| You need | Use |
|---|---|
| Current balance per address | Balances (latest row) |
| Balance on a past date, or a daily series | Balances with Block.Date — or Holders(date: …), which agrees exactly |
| A token's holders, ranked | Holders |
| When a position first or last moved | Balances — FirstChangeTime, LastChangeTime, UpdateCount |
| The individual transfers behind a change | Transfers |
The daily aggregate does not say why a balance moved. For that, use
Transfers plus transaction context, or TransactionBalances for per-transaction balance changes.
Related
- Address Labels API — identify which of those addresses are exchanges, contracts, or known entities
- Transfers cube — the transfers that drive most balance changes
- Token Holders API (Ethereum) — worked
EVM.Holdersexamples - TRC20 USDT API —
Tron.BalancesandTron.Holdersin practice
Ready to run this in production?
Get an API key and run these queries in minutes, or talk to us about plans and enterprise delivery.