Solana WebSocket nodes for live account, log, and slot subscriptions
Polling getAccountInfo on a loop is the first Solana mistake every team makes and the first thing every reviewer flags. Solana exposes a real WebSocket PubSub interface, with subscriptions for accounts, transaction logs, programs, signatures, slots, blocks, and votes. The protocol's been stable since 2021. The hard part isn't subscribing. The hard part is keeping the socket alive under real load, knowing which subscription type fits the question you're asking, and having a clean fallback when blockSubscribe disappears mid-stream because the validator restarted. NoLimitNodes runs WebSocket on the same bare-metal fleet as RPC, with persistent connections that auto-reconnect and an explicit posture on which subscriptions are production-grade and which ones aren't. The endpoint is wss://ws.nln.clr3.org. Auth is the same x-api-key header your RPC client uses.
- Solana System Program11111111111111111111111111111111, Useful as the canonical accountSubscribe target while you smoke-test a connection. Always live, always ticking.
- Vote ProgramVote111111111111111111111111111111111111111, voteSubscribe surfaces every validator vote tx. Useful for cluster observability, noisy for everything else.
- Token ProgramTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA, A common programSubscribe target. Always pair it with dataSize and memcmp filters or you will drown.
Every Solana PubSub subscription you can open
the full standard PubSub method set with honest notes on which ones to actually use
| Event | Type | Description | Frequency | Latency |
|---|---|---|---|---|
| accountSubscribe | event | Push every change to a single account by pubkey. The right call for wallet UIs, oracle feeds, and pool reserves. | High | 8ms |
| logsSubscribe | event | Stream raw program logs filtered by mentions, all, or vote. Returns base64 log lines, not parsed events. | Very high | 9ms |
| programSubscribe | event | Push every account update under a program. Always pass dataSize and memcmp or you will saturate the socket. | Very high | 10ms |
| signatureSubscribe | event | Fires when a transaction reaches the requested commitment level. Auto-unsubscribes after the notification. | High | 8ms |
| slotSubscribe | event | Notification on every slot processed by the validator. The cluster heartbeat; useful for liveness checks. | Very high | 6ms |
| slotUpdatesSubscribe | event | Richer slot stream with first-shred-received, completed, optimistic-confirmation, and root events. | Very high | 6ms |
| rootSubscribe | event | Fires when a slot becomes rooted (finalized). Lower frequency than slotSubscribe; useful for settlement. | Medium | 8ms |
| blockSubscribe | event | Pushes full blocks with all transactions on the requested commitment. Heavy; not stable across all releases. | Medium | 25ms |
| voteSubscribe | event | Stream of validator vote transactions. About 70% of mainnet tx volume; almost always the wrong subscription. | Very high | 7ms |
WebSocket performance at a glance
last reviewed 2026-04-28
What logsSubscribe and accountSubscribe actually return
Both methods sit on the same socket and look similar in the docs. The shape of the data they push is wildly different, and getting that wrong costs you a week of debugging.
accountSubscribe fires whenever the bytes at a single pubkey change. The notification carries the slot, the lamport balance, the owner program, and the full data buffer in your requested encoding. We default to jsonParsed for token, stake, and vote accounts so you skip the bincode pass; pass base64 if you need raw bytes. Each notification is one account, fully serialized. Easy to reason about. The cost is one subscription per account; tracking a wallet means subscribing to its base account plus every token account you care about.
logsSubscribe is a different beast. The notification carries a transaction signature, the slot, an error field, and an array of log lines straight from the BPF runtime. No parsed instructions, no decoded arguments, no idea which inner instruction emitted which log unless you walk the array yourself. You also get every log from every program the transaction touched, which on a Jupiter swap might be eight programs deep. The filter options are all, allWithVotes, or a { mentions: [...] } object that scopes to transactions touching specific programs or accounts. Always use the mentions filter. all on mainnet is roughly 2,500 messages per second, most of it vote traffic.
The opinion: logsSubscribe is the wrong tool for parsed events. It's the right tool for two narrow cases: a known-program error log feed (you watch for Program log: Error), and a quick-and-dirty signal that anything happened at a program address while you build the proper gRPC pipeline.
How much throughput a WebSocket connection actually has
The protocol gives you a TCP connection with TLS on top and JSON messages over the wire. Three things cap your effective throughput and the order they bite is predictable.
| Bottleneck | Where it bites | Practical ceiling |
|---|---|---|
| JSON parse cost | Client side | ~400 msg/s in Node, ~700 in Rust |
| Single TCP connection | Network buffer + head-of-line | ~30-50 MB/s sustained |
| Subscription count | Server enforces a cap | 100 subs per connection (NLN) |
The first ceiling, the JSON parse, is what kills most production WebSocket setups. A busy programSubscribe on Token-2022 can land 600 messages per second, and a Node handler that does anything besides JSON.parse falls behind. The server can't magically make your client faster, so the messages queue, then drop. We watch teams hit this around the three-week mark of a launch.
The fix is one of two things. Either narrow the subscription (better filters, accountSubscribe per pubkey instead of one big programSubscribe), or change transports. Yellowstone gRPC handles 5,000+ messages per second on a single connection because Protocol Buffers parse in a quarter of the time JSON does and HTTP/2 multiplexes streams so one slow message doesn't block the next.
When to graduate from WebSocket to gRPC
We have an opinion on this and we'll be direct about it. WebSocket is the right transport for two things: account-level UIs (a wallet, a position dashboard, a mint counter), and prototypes where you want to see something working in fifteen minutes. Past those two, gRPC is the right answer.
The signal you've outgrown WebSocket is rarely throughput. It's parsing tax. You start writing your own log decoder, then a discriminator router, then a fallback when the log lines are truncated, then a reconciliation pass when the subscription reconnects mid-stream. Every one of those is solved upstream when you switch to gRPC. The events arrive as protobuf messages with a stable schema. You skip the entire decode layer.
Some specific triggers. If you find yourself running multiple logsSubscribe connections in parallel because one isn't keeping up, switch. If you need to filter by accountInclude and accountExclude on the same stream, switch. If you want vote-free transactions and your logsSubscribe mentions filter is matching too many vote txs because the validator touched them, switch.
What stays on WebSocket: signature confirmation polling replaced by signatureSubscribe, a wallet UI showing a balance update, a slot heartbeat for cluster observability, a programSubscribe with tight filters on a low-volume program. Use the right tool for the question. The endpoint sits on our gRPC product page when you're ready.
Subscription patterns and reconnection that actually works
A WebSocket connection isn't TCP-stable. It will drop. Your client needs to assume that and rebuild state on reconnect, or you'll spend a Saturday wondering why the dashboard stopped updating at 3am.
Heartbeat & ping/pong
We send a ping every 25 seconds. Your client sends a pong back automatically in most libraries. If you go three pings without a pong, treat the connection as dead. A silent socket isn't a healthy socket; it's a TCP connection with a NAT box that gave up half an hour ago and forgot to tell anyone.
Re-subscribe on reconnect
Subscriptions don't survive a reconnect. The new connection has no idea what you cared about. Keep your subscription list in memory and re-send every accountSubscribe and logsSubscribe call as part of your reconnect handler.
Backfill the gap
Between the disconnect and the reconnect, you missed events. Snapshot the slot you last saw, then on reconnect issue a one-shot getSignaturesForAddress or getAccountInfo to backfill. WebSocket has no replay buffer; ours, theirs, no one's.
Idempotent handlers
Reconnect plus backfill means some events arrive twice. Key every downstream effect by signature plus slot or by account pubkey plus update slot. Two notifications with the same key produce the same outcome. This is the cheapest insurance you can buy.
One more pattern that earns its keep: the snapshot-then-stream flow. Open the WebSocket and subscribe first. Once the subscription ack comes back, fire a one-shot getAccountInfo at the same commitment level. Apply the snapshot to your state. From that point, every notification is a delta. You never miss the starting state and you never double-count the moment between the subscribe and the snapshot.
WebSocket vs SSE vs polling: pick the right transport
Solana doesn't expose Server-Sent Events natively, but the comparison still matters because some teams reach for SSE through a proxy and end up worse off. Here's the honest take.
| Transport | Right for | Watch out for |
|---|---|---|
| HTTP polling | Status pages, slow data, cron-style reads | Rate limits, wasted requests |
| SSE (server proxy) | Pure browser fan-out from a backend | One-way only, no native auth headers |
| WebSocket | Live UIs, account streams, signature acks | Browser can't set headers; backpressure |
| gRPC | Indexers, MEV, programmatic streams | Not browser-native; needs gRPC-Web bridge |
For a frontend that needs to show live state, the working pattern is: backend connects to our WebSocket (or gRPC for high volume), fans the relevant slice out to the browser via either WebSocket or SSE. That keeps the auth header on the server, lets you cache and dedupe before the browser sees anything, and gives you a natural place to add business logic between the chain and the UI.
For a backend that needs to react to state, skip SSE entirely. WebSocket if the volume fits, gRPC if it doesn't. Polling only for things that genuinely change slowly: an Epoch boundary, a stake-account state, a once-per-minute price.
Frequently asked questions
Related products
The pull side of the same fleet. Use HTTP for one-shot reads and tx submission.
Where you graduate when WebSocket runs out of headroom. Binary, multiplexed, thousands of msg/s per connection.
18 curated, decoded topics for Raydium, PumpFun, Orca, and Meteora on top of our gRPC. Skip the IDL step.
1,074 topics across 37 Solana programs. Every decoded event, browsable.
Run your own Geyser plugin on our validators. The next step past managed gRPC.
Start streaming Solana over WebSocket in under 60 seconds
Free tier covers prototyping. Pro at $49/mo unlocks the full subscription budget plus blockSubscribe. Ultra at $199/mo lifts the cap to 100 concurrent subscriptions per connection and adds 30 hours of custom dev time.