Solana WebSocket API (Pub/Sub) — /ws/sol

A single WebSocket endpoint for Solana JSON-RPC Pub/Sub: subscribe to account, signature, log, program, slot, root, block, vote, and slot-update events and receive server-pushed notifications in real time.

Network
Solana
Scope
sol.pubsub
Auth
x-token: $TRIPORT_API_KEY on the upgrade request (Authorization: Bearer also accepted), an auth frame, or ?api-key= query
Rate limit
Per-method tier gating — free / basic / pro (see Methods by plan)

Endpoint and plans: Solana RPC endpoint.

/ws/sol is one WebSocket connection that multiplexes all nine Solana JSON-RPC Pub/Sub method families. Every frame is a JSON-RPC 2.0 message; the method field disambiguates a subscribe request, an unsubscribe request, and a server-pushed notification.

The lifecycle is: open the socket, send a *Subscribe request, and the server replies with a SubscribeAck carrying an integer result — the subscription id. From then on the server pushes *Notification frames whose params.subscription matches that id, until you send the matching *Unsubscribe (or the socket closes). Keep the subscription id — it is the only handle you have to stop a stream.

Each method family carries a minimum tier requirement. accountSubscribe and signatureSubscribe are available on the Free trial; logsSubscribe, programSubscribe, slotSubscribe, and rootSubscribe require basic; and blockSubscribe, voteSubscribe, and slotsUpdatesSubscribe require pro. Subscribing to a method above your tier closes the connection with WS close code 4003 (forbidden) — see Errors.

Parameters

A subscribe frame is a JSON-RPC 2.0 request: {jsonrpc, id, method, params}.

jsonrpcstringrequired
Must be "2.0".
idinteger | stringrequired
Client-chosen request id; echoed back on the ack.
methodstringrequired
One of the nine subscribe methods (see Methods by plan).
paramsarrayrequired
Positional params array — shape depends on the method. For accountSubscribe / programSubscribe it is [<base58 pubkey>, <SolSubscribeParams>?]; for slot/root-style methods it is usually empty.
SolSubscribeParams (config )object
commitmentstringoptional
One of processed, confirmed, finalized. Defaults to confirmed.
encodingstringoptional
Account data encoding: base58, base64, base64+zstd, or jsonParsed.

Response

The first server frame is the SubscribeAck, whose result is the subscription id:

Subsequent frames are server-pushed *Notification envelopes. An accountNotification looks like:

{
  "jsonrpc": "2.0",
  "method": "accountNotification",
  "params": {
    "subscription": 24040,
    "result": {
      "context": { "slot": 348392041 },
      "value": {
        "lamports": 10000000000,
        "owner": "11111111111111111111111111111111",
        "data": ["", "base64"],
        "executable": false,
        "rentEpoch": 361
      }
    }
  }
}
result (on ack)integer
The subscription id. Pass it to the matching *Unsubscribe.
method (on notification)string
The notification method, e.g. accountNotification.
params.subscriptioninteger
The subscription id this notification belongs to.
params.resultobject
The event payload — shape is per-method (account state, signature status, log batch, slot info, etc.).

Methods by plan

The lowest plan that may open each subscription; every higher plan includes it.

MethodPlan fromSubscribes to
accountSubscribeFree (trial)Changes to a single account, by base-58 pubkey.
signatureSubscribeFree (trial)Confirmation status of a single transaction signature.
logsSubscribeBasicTransaction logs (all, or filtered by mentioned account).
programSubscribeBasicChanges to accounts owned by a program.
slotSubscribeBasicEach new processed slot.
rootSubscribeBasicEach new root the node sets.
blockSubscribeProFull block notifications.
voteSubscribeProNew vote transactions observed by the node.
slotsUpdatesSubscribeProFine-grained per-slot update events.

A subscribe above your plan is refused: the server sends an error frame with required_tier and closes the socket with code 4003 (see Errors).

If a subscription stops delivering without a close, see why a subscription goes silent. For all Solana streaming options side by side, see the Solana streams overview; need filters across many accounts? Yellowstone gRPC. How Pub/Sub differs from Geyser-based gRPC: the Solana Geyser plugin explained.

Errors

Application errors are delivered as a final WSError frame immediately before the socket closes with one of the canonical WebSocket close codes. The frame mirrors the HTTP error envelope so SDKs can reuse the same error classes — see the shared errors reference for the full envelope.

CodeMeaningWhen it happens
4001unauthorizedMissing or invalid API key on the upgrade / auth frame.
4003forbiddenSubscribing to a method above your tier (tier_insufficient) or an unknown method (method_unknown). The frame carries current_tier, required_tier, method, and an upgrade_url.
4029rate_limitedSubscribe rate exceeded for your tier; the frame carries retry_after_sec and limit_rps.
4030trial_expiredThe account's trial has ended; the frame carries an upgrade_url.

Example WSError frame sent before a 4003 close:

{
  "error": "forbidden",
  "message": "method requires a higher tier",
  "current_tier": "free",
  "required_tier": "pro",
  "method": "blockSubscribe",
  "category": "sol_ws_pubsub",
  "upgrade_url": "https://triport.io/billing/upgrade?tier=pro"
}

Examples

JavaScript (fetch)

The browser WebSocket API can't set custom headers, so authenticate with the ?api-key= query form:

const ws = new WebSocket(
  `wss://triport.io/ws/sol?api-key=${process.env.TRIPORT_API_KEY}`
);


const subs = new Map(); // subscription id -> label


ws.addEventListener("open", () => {
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "accountSubscribe",
    params: [
      "SysvarC1ock11111111111111111111111111111111",
      { commitment: "finalized", encoding: "jsonParsed" },
    ],
  }));
});


ws.addEventListener("message", (ev) => {
  const msg = JSON.parse(ev.data);
  if (msg.id === 1 && typeof msg.result === "number") {
    subs.set(msg.result, "clock-account");
    console.log("subscribed, id =", msg.result);
  } else if (msg.method === "accountNotification") {
    console.log("account changed at slot", msg.params.result.context.slot);
  }
});

Notes

  • Hold onto the subscription id. The ack's result is the only handle for unsubscribing; route notifications by params.subscription when you have several active subscriptions on one socket.
  • Multiple subscriptions, one socket. A single /ws/sol connection can carry many subscriptions across different method families — open one socket per client, not one per subscription.
  • Tier gating is per method. A free connection can run accountSubscribe and signatureSubscribe indefinitely, but a blockSubscribe on the same socket triggers a 4003 close. Check required_tier in the error frame to build an upgrade prompt without a second API call.
  • Commitment trade-off. processed arrives first but may be rolled back; finalized is the strongest guarantee. The default is confirmed.
  • Related channels: the other Solana connection options are the Firehose (WebSocket) at wss://triport.io/ws/sol-stream, gRPC (Yellowstone) at https://grpc.triport.io and the shred stream at wss://triport.io/ws/sol-preexec (all Pro). The streaming overview lists every channel: Streaming overview.