tokenAccounts filter on the transactionSubscribe WebSocket method lets a subscription match activity on the associated token accounts (ATAs) a wallet owns, not just transactions where the wallet’s pubkey appears directly. The same filter is available over gRPC — see Token Account (ATA) Filtering for the gRPC version.
The problem: plain account filters miss incoming token transfers
When you watch a wallet withaccountInclude: [wallet], you only match transactions where that wallet pubkey appears in the transaction’s account keys. A common case slips through: when someone sends the wallet an SPL token (USDC, for example), the transfer touches the wallet’s associated token account (ATA) — a separate program-derived address — not the wallet pubkey itself.
So a plain accountInclude: [wallet] subscription never sees incoming token transfers. You would have to enumerate every ATA the wallet owns up front and add each one to the filter — but ATAs are created on demand (one per mint), so you can’t know the full set in advance.
How tokenAccounts expansion works
Set tokenAccounts on the subscription to expand matching so an accountInclude wallet also matches transactions that touch a token account it owns. Matching is owner-based: LaserStream resolves the token accounts owned by your accountInclude addresses at match time, so it catches any token account the wallet owns — including non-canonical ones — not just the derived ATA address. You never have to list the ATAs yourself.
Subscriptions that omit tokenAccounts behave exactly as before, so it’s safe to add to an existing filter.
Expansion modes
tokenAccounts takes one of three string values:
Start with
"balanceChanged". It captures real fund movement at a fraction of the volume of "all".
Use it in transactionSubscribe
tokenAccounts is a Helius extension to the standard Solana WebSocket API. An invalid value returns JSON-RPC error -32602: Invalid tokenAccounts value '<x>', expected one of: none, balanceChanged, all.
Reading what matched
Once a transaction matches via ATA expansion, the wallet’s token movement lives in the transaction’smeta.postTokenBalances and meta.preTokenBalances. Filter those entries by owner to isolate the balances your wallet actually owns, then diff preTokenBalances against postTokenBalances on the same accountIndex to see how much each mint moved. The example above shows the filtering step.
Related
transactionSubscribe
Every
transactionSubscribe filter and option, including tokenAccounts.Token Account Filtering (gRPC)
The same
tokenAccounts expansion on LaserStream gRPC transaction filters.notifyOn Filtering
Skip no-op account updates on
accountSubscribe and programSubscribe.WebSocket Quickstart
Connect to LaserStream WebSocket and stream your first events.