A Solana WebSocket subscription is a way to receive real-time notifications from the blockchain over a single open connection, instead of repeatedly asking an endpoint whether something changed. You open a WebSocket, send a subscription request such as accountSubscribe, and the server pushes a message to you every time that account updates.
This matters because Solana moves fast. The network produces a new slot roughly every 400 milliseconds, so polling over HTTP either floods your endpoint with requests or leaves you a few seconds behind. WebSockets fit that speed much better. This guide walks through what Solana WSS connections do, the subscription methods available, how commitment levels change what you receive, and how to open a connection in practice.
What Is a Solana WebSocket Subscription?
A Solana WebSocket subscription is a persistent “publish/subscribe” (PubSub) channel between your app and a Solana endpoint. You subscribe to an event type once, and the server streams updates until you unsubscribe or the connection closes.
Under the hood it uses JSON-RPC 2.0 over a WebSocket connection. You connect to a PubSub endpoint at wss://<ADDRESS>/, then send subscription requests as JSON objects. Each request carries a jsonrpc field set to "2.0", an id you choose, a method name like accountSubscribe, and a params array. The server replies with a numeric subscription ID, and every later notification references that same ID. The full behavior is documented in the official Solana WebSocket reference.
The mental model is simple: HTTP is a question you ask, a WebSocket is a feed you listen to.
WebSocket vs HTTP: when does each fit?
Use HTTP (JSON-RPC calls) when you need data once — a balance lookup, a transaction history, a one-time account read. Use a WebSocket when you need to know the moment something changes and can’t afford to miss it or wait.
| Need | Better fit |
|---|---|
| One-off account or balance read | HTTP |
| Live balance or price feed | Solana WebSocket |
| Confirming a single transaction | Solana WebSocket (signatureSubscribe) |
| Backfilling historical data | HTTP (archive endpoint) |
| Monitoring every account a program touches | Solana WebSocket (programSubscribe) |
Most production apps use both: HTTP for reads and writes, WSS for the live layer.
Why Use WebSockets Instead of Polling?
Polling wastes resources and still lags. If you want near-instant updates by polling, you have to send requests constantly, most of which return “nothing changed.” Scale that across hundreds of accounts and you burn through request limits quickly.
A WebSocket subscription replaces that whole pattern with one connection. The server only sends you data when the event you asked about actually happens, so you get lower latency and far fewer wasted calls. For anything reacting to on-chain state in real time, that difference is the whole point.
There is a trade-off. WebSocket connections are stateful and can drop, so you have to handle reconnects and re-subscribe after an outage. That lifecycle work is covered further down.
Who Uses Solana WSS Connections?
Real-time subscriptions show up anywhere an app has to respond to the chain as it moves:
- Trading and arbitrage bots watch pool and account state with
accountSubscribeorprogramSubscribeto act on price changes within a slot or two. - Block explorers and dashboards stream slots, blocks, and logs to update the screen without a refresh.
- Indexers subscribe to a program and record every account change it produces.
- Wallets track a user’s balance and token accounts live.
- Monitoring tools use
signatureSubscribeto confirm that a submitted transaction landed.
If your product answers the question “what is happening on-chain right now,” it almost certainly needs a Solana WebSocket subscription somewhere.
The Core Subscription Methods
Solana’s PubSub API exposes a fixed set of subscription methods. Each *Subscribe method has a matching *Unsubscribe to cancel it. Here are the ones you’ll actually reach for:
| Method | Subscribes to | Common use |
|---|---|---|
accountSubscribe | Lamport or data changes on one account | Balance and state tracking |
logsSubscribe | Transaction log messages matching a filter | Debugging, activity triggers |
programSubscribe | Changes to accounts owned by a program | Indexers, protocol monitoring |
signatureSubscribe | Status of one transaction signature | Confirming a transaction |
slotSubscribe | Each slot processed by the validator | Chain-progress tracking |
blockSubscribe | New blocks (unstable) | Block syncing |
rootSubscribe | New roots set by the validator | Finality tracking |
A few methods — blockSubscribe, slotsUpdatesSubscribe, and voteSubscribe — are marked unstable and are disabled or unsupported on many endpoints, so check your provider before relying on them.
accountSubscribe — track one account
accountSubscribe notifies you whenever the lamports or data of a specific account public key change. It’s the workhorse for watching a single balance, a token account, or a program-derived state account. You can set the data encoding (base58, base64, or jsonParsed) and a commitment level. See the accountSubscribe spec for the full parameter list.
logsSubscribe — watch transaction logs
logsSubscribe streams log messages emitted by transactions, filtered either by all or by accounts a transaction mentions. It’s useful for catching program activity and events in near real time, though logs can be noisy and shouldn’t be treated as a guaranteed, ordered record of state.
programSubscribe — monitor a whole program
programSubscribe fires on changes to any account owned by a given program. This is how indexers keep up with an entire protocol without subscribing to each account individually. You can narrow the firehose with filters on account size or memory contents.
signatureSubscribe — confirm a transaction
signatureSubscribe tracks the status of a single transaction signature. It has one behavior worth memorizing: the subscription ends automatically after the terminal confirmation notification, so you don’t manually unsubscribe in the normal case. If you set enableReceivedNotification to true, the endpoint may send an earlier receivedSignature message and keep the subscription open until the signature reaches your requested commitment. Supported commitment values are processed, confirmed, and finalized, with finalized as the default, per the signatureSubscribe docs.
slotSubscribe and blockSubscribe — follow the chain
slotSubscribe sends a small notification for every slot the validator processes — handy as a heartbeat or progress counter. blockSubscribe delivers full block data but is unstable and heavier, so most apps lean on slot and root notifications instead.
How Commitment Levels Shape Your Subscriptions
Commitment tells Solana how confirmed a change must be before it notifies you. There are three levels:
- processed — the node has processed the slot, but it may still be skipped. Fastest, least certain.
- confirmed — a supermajority of the cluster has voted on the block. A good balance for most apps.
- finalized — the block is rooted and effectively irreversible. Slowest, safest.
For subscriptions, if you don’t set a commitment, Solana defaults to finalized. That’s the conservative choice, but it also means you wait longer for each notification. Trading logic often uses confirmed to move faster while accepting a small reorg risk, while anything touching settlement or accounting sticks with finalized. Pick the level per subscription based on how much you’d regret acting on a change that later reverts.
How to Open a Solana WebSocket Connection
The quick way: test with wscat
Before writing any app code, you can poke a Solana WebSocket subscription straight from a terminal with wscat:
bash
wscat -c wss://api.mainnet-beta.solana.com
Once connected, paste a subscription request:
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
"SysvarC1ock11111111111111111111111111111111",
{ "encoding": "jsonParsed", "commitment": "confirmed" }
]
}
The server replies with a subscription ID, then streams accountNotification messages as the account changes. This is the fastest way to confirm an endpoint works and to see the raw notification shape.
With @solana/kit (the current TypeScript SDK)
The JavaScript tooling changed in a way that trips people up. The 2.x line of @solana/web3.js was renamed to @solana/kit, and the old Connection class is gone. Kit splits functionality into two objects: Rpc for HTTP and RpcSubscriptions for WebSockets, each taking its own transport. Node.js 20 or higher is recommended.
bash
npm install @solana/kit
ts
import {
createSolanaRpcSubscriptions,
address,
} from '@solana/kit';
const rpcSubscriptions = createSolanaRpcSubscriptions(
'wss://YOUR_WSS_ENDPOINT'
);
const abortController = new AbortController();
const notifications = await rpcSubscriptions
.accountNotifications(address('ACCOUNT_PUBKEY'), { commitment: 'confirmed' })
.subscribe({ abortSignal: abortController.signal });
for await (const notification of notifications) {
console.log('Account changed:', notification);
}
// Later, to stop:
// abortController.abort();
Kit’s subscriptions are async iterables, and you cancel them with an AbortSignal rather than a manual unsubscribe call. Separating the two transports also removes a long-standing bug (more on that in the FAQ).
With legacy @solana/web3.js
Plenty of existing code still runs on @solana/web3.js 1.x, which now sits on a maintenance branch — security fixes only, no new features. There it’s the Connection class with helpers like onAccountChange and onLogs:
ts
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://YOUR_HTTP_ENDPOINT', {
wsEndpoint: 'wss://YOUR_WSS_ENDPOINT',
commitment: 'confirmed',
});
const subId = connection.onAccountChange(
new PublicKey('ACCOUNT_PUBKEY'),
(accountInfo) => console.log('Account changed:', accountInfo)
);
// connection.removeAccountChangeListener(subId);
Pass wsEndpoint explicitly. On v1 the library otherwise guesses the WebSocket URL from the HTTP URL, and the guess frequently fails against managed endpoints. New projects should start on Kit.
Connection Lifecycle and Common Pitfalls
A Solana WebSocket connection is not permanent. Network blips, endpoint maintenance, and idle timeouts all close it, and the server won’t replay what you missed while you were gone. Build for that from the start:
- Reconnect and re-subscribe. On disconnect, reopen the socket and send every subscription request again. Track your subscription IDs so you can rebuild state.
- Keep the connection alive. Send periodic pings so idle timeouts don’t drop you. Raw WebSocket clients don’t do this for you.
- Unsubscribe when done. Each subscription consumes server resources. Call the matching
*Unsubscribe(or abort in Kit) when you no longer need a feed, and remembersignatureSubscribecleans itself up after it fires. - Use
wss://, nothttps://. A common first error is pointing a WebSocket at the HTTP URL. Mainnet endpoints requirewss://. - Mind the limits. Public endpoints cap concurrent connections, subscription counts, and message volume. Heavy workloads need a dedicated endpoint.
Treat disconnects as normal and test your reconnect path, rather than assuming the socket stays open.
Getting a Solana WebSocket Endpoint
To subscribe to anything, you need a wss:// endpoint. Solana’s public mainnet endpoint (wss://api.mainnet-beta.solana.com) works for quick tests but is heavily rate-limited and not meant for production traffic.
For a stable connection with usable limits, most teams use a managed provider. NOWNodes is one option that gives you a WebSocket endpoint tied to your API key, covering both Solana mainnet and testnet, plus archive access for historical reads and a Debug and Trace API. According to the Solana Compass directory, its entry plan runs at EUR 20/month for 1M requests, which suits early-stage projects that don’t yet need validator-proximity or MEV-specific features. You grab the endpoint from the dashboard, drop the wss:// URL into Kit’s createSolanaRpcSubscriptions, and your subscriptions run against it.
Whichever provider you choose, test against devnet or testnet first, then move to a dedicated mainnet endpoint sized for your subscription load.
Conclusion
Solana WebSocket subscriptions turn the blockchain into a live feed your app can react to. Open one connection, subscribe to the events you care about with methods like accountSubscribe, logsSubscribe, programSubscribe, or signatureSubscribe, and let the server push updates as they happen. Set commitment per subscription to balance speed against certainty, use @solana/kit for new code, and design for reconnects from day one. Get those pieces right and you have a real-time Solana app that stays in sync without hammering an endpoint.
FAQ
Is the Solana WebSocket API free to use?
The public endpoint is free but heavily rate-limited and unsuitable for production. Managed providers offer higher connection and subscription limits on paid plans, and some include a free tier with capped WebSocket traffic. For anything beyond testing, budget for a dedicated endpoint.
Can I use Solana WebSocket subscriptions directly in a browser?
Yes. Browsers support the native WebSocket API, and @solana/kit works client-side. Be careful not to expose a private or high-limit endpoint key in frontend code, and remember browsers can close idle sockets, so your reconnect logic still applies.
How many subscriptions can one WebSocket connection handle?
A single connection can hold multiple active subscriptions at once, so you don’t need a separate socket per account. The practical ceiling is set by your provider’s limits on concurrent subscriptions and message throughput, not by the protocol itself.
Why am I getting a 404 or 503 when I open a Solana WebSocket connection?
On @solana/web3.js 1.x this usually means the library derived the wrong WebSocket URL from your HTTP URL. Pass wsEndpoint explicitly to Connection, or migrate to @solana/kit, which takes the HTTP and WebSocket transports as separate inputs and avoids the guess entirely.



