Learn how to use Tatum's Solana WebSocket endpoint to subscribe to real-time on-chain events including account updates, transaction logs, signature confirmations, and block notifications.
Solana's WebSocket PubSub interface pushes real-time notifications to your application over a persistent connection, with no polling required.
With Tatum's Solana WebSocket endpoint, you can subscribe to:
- Account updates
- Transaction logs
- Signature confirmations
- Program account changes
- Slot progression
- Blocks
Solana Mainnet WebSocket endpoint
| Interface | Endpoint |
|---|---|
| WSS (Mainnet) | wss://solana-mainnet.gateway.tatum.io |
For JSON-RPC (HTTP) and the full network list, see Solana RPC.
Getting started
Environment setup
This guide uses Node.js 22+ with the native WebSocket API and TypeScript via tsx.
-
Install Node.js (version 22+):
brew install node node -v -
Create your project:
mkdir tatum-wss-test cd tatum-wss-test npm init -y npm install -D tsx typescript @types/node -
Get your API key from the Tatum Dashboard.
Connection and authentication
The API key is passed as a path segment on the WebSocket URL.
const API_KEY = "YOUR_API_KEY";
const ENDPOINT = `wss://solana-mainnet.gateway.tatum.io/${API_KEY}`;
const ws = new WebSocket(ENDPOINT);Quick start checklist
Before opening subscriptions, confirm:
- Your WebSocket connection opens without error
- You receive a numeric subscription ID in the server response after the first subscribe call
- Your service can hold long-lived outbound connections open
- You have a reconnect strategy — WebSocket connections drop; assume it and rebuild on reconnect
- You send a ping every 60 seconds to prevent the connection from timing out due to inactivity
End-to-end example
The following script connects to the endpoint and subscribes to log notifications for all finalized non-vote transactions. It is the fastest way to confirm your credentials and connection are working.
const API_KEY = "YOUR_API_KEY";
const ENDPOINT = `wss://solana-mainnet.gateway.tatum.io/${API_KEY}`;
const ws = new WebSocket(ENDPOINT);
ws.onopen = () => {
console.log("connected");
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "logsSubscribe",
params: [
"all",
{ commitment: "finalized" }
]
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data as string);
if (msg.result !== undefined) {
console.log("subscription id:", msg.result);
return;
}
if (msg.method === "logsNotification") {
const { signature, err, logs } = msg.params.result.value;
console.log("tx:", signature, "| status:", err ? "failed" : "ok");
console.log("logs:", logs);
}
};
ws.onerror = (err) => console.error("error:", err);
ws.onclose = () => console.log("connection closed");Run it with:
npx tsx index.ts
Note
tsxruns TypeScript files directly with notsconfig.jsonrequired.
AttentionThis example streams live Solana transactions and will keep running until you stop it. On macOS, press Ctrl+C in the terminal to stop the stream once you have confirmed it is working.
Example requests
Note: These are minimal examples. For production, implement reconnection with exponential backoff, track subscription IDs, and call the matching unsubscribe method when a subscription is no longer needed.
accountSubscribe
Subscribe to notifications whenever a specific account's lamports or data change.
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "accountSubscribe",
params: [
"CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12",
{
encoding: "jsonParsed",
commitment: "finalized"
}
]
}));Each notification arrives as accountNotification and includes the slot, lamport balance, owner program, and full account data in the requested encoding. Use base64 if you need raw bytes.
To unsubscribe, call accountUnsubscribe with the subscription ID returned by the server:
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 2,
method: "accountUnsubscribe",
params: [SUBSCRIPTION_ID]
}));logsSubscribe
Subscribe to transaction log output. Filter by transaction type or by a specific address.
All non-vote transactions:
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "logsSubscribe",
params: [
"all",
{ commitment: "finalized" }
]
}));Transactions mentioning a specific address:
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "logsSubscribe",
params: [
{ mentions: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"] },
{ commitment: "confirmed" }
]
}));Available filter values:
| Filter | Description |
|---|---|
"all" | All transactions except simple vote transactions |
"allWithVotes" | All transactions, including vote transactions |
{ "mentions": ["<pubkey>"] } | Transactions mentioning a single address (exactly one address per call) |
Notifications arrive as logsNotification and include the transaction signature, slot, error status, and raw log lines from the BPF runtime.
signatureSubscribe
Subscribe to confirmation status for a submitted transaction. The subscription closes automatically once the terminal confirmation is delivered.
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "signatureSubscribe",
params: [
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQo",
{
commitment: "finalized",
enableReceivedNotification: false
}
]
}));Set enableReceivedNotification: true to receive an early receivedSignature notification when the RPC node first sees the transaction, before confirmation.
programSubscribe
Subscribe to changes in all accounts owned by a specific program.
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "programSubscribe",
params: [
"11111111111111111111111111111111",
{
encoding: "jsonParsed",
commitment: "finalized"
}
]
}));Notifications arrive as programNotification. Use specific program addresses rather than broad ones to keep notification volume manageable.
slotSubscribe
Subscribe to slot progression for liveness monitoring and network clock synchronization.
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "slotSubscribe",
params: []
}));Notifications arrive as slotNotification and include the slot number, parent slot, and root slot.
rootSubscribe
Subscribe to root slot changes. The root is the slot that has been finalized by the cluster.
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "rootSubscribe",
params: []
}));blockSubscribe
Subscribe to new block notifications.
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "blockSubscribe",
params: [
"all",
{
commitment: "confirmed",
encoding: "jsonParsed",
transactionDetails: "full",
showRewards: false
}
]
}));Commitment levels
Most subscription methods accept an optional commitment parameter. If omitted, finalized is the default.
| Commitment | Description |
|---|---|
processed | The node has received the transaction. Fastest, lowest certainty. |
confirmed | The transaction has been voted on by a supermajority of the cluster. |
finalized | The transaction is finalized and will not be rolled back. Default. |
Troubleshooting
| Symptom | Check |
|---|---|
| Connection drops immediately | Verify your API key is valid and included in the URL |
| No subscription ID returned | The subscribe call was malformed; check the JSON structure |
| No notifications received | Widen your filter or lower commitment to confirmed |
| Frequent disconnects | Add reconnect logic with exponential backoff and jitter |
| High notification volume | Narrow your filter — use mentions instead of all, or move to Solana gRPC for high-throughput use cases |
WebSocket vs gRPC
WebSocket is well suited for: signature confirmation polling, wallet UI balance updates, slot heartbeats, and low-volume program monitoring with tight filters.
For high-throughput use cases (multiple parallel subscriptions, trading infrastructure, indexers), consider Solana Yellowstone gRPC, which delivers typed, structured data without the parsing overhead of raw log lines.