Skip to main content

Which cubes support subscriptions

Every cube on EVM, Solana, Trading and Tron appears on the subscription schema. A subscription against any of them is a valid document and the server accepts it.

That does not mean every cube pushes data.

A valid subscription is not a working subscription

Three cubes accept your subscription and then disconnect you under load. Five accept it and never send a single message. In both cases you get no error from the schema, no rejection at subscribe time, and nothing in your logs except silence. This page tells you which is which.


The three behaviours

BehaviourWhat you seeWhat to do
StreamsMessages arrive within secondsNothing, it works
Needs a filterSome messages, then the socket closes with code 1013Add a where filter, consume asynchronously
Query-onlySocket stays open, no message ever arrivesUse a query, or stream a different cube

Matrix

Relative volume is a rough guide to what your consumer has to keep up with, not a throughput guarantee.

Solana

CubeSubscriptionVolume
BlocksStreamsHigh
TransactionsStreamsHigh
TransfersStreamsHigh
TokenSupplyUpdatesStreamsHigh
DEXPoolsStreamsHigh
DEXTradesStreamsModerate
DEXTradeByTokensStreamsModerate
RewardsStreamsModerate
DEXOrdersStreamsLow
InstructionsFilter requiredVery high
BalanceUpdatesFilter requiredVery high
InstructionBalanceUpdatesFilter requiredVery high

EVM

CubeSubscriptionVolume
MinerRewardsStreamsHigh
EventsStreamsModerate
TransactionsStreamsModerate
TransfersStreamsModerate
BlocksStreamsLow (block cadence)
CallsStreamsLow
DEXTradesStreamsLow
DEXTradeByTokensStreamsLow
DEXPoolEventsStreamsLow
DEXPoolSlippagesStreamsLow
TransactionBalancesStreamsLow
PredictionTradesStreams (network: matic)Moderate
PredictionSettlementsStreams (network: matic)Moderate
PredictionManagementsStreams (network: matic)Low
BalancesQuery-only
HoldersQuery-only
UnclesQuery-only

Trading

CubeSubscriptionVolume
TradesStreamsVery high
PairsStreamsVery high
CurrenciesStreamsVery high
TokensStreamsVery high

All four Trading cubes are among the busiest streams on the platform. Filter them.

Tron

CubeSubscriptionVolume
TransfersStreamsModerate
TransactionsStreamsModerate
DEXTradeByTokensStreamsLow
EventsStreamsLow
BlocksStreamsLow (block cadence)
CallsStreamsLow
DEXTradesStreamsLow
BalancesQuery-only
HoldersQuery-only

Filter-required cubes and close code 1013

Solana.Instructions, Solana.BalanceUpdates and Solana.InstructionBalanceUpdates carry every instruction and every balance change on Solana. Subscribing without a filter asks for the entire firehose, and when your client cannot drain the socket fast enough the server closes it:

close code 1013 — client is not consuming messages fast enough

1013 is "Try Again Later" in the WebSocket spec. Nothing is wrong with your query. The server is shedding a consumer that fell behind.

The failure is worse than a clean error because it is load-dependent. The same unfiltered subscription can run fine during a quiet minute and get dropped during a busy one, so it passes in development and fails in production.

The fix

Add a where filter so the server sends you only what you need. An unfiltered BalanceUpdates subscription is dropped; the same subscription narrowed to a single token runs cleanly:

subscription {
Solana {
BalanceUpdates(
where: {
BalanceUpdate: {
Currency: { MintAddress: { is: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" } }
}
}
) {
BalanceUpdate {
Amount
Currency { Symbol }
Account { Address }
}
}
}
}

The same applies to Instructions. Filter by program:

subscription {
Solana {
Instructions(
where: {
Instruction: { Program: { Address: { is: "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" } } }
}
) {
Block { Time }
Transaction { Signature }
Instruction { Program { Method } }
}
}
}

Filtering is not only a throughput fix. It is also cheaper, since you are not paying to receive rows you discard.

Also drain the socket asynchronously

A filter reduces the rate; it does not make a blocking consumer safe. Push each message onto a queue and process it elsewhere, so parsing or database writes never stall the read loop. See Reconnect automatically after disconnect for a working consumer and reconnect loop.


Query-only cubes

These accept a subscription and never emit, and the reason is structural. Balances and Holders are backed by aggregate-state tables (balances_by_address and balances_by_currency) holding daily balance aggregates rather than individual changes. A daily grain has no per-event row to push, so there is nothing for a subscription to deliver.

That is also what makes them fast to query and what gives them combined support, so the trade-off is deliberate rather than a gap.

These are the current cubes, not legacy ones

Balances and Holders supersede the deprecated BalanceUpdates and TokenHolders cubes. If you are migrating: Holders takes its currency filter through the standard where: argument instead of the old required tokenSmartContract / date arguments, and both new cubes support realtime, archive and combined.

So the move to Balances/Holders trades a streamable per-change event log for cheap daily aggregates. Where you previously streamed BalanceUpdates, stream Transfers (or TransactionBalances on EVM) instead and apply the deltas yourself.

EVM.BalanceUpdates, EVM.TokenHolders and Tron.BalanceUpdates sunset on 10 August 2026.

CubeUse instead
EVM.BalancesQuery it on a schedule, or stream EVM.TransactionBalances / EVM.Transfers and apply deltas
EVM.HoldersQuery it on a schedule; stream EVM.Transfers for the token to know when to refresh
Tron.BalancesQuery on a schedule, or stream Tron.Transfers
Tron.HoldersQuery on a schedule, or stream Tron.Transfers
EVM.UnclesQuery with dataset: archive. Ethereum has produced no uncles since the Merge

The pattern for a live balance is to read the balance once, then keep it current from the transfer stream, rather than polling the balance cube in a loop.


Cubes that need a specific network or dataset

Some cubes exist on the EVM root but only carry data on one network or one dataset. The error message is the only place this is stated today, so it is worth listing:

CubeRequirement
EVM.PredictionTradesnetwork: matic
EVM.PredictionManagementsnetwork: matic
EVM.PredictionSettlementsnetwork: matic
EVM.Unclesdataset: archive
EVM.TransactionBalancesrealtime only, no archive tables

Querying EVM.PredictionTrades on network: eth returns no data available yet to query dataset realtime eth for PredictionTrade, which reads like an outage but is a routing mistake.


How this was measured

Every row was tested against wss://streaming.bitquery.io/eap with the graphql-ws subprotocol, one socket per cube, using a minimal selection set generated from schema introspection. Cubes that produced nothing in the first pass were retried on a longer window, because some low-frequency cubes take longer than 20 seconds to deliver their first message.

Classification rules:

  • Streams — at least one message on a socket held open long enough for the cube's cadence.
  • Filter required — reproducibly closed with 1013 when unfiltered, and delivered normally once a where clause was added.
  • Query-only — no message and no error across repeated runs, while control subscriptions on the same chain in the same session delivered normally.

The controls matter: a chain-level failure would have shown up as every cube on that chain going quiet, and that did not happen.