Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Network & Protocol Guides12 min read

Solana RPC WebSocket: Pubsub Methods, Subscriptions, and Connection Lifecycle

Learn how to use Solana's PubSub WebSocket API for real-time account, program, and log subscriptions, with a Node.js example and troubleshooting guide.

TL;DR

This guide explains Solana's PubSub WebSocket API, covering subscription methods, connection lifecycle, and common failures. It includes a runnable Node.js example using @solana/web3.js and a troubleshooting checklist.

Direct Answer: What is Solana RPC WebSocket?

Solana's RPC WebSocket API (also called PubSub) lets you receive real-time notifications about account changes, program activity, and transaction logs over a persistent WebSocket connection. Unlike polling the standard JSON-RPC HTTP endpoint, WebSocket subscriptions push data to your client as soon as it's available, reducing latency and load. The official endpoint is ws://<ADDRESS>/ or wss://<ADDRESS>/ (e.g., ws://localhost:8899 for a local validator, or a public RPC provider's wss URL). This guide covers the three primary subscription methods—accountSubscribe, programSubscribe, and logsSubscribe—plus slot and root subscriptions, and explains the full connection lifecycle with a runnable Node.js example.

Solana's WebSocket API is JSON-RPC 2.0 over WebSocket, with a request/response pattern for subscriptions and a notification format for incoming events. It's distinct from EVM-style eth_subscribe because Solana's model is account-centric and uses leader-schedule-based notification timing. Understanding these differences is key to building reliable real-time applications.

  • Endpoint families: ws://localhost:8899 for local development, wss:// for public RPC providers (e.g., wss://api.mainnet-beta.solana.com).
  • Three main subscription methods: accountSubscribe, programSubscribe, logsSubscribe.
  • Additional methods: slotSubscribe, rootSubscribe, and signatureSubscribe (though signature notifications are often handled via polling).
  • Unsubscribe via accountUnsubscribe, programUnsubscribe, logsUnsubscribe, etc.

How Solana PubSub Works: Architecture and Notification Flow

Solana's PubSub API is built on JSON-RPC 2.0. To subscribe, you send a request with a method name and parameters, and the server responds with a subscription ID. From then on, the server sends notification messages (JSON-RPC 2.0 notifications) to the client whenever the subscribed event occurs. Each notification includes the subscription ID and the data payload.

The notification cadence is tied to Solana's leader schedule. For accountSubscribe and programSubscribe, notifications are sent when the account or program data changes, but only after a slot is confirmed. For logsSubscribe, notifications are sent for each log message emitted by transactions that touch the subscribed program or account. This means you might see bursts of notifications around leader changes, and there can be a slight delay compared to the actual transaction processing.

The official Solana documentation (Solana RPC WebSocket Methods) specifies the exact request and notification formats. For example, a subscription request looks like: {"jsonrpc":"2.0","id":1,"method":"accountSubscribe","params":["PUBKEY",{"encoding":"base64","commitment":"confirmed"}]}. The response is {"jsonrpc":"2.0","result":"SUBSCRIPTION_ID","id":1}. Notifications then arrive as {"jsonrpc":"2.0","method":"accountNotification","params":{"result":{"context":{"slot":123},"value":{...}},"subscription":"SUBSCRIPTION_ID"}}.

  • Request format: JSON-RPC 2.0 request with method, params, and id.
  • Response format: JSON-RPC 2.0 response with result (subscription ID) and id.
  • Notification format: JSON-RPC 2.0 notification with method (e.g., accountNotification) and params containing subscription ID and result.
  • Commitment levels: processed, confirmed, finalized affect when notifications are sent.

Subscription Methods Deep Dive

The three main subscription methods are accountSubscribe, programSubscribe, and logsSubscribe. Each has specific parameters and notification payloads.

accountSubscribe monitors a single account's state changes. Parameters: account pubkey (base58 string) and optional config object with commitment and encoding. The notification payload includes the account's data, lamports, owner, executable flag, rent epoch, and slot context.

programSubscribe monitors all accounts owned by a program. Parameters: program pubkey and optional config with encoding and filters (e.g., dataSize or memcmp). Notifications are similar to account notifications but for any account owned by the program.

logsSubscribe monitors transaction logs. Parameters: a filter (either "all", {"mentions": [pubkey]} for account or program mentions, or {"mentions": [pubkey], "commitment": "confirmed"}) and optional commitment. Notifications include the logs array, signature, and slot.

