Skip to main content
WS channel: wallet-balances The exact token balances of the wallets you follow, pushed as each transaction changes them, with the new balance of every token the transaction moved. Follow one wallet (on one chain or on several), a list of up to 100, or a watchlist of thousands of wallets in one subscription. Use it for a live holders tab of your own users, balance alerts, or any screen that shows what a large set of wallets holds. For positions with entry price and PnL, see the Wallet Positions Stream; for every trade, the Wallet Trades Stream.

Delivery model

  • Events, as they land. There is no snapshot: the stream starts with the first balance change after your subscription. Take the starting balances from REST: token holders with watchlist=<id> for a watchlist, /v1/wallet/positions for a wallet.
  • One message per transaction and wallet. Every message is "data": [balance, ...]: the balance after the transaction of each token it moved for that wallet. A transaction that moves 5 tokens of a wallet is one message of 5 balances. When your connection falls behind, the balances waiting for it are merged (the latest per wallet, chain and token): a message can then carry several transactions, and costs 1 per wallet and chain it carries.
  • Absolute balances. Each balance is the wallet’s whole balance of the token after the transaction, never a difference: the latest one replaces what you had.
  • Keep the greatest (block, logIndex). For each wallet, chain and token, keep the balance with the greatest block, then the greatest logIndex. A balance that arrives late, or twice (after a resume), then changes nothing.
  • Tokens. SPL tokens on Solana and ERC-20 tokens on EVM chains. Native coins (SOL, ETH and the native coin of each chain), their wrapped versions and USD stablecoins are not on this channel: read them with /v1/wallet/positions (includeNative=true, includeStables=true).

Parameters

Give address with either chain or chains, or give wallets, or give watchlist.
string
Public chain id, evm:<id> or solana. Example: solana
string | string[]
Several chains of the wallet in one subscription, in place of chain: public chain ids, comma-separated ("evm:8453,evm:1") or as an array. evm stands for every EVM chain and combines with explicit ids. Every chain must belong to the wallet’s family: EVM chains for a hex address, solana for a base58 address. Send chain or chains, not both. Example: evm
string
The wallet address (EVM hex, case-insensitive; Solana base58). Example: DfMxre4cKmvogbLrPigxmibVTTQDuzjdXojWzjCXXhzj
object[]
A list of 1 to 100 wallets, [{ "chain": "...", "address": "..." }, ...], in place of chain and address. Each entry takes one chain and counts as one subscription in the limit of your account. Each balance carries its wallet.
string
The id of one of your watchlists, in place of chain, chains, address and wallets. Every wallet of the list is followed, and the subscription counts as one in the limit of your account, whatever the size of the list. Wallets you add or remove over REST are followed or dropped within seconds, without subscribing again. Example: app-holders
integer
Milliseconds, 0 to 60000; default 0 (every transaction as it lands, in its own message while your connection keeps up). Above 0, one message per period carries the latest balance of each wallet, chain and token that changed in the period (the greatest block, logIndex); a period that piles up more than 256 balances, or 256 KB of them, is sent at once and the next period starts, so no balance is left out. The ack carries the value applied (snapped up to 0, 100, 200, 250, 500, 1000, 2000, 5000, 10000, 30000, 60000). "block" is not available on this channel. See Choosing your pace.
boolean
default:"false"
true leaves out the balances of tokens flagged as spam. A token without a spam verdict is delivered. On a list, give it once, next to wallets.

The balance

Parse rawBalance and balance with a decimal or big-integer library, not a float, when the last digits matter: an 18-decimal balance does not fit in a float.

Resume

With a single wallet, on one chain or on several, or a watchlist, every message carries a cursor. After a reconnect, subscribe again with "since": "<cursor of the last message you received>": you receive the balance changes after it (up to 5 minutes back), then live. You are never billed twice: anything up to your cursor that comes back is flagged "replay": true and free (on a watchlist, this can include a few messages from the seconds around your disconnection); what comes after it is billed like live messages, even when it carries "replay": true. Keep the greatest (block, logIndex) as usual: a balance you already applied changes nothing. On a watchlist, a wallet removed, or the list cleared, while you were away may not be replayed. If the replay is not available, a REPLAY_TOO_OLD notice gives details.restFromMs: the subscription keeps going; since balances are absolute, read the current ones again over REST (token holders with watchlist=<id>, /v1/wallet/positions) for the tokens you show. A message flagged "gap": true follows balances that could not be delivered (for example when your connection fell behind): read the current balances over REST for the tokens you show, then keep applying messages. A wallets list takes no since: after a reconnect, subscribe again and read the current balances over REST. See Reconnect & resume.

Billing

1 credit per wallet and chain in each message: one transaction of the wallet costs 1 at the default pace, however many of its tokens it moved, and a batch (updatePeriod) costs 1 per wallet and chain it carries. A wallet whose balances do not change costs nothing, and the connection is free. A wallet followed on several chains, or a chains: "evm" wallet of a watchlist, pays only the chain of each transaction. On a resume you are never billed twice: what comes back up to your cursor is free, and what comes after it is billed like live messages (see Resume). Details in Billing. GET /v1/usage/breakdown reports this channel under WS wallet-balances, where requests is the number of billed messages.

Errors

Close codes

Ack
type:object

Server acknowledgement: the subscription is live.

Balances
type:object

Server push frame, one per transaction and followed wallet, data always an array of its token balances (a batch per period with updatePeriod): 1 credit per wallet and chain in the message (one transaction: 1, however many tokens it moved).

Subscribe
type:object

Client frame opening the subscription (additive, ack is explicit).