WebSocket Guide
Nodius provides a WebSocket proxy for real-time streaming of Solana data. Subscribe to account changes, transaction logs, slot updates, and more.
Connecting
WebSocket connections require authentication via a session token or API key.
Endpoint
wss://rpc.nodius.xyz/ws
Authentication
Three options are available:
Option 1: Authorization header (session token or API key)
GET /ws HTTP/1.1
Authorization: Bearer <session-token-or-api-key>
Upgrade: websocket
Connection: Upgrade
Option 2: X-Api-Key header (API key only)
GET /ws HTTP/1.1
X-Api-Key: srpc_live_<base64url>
Upgrade: websocket
Connection: Upgrade
Option 3: Sec-WebSocket-Protocol header (for browser clients that don't support custom headers)
Both auth.<token> and solana-rpc subprotocols must be sent together:
GET /ws HTTP/1.1
Sec-WebSocket-Protocol: auth.<token>, solana-rpc
Upgrade: websocket
Connection: Upgrade
The server responds with Sec-WebSocket-Protocol: solana-rpc โ the token is never echoed back for security.
Where <token> is either a session token (from the challenge/verify flow) or a persistent API key (from POST /account/api-key).
Obtain a Session Token
// Using the SDK
const rpc = new NodiusClient("https://rpc.nodius.xyz", {
keypair,
auth: "session",
});
const token = await rpc.getSessionToken();
Or manually via the challenge-response flow.
Connect
// Using Authorization header
const ws = new WebSocket("wss://rpc.nodius.xyz/ws", {
headers: {
Authorization: `Bearer ${token}`,
},
});
// Or using Sec-WebSocket-Protocol (browser-friendly)
const ws = new WebSocket(
"wss://rpc.nodius.xyz/ws",
[`auth.${token}`]
);
ws.on("open", () => {
console.log("Connected");
});
ws.on("message", (data) => {
const msg = JSON.parse(data.toString());
console.log(msg);
});
ws.on("close", (code, reason) => {
console.log(`Disconnected: ${code} ${reason}`);
});
Connection Requirements
- Minimum 100 credits to establish a connection
- Auto-disconnect below 10 credits โ the server closes the connection with code
4002and reason"insufficient_credits" - Token must be valid (not expired)
Subscriptions
Creating a Subscription
Send a JSON-RPC subscription request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{
"encoding": "base64",
"commitment": "confirmed"
}
]
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": 42
}
The result is the subscription ID. Use it to unsubscribe later.
Cost: 5 credits per subscription created (WS_SUBSCRIBE).
Receiving Notifications
{
"jsonrpc": "2.0",
"method": "accountNotification",
"params": {
"subscription": 42,
"result": {
"context": {
"slot": 285943216
},
"value": {
"data": ["base64data...", "base64"],
"executable": false,
"lamports": 1000000000,
"owner": "11111111111111111111111111111111",
"rentEpoch": 400,
"space": 200
}
}
}
}
Cost: 1 credit per notification received (WS_NOTIFICATION).
Unsubscribing
{
"jsonrpc": "2.0",
"id": 2,
"method": "accountUnsubscribe",
"params": [42]
}
Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": true
}
Unsubscribing does not refund the subscription cost but stops further notification charges.
Supported Subscription Methods
| Subscribe | Unsubscribe | Description | Access |
|---|---|---|---|
accountSubscribe |
accountUnsubscribe |
Account data changes | All |
logsSubscribe |
logsUnsubscribe |
Transaction logs (all or by pubkey) | All |
programSubscribe |
programUnsubscribe |
All accounts owned by a program | All |
signatureSubscribe |
signatureUnsubscribe |
Transaction confirmation | All |
slotSubscribe |
slotUnsubscribe |
Slot number updates | All |
rootSubscribe |
rootUnsubscribe |
Finalized root slot | All |
blockSubscribe |
blockUnsubscribe |
Full block data streaming | Credit-gated |
accountSubscribe
Monitor changes to a specific account.
{
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
"pubkeyBase58...",
{"encoding": "base64", "commitment": "confirmed"}
]
}
logsSubscribe
Monitor transaction logs.
// All transactions
{
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": ["all", {"commitment": "confirmed"}]
}
// Transactions mentioning a specific pubkey
{
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{"mentions": ["pubkeyBase58..."]},
{"commitment": "confirmed"}
]
}
programSubscribe
Monitor all accounts owned by a program.
{
"jsonrpc": "2.0",
"id": 1,
"method": "programSubscribe",
"params": [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{"encoding": "base64", "commitment": "confirmed"}
]
}
Warning:
programSubscribeon popular programs (Token Program, System Program) generates very high notification volume and will consume credits rapidly.
signatureSubscribe
Wait for a transaction to be confirmed.
{
"jsonrpc": "2.0",
"id": 1,
"method": "signatureSubscribe",
"params": [
"txSignatureBase58...",
{"commitment": "confirmed"}
]
}
This subscription automatically unsubscribes after delivering one notification.
slotSubscribe / rootSubscribe
{"jsonrpc": "2.0", "id": 1, "method": "slotSubscribe"}
{"jsonrpc": "2.0", "id": 1, "method": "rootSubscribe"}
Note:
slotSubscribefires every ~400ms. At 1 credit per notification, this costs approximately 150 credits per minute.
Subscription Limits
WebSocket limits are per-account operational safety caps applied uniformly to all accounts.
| Limit | Default |
|---|---|
| Max concurrent connections per account | 50 |
| Max concurrent subscriptions per account | 100 |
Attempting to create a subscription beyond the limit returns an error:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "Subscription limit reached (100/100)"
}
}
Credit Metering
| Event | Credits |
|---|---|
| Creating a subscription | 5 |
| Each notification received | 1 |
| Unsubscribing | 0 (free) |
| Connection/disconnection | 0 (free) |
Estimating Costs
| Subscription | Approx. Frequency | Credits/Hour |
|---|---|---|
slotSubscribe |
~2.5/sec | ~9,000 |
accountSubscribe (active account) |
varies | varies |
accountSubscribe (cold account) |
rare | ~5 (subscription only) |
signatureSubscribe |
1 (then auto-unsub) | ~6 |
logsSubscribe (all) |
very high | 10,000+ |
programSubscribe (Token Program) |
extremely high | 100,000+ |
Be mindful of high-frequency subscriptions. Monitor your credit balance.
Connection Lifecycle
1. Client connects with token (Authorization header or Sec-WebSocket-Protocol)
2. Server validates token and checks credit balance (โฅ100)
3. Connection established
4. Client sends subscribe/unsubscribe messages
5. Server streams notifications, deducting credits
6. If credits drop below 10:
a. Server sends warning message
b. Server closes connection (code 4002)
7. Client can reconnect after depositing more credits
Close Codes
| Code | Reason | Description |
|---|---|---|
| 1000 | normal |
Clean disconnect by client |
| 1008 | policy_violation |
Invalid or expired token |
| 4001 | auth_failed |
Authentication failed |
| 4002 | insufficient_credits |
Credits below minimum |
| 4003 | rate_limited |
Too many messages |
SDK WebSocket Usage
import { NodiusClient } from "@nodius/sdk";
const rpc = new NodiusClient("https://rpc.nodius.xyz", {
keypair,
auth: "session",
});
// Subscribe to account changes
const subId = await rpc.ws.accountSubscribe(
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
{ encoding: "base64", commitment: "confirmed" },
(notification) => {
console.log("Account changed:", notification);
}
);
// Later: unsubscribe
await rpc.ws.accountUnsubscribe(subId);
// Subscribe to slot changes
await rpc.ws.slotSubscribe((slot) => {
console.log("New slot:", slot);
});
// Clean disconnect
rpc.ws.close();
The SDK handles: - Automatic session token management and renewal - Reconnection with exponential backoff - Subscription restoration after reconnect - Credit balance monitoring
Best Practices
- Unsubscribe when done โ Don't rely on disconnection to clean up subscriptions. Unsubscribe explicitly.
- Monitor credit consumption โ High-frequency subscriptions (slots, logs, active programs) can drain credits quickly.
- Use commitment levels โ
confirmedis a good default.finalizedreduces notification frequency.processedincreases it. - Handle reconnections โ Network issues happen. Implement reconnection logic or use the SDK which handles this automatically.
- Avoid subscribing to the entire log stream โ
logsSubscribe("all")generates enormous traffic. Filter by pubkey when possible.