← all notes
2026.07.06 · 5 min · crypto

WebSocket subscriptions: let the network tell you what happened

Polling is slow, blind, and expensive — WebSocket subscriptions push account changes, logs, and slots to you instead.

In the previous chapter we poked the network over HTTP: ask a question, get an answer. A simple model, but it has a ceiling. Say you want to know when an account’s balance changes. Over HTTP your only option is to call getAccountInfo in a loop — so-called polling. It’s slow (between polls you’re blind), expensive (your RPC credits go up in smoke), and it doesn’t scale to a hundred accounts.

That’s what the second, push-based model is for: WebSocket subscriptions. A WebSocket, if you’ve never used one, is just an HTTP connection that’s upgraded once and then stays open, so either side can send messages at any time. You open one persistent connection, say “tell me when X changes” — and the RPC node sends you notifications itself.

fig. 1 — polling asks and asks and asks; a subscription just waits to be told

Connecting

Remember the WebSocket URL line in the output of solana config get? This is it. Every RPC node runs a WSS endpoint right next to its HTTP one:

# public node
wss://api.mainnet-beta.solana.com
# helius
wss://mainnet.helius-rpc.com/?api-key=<your-key>

For experiments, the wscat utility is all you need:

npm install -g wscat
wscat -c "wss://mainnet.helius-rpc.com/?api-key=<your-key>"

The protocol is the same JSON-RPC, except the conversation is now two-way: you send a *Subscribe request, the node replies with a subscription number, and from then on it pushes notifications to you until you unsubscribe (*Unsubscribe) or the connection drops.

accountSubscribe: watching an account

Let’s subscribe to our own wallet:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "accountSubscribe",
  "params": ["<your-address>", { "encoding": "jsonParsed", "commitment": "confirmed" }]
}

The node answers with {"result": 23784, ...} — that’s your subscription ID. Now send yourself some SOL from another wallet and watch: an accountNotification arrives with the account’s new state. Subscribe the same way to your ATA (the associated token account from part 3 — the separate account that holds your balance of a given token) and you’ll get notifications for token movements too. Note the commitment in the params: it decides at which confirmation level you’re told about a change — the ladder from part 4, from processed (a validator has executed the transaction, but the block could still be discarded) through confirmed to finalized (set in stone). For a bot — processed, the fastest of the three, at the price of occasionally seeing changes that later get rolled back. For accounting — finalized.

logsSubscribe: watching a program

Every transaction on Solana leaves behind logs — text lines emitted by the programs it executed, roughly the on-chain equivalent of console.log. logsSubscribe streams you the logs of every transaction that mentions a given address:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "logsSubscribe",
  "params": [{ "mentions": ["<program-or-wallet-address>"] }, { "commitment": "confirmed" }]
}

This is the most popular method among “entry-level” trading bots: subscribe to the logs of a DEX program (a DEX is a decentralized exchange — a program people trade tokens through) and you see every swap in near real time, along with the transaction’s signature, which you can immediately follow up on via getTransaction. Subscribe to a whale’s wallet — and there’s your foundation for the copy-trading idea from the introduction (with all the caveats about speed we’ll get back to below).

signatureSubscribe: “tell me when my transaction lands”

In the previous chapter we sent a transaction and polled its status. The proper way:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "signatureSubscribe",
  "params": ["<signature>", { "commitment": "confirmed" }]
}

One notification when the transaction reaches the requested commitment — and the subscription closes automatically. This is exactly how the SDKs implement functions like confirmTransaction.

The rest of the zoo

  • programSubscribe — notifications when any account owned by a program changes. Powerful (you can watch every token account or every DEX pool at once), but the stream is enormous, and on public/cheap nodes the method is often throttled or disabled. Filters save you (filters: by account size and by byte content).
  • slotSubscribe — ticks every slot (a slot, from part 1, is the network’s ~400 ms heartbeat — one leader’s turn to produce a block), handy as the network’s pulse.
  • rootSubscribe, voteSubscribe, blockSubscribe — exotica; some are marked unstable and disabled on most nodes.

The fly in the ointment: what to understand about WebSocket

And about speed: a WebSocket notification arrives after the transaction has made it into a block and the block has reached your RPC node. For 95% of tasks that’s plenty. But if you’re racing other bots — remember the introduction: the serious players don’t listen to WSS, they tap streams one level down:

  • Geyser plugins — a plugin that sits inside the validator itself, streaming account changes and transactions out;
  • Yellowstone gRPC — the de facto standard on top of Geyser (Helius offers it as the managed LaserStream);
  • ShredStream — that same raw stream of shreds from the first chapter, and the only thing faster is becoming the leader yourself.

And so the circle closes: we opened this series explaining why people pay serious money for these streams — and now you know exactly where they sit in the hierarchy of speed: HTTP polling → WebSocket → Geyser/gRPC → ShredStream.

What’s next

This wraps up the “protocol” part of the series: you can read the network, send transactions, and listen for events. From here there are two directions: writing client code with an SDK (@solana/web3.js / Rust / Python), or going a level lower — your own node, Geyser, and streams. Tell me which sounds more interesting — that’s what we’ll build on.