Additionally, slotSubscribe and rootSubscribe provide slot and root updates, useful for tracking chain progress. signatureSubscribe is also available but often less used because it requires knowing the signature in advance.

  • accountSubscribe: params: [pubkey, {encoding, commitment}]
  • programSubscribe: params: [programId, {encoding, filters, commitment}]
  • logsSubscribe: params: [filter, {commitment}] where filter is "all" or {"mentions": [pubkey]}
  • slotSubscribe: params: []
  • rootSubscribe: params: []
  • Unsubscribe methods: accountUnsubscribe, programUnsubscribe, logsUnsubscribe, etc.

Runnable Node.js Example with @solana/web3.js

The easiest way to use Solana WebSocket subscriptions is via the @solana/web3.js library, which wraps the raw WebSocket API. The Connection class provides methods like onAccountChange, onProgramAccountChange, and onLogs that handle subscription and notification parsing automatically.

Below is a complete example that subscribes to account changes for a given address, program account changes for a program ID, and logs for a program. It also demonstrates how to handle reconnection and cleanup. To run it, install @solana/web3.js and ws (for WebSocket in Node.js).

Expected output: The script will print subscription IDs and then log notifications as they arrive. Since real-time data depends on network activity, you may need to trigger some transactions to see notifications. The example includes a timeout to exit after 30 seconds.

  • Use Connection with a WebSocket URL (e.g., wss://api.mainnet-beta.solana.com).
  • onAccountChange returns a subscription ID (number).
  • onProgramAccountChange accepts a program ID and optional filter.
  • onLogs accepts a filter (e.g., 'all' or {mentions: [programId]}).
  • Always handle error and close events on the WebSocket to implement reconnection.
// Install: npm install @solana/web3.js ws
const { Connection, PublicKey } = require('@solana/web3.js');

// Replace with your endpoint (e.g., wss://api.mainnet-beta.solana.com)
const wsUrl = 'wss://api.mainnet-beta.solana.com';
const connection = new Connection(wsUrl, 'confirmed');

// Example: subscribe to account changes for a known token account
const accountPubkey = new PublicKey('YOUR_ACCOUNT_PUBKEY');
const subId = connection.onAccountChange(accountPubkey, (accountInfo, context) => {
  console.log('Account change at slot', context.slot);
  console.log('Lamports:', accountInfo.lamports);
  console.log('Data length:', accountInfo.data.length);
}, 'confirmed');

// Subscribe to program account changes (e.g., SPL Token program)
const programId = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const programSubId = connection.onProgramAccountChange(programId, (accountInfo, context) => {
  console.log('Program account change at slot', context.slot);
}, 'confirmed');

// Subscribe to logs for the same program
const logsSubId = connection.onLogs(programId, (logs, context) => {
  console.log('Logs at slot', context.slot);
  console.log('Signature:', logs.signature);
  console.log('Logs:', logs.logs);
}, 'confirmed');

console.log('Subscribed with IDs:', subId, programSubId, logsSubId);

// Keep the process alive and handle cleanup
setTimeout(() => {
  connection.removeAccountChangeListener(subId);
  connection.removeProgramAccountChangeListener(programSubId);
  connection.removeOnLogsListener(logsSubId);
  console.log('Unsubscribed and exiting.');
  process.exit(0);
}, 30000);

// Expected output (example):
// Subscribed with IDs: 1 2 3
// Account change at slot 123456
// Lamports: 2039280
// Data length: 165
// ... (notifications as they occur)

Connection Lifecycle: Keepalive, Reconnection, and Resubscription

A WebSocket connection to Solana's PubSub endpoint is not permanent. It can drop due to network issues, server restarts, or idle timeouts. To build a robust client, you must handle the connection lifecycle: connect, keep alive, detect close, and resubscribe.

Keepalive: Most WebSocket servers send ping frames periodically. In Node.js, the ws library automatically responds to pings, but you can also send application-level pings. Solana's official docs don't specify a keepalive interval, but it's common to send a ping every 30 seconds to prevent idle timeouts. If you're using @solana/web3.js, the library handles pings internally, but you should still listen for close events.

Reconnection: When the connection closes, you need to reconnect and re-establish all subscriptions. The @solana/web3.js Connection class does not automatically resubscribe; you must implement it. A common pattern is to wrap the subscription setup in a function and call it on reconnect. You can use a library like reconnecting-websocket or write your own logic.

Resubscription: After reconnecting, you must call the subscription methods again. Keep track of your subscription IDs and the parameters used, so you can re-subscribe with the same filters. Be aware that subscription IDs may change after reconnection.

For a deeper dive into generic WebSocket disconnection fixes, see our generic WebSocket RPC disconnection fixes.

  • Listen for open, message, error, and close events.
  • Implement exponential backoff for reconnection attempts (e.g., 1s, 2s, 4s, max 30s).
  • On reconnect, re-subscribe to all active subscriptions.
  • Use a heartbeat (ping/pong) to detect dead connections.
  • Consider using a library like reconnecting-websocket for automatic reconnection.
// Example reconnection logic using ws and @solana/web3.js
const WebSocket = require('ws');
const { Connection } = require('@solana/web3.js');

let connection;
let subscriptions = [];

function setupSubscriptions() {
  // Clear old subscriptions
  subscriptions.forEach(id => connection.removeAllListeners(id));
  subscriptions = [];

  // Re-subscribe
  const subId = connection.onAccountChange(accountPubkey, callback, 'confirmed');
  subscriptions.push(subId);
  // ... add other subscriptions
}

function connect() {
  connection = new Connection(wsUrl, 'confirmed');
  connection._ws.on('close', () => {
    console.log('Connection closed. Reconnecting in 5s...');
    setTimeout(connect, 5000);
  });
  connection._ws.on('open', () => {
    console.log('Connected. Setting up subscriptions.');
    setupSubscriptions();
  });
}

connect();

Common Failures and Troubleshooting Checklist

Even with a solid implementation, you may encounter issues. Here are common failures and how to fix them.

Disconnect under load: High-throughput subscriptions can overwhelm your client or the server. If you see frequent disconnects, reduce the number of subscriptions or use filters to narrow the data. Also ensure your client processes messages quickly; if your callback is slow, it can block the event loop and cause timeouts.

Notification lag: Notifications are tied to the leader schedule and commitment level. If you need faster updates, use processed commitment, but be aware that data may be reverted. For final data, use confirmed or finalized.

Filter misuse: For programSubscribe, filters like dataSize and memcmp must be correctly formatted. memcmp requires offset and bytes (base58 encoded). If you get no notifications, double-check your filter logic.

Connection errors: Common errors include Unexpected server response: 403 (if the endpoint requires an API key) or WebSocket is closed before the connection is established. Ensure your URL is correct and you have network access.

Subscription ID not found: If you try to unsubscribe with an invalid ID, you'll get an error. Keep track of IDs and remove listeners properly.

  • Check your endpoint URL: use wss:// for secure connections, and ensure it's reachable.
  • Verify commitment level: processed is fastest but less reliable; confirmed is a good default.
  • Use filters to reduce data volume: dataSize and memcmp for program subscriptions.
  • Monitor your WebSocket connection state and implement reconnection with backoff.
  • Test with a local validator (solana-test-validator) to avoid rate limits.
  • If using a public RPC provider, check their documentation for rate limits and WebSocket-specific rules.

Tradeoffs and Limitations

Solana's PubSub API is powerful but has tradeoffs. Notifications are not guaranteed to be in order or complete; you may miss events if the connection drops. Also, the notification cadence depends on the leader schedule, so you might see bursts of activity rather than a steady stream.

Endpoint retention: Public RPC providers may have different retention policies for WebSocket connections. Some may disconnect idle connections after a certain period. Always implement reconnection logic.

Notification timing varies by provider and network load. Do not rely on exact timing for critical applications; use commitment levels to balance speed and reliability.

Compared to EVM eth_subscribe, Solana's model is account-centric and requires understanding of Solana's account model. For example, programSubscribe is similar to eth_subscribe for contract events but operates on account state changes.

For production, consider using a dedicated WebSocket provider with high availability. OnFinality's API service offers reliable WebSocket endpoints, and you can compare Solana RPC endpoints in our RPC Assistant.

  • Notifications are not guaranteed to be lossless; implement your own reconciliation if needed.
  • Public endpoints may have rate limits; check your provider's documentation.
  • WebSocket connections are stateful; reconnection is mandatory for production.
  • Use finalized commitment for irreversible data, but expect higher latency.
  • For high-throughput use cases, consider using a dedicated streaming service like Helius LaserStream (independent third-party).

Next Steps and Further Reading

Now that you understand Solana's WebSocket API, you can build real-time applications such as transaction monitors, portfolio trackers, or arbitrage bots. Start with the official Solana RPC WebSocket Methods documentation for the exact JSON formats.

If you're new to Solana development, explore our Solana network guide for an overview. For pricing considerations, see RPC pricing. If you encounter disconnection issues, refer to our generic WebSocket RPC disconnection fixes.

For a broader understanding of RPC services, visit the OnFinality Learn hub for more tutorials and guides.

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started