BNB Smart Chain Pub/Sub WebSocket — /ws/bsc

Subscribe over WebSocket to real-time BNB Smart Chain events — new block headers, filtered logs, and pending-transaction hashes — using the standard eth_subscribe / eth_unsubscribe JSON-RPC shape. Ordinary JSON-RPC calls and batches work on the same socket.

Network
BNB Smart Chain (chain ID 56)
Scope
bsc:rpc (subscription type then gated by tier; see below)
Auth
x-token: $TRIPORT_API_KEY on the upgrade request (Authorization: Bearer also accepted), an auth frame, or ?api-key= query
Rate limit
Per subscription type: newPendingTransactions free · logs basic · newHeads pro. Category bsc_ws_pubsub

Endpoint and plans: BNB Smart Chain RPC endpoint.

Terms on this page: JSON-RPC.

/ws/bsc is the BNB Smart Chain JSON-RPC Pub/Sub channel. Open a single WebSocket connection, send an eth_subscribe request naming one of three subscription types, and the server replies with a hex-encoded subscription id. From then on the server pushes eth_subscription notification frames carrying that id and the event payload until you send eth_unsubscribe (or the socket closes).

You can hold multiple subscriptions on one connection; each notification frame identifies its source via params.subscription, so match incoming events to the id you stored at subscribe time.

The same socket also serves ordinary request/response JSON-RPC: any non-subscribe method (for example eth_call, eth_getLogs, eth_chainId) and JSON-RPC batch arrays are proxied to the same backend as POST /rpc/bsc, under the same scope and rate budget. This lets a client subscribe and query over one connection.

The three subscription types unlock at different tiers. Attempting to subscribe to a type above your plan returns a forbidden error frame followed by a WS close 4003 — the frame includes current_tier, required_tier, and an upgrade_url so you can prompt for an upgrade without a second call.

Parameters

The eth_subscribe request params array holds the subscription type in params[0] and an optional, type-specific filter object in params[1].

logsobject
addressstring | string[]optional
One contract address, or an array of addresses, to match.
topicsarrayoptional
Up to 4 indexed-topic slots. Each slot is a topic string, an array of strings (OR-match), or null (wildcard for that position).

Response

Subscribe acknowledgement — result is the hex subscription id to keep:

Notification (newPendingTransactions):

{
  "jsonrpc": "2.0",
  "method": "eth_subscription",
  "params": {
    "subscription": "0x9cef478923ff08bf67fde6c64013158d",
    "result": "0x68e63ee5b6f7e2b2e9e1cf0d2f5cda0c4f3a9b1d7e8c6a5b4c3d2e1f0a9b8c7d"
  }
}

Notification (newHeads):

{
  "jsonrpc": "2.0",
  "method": "eth_subscription",
  "params": {
    "subscription": "0x1a2b3c4d5e6f70819a0b1c2d3e4f5061",
    "result": {
      "number": "0x7cb5e92",
      "hash": "0x4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c",
      "parentHash": "0x9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f",
      "nonce": "0x0000000000000000",
      "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
      "logsBloom": "0x00000000000000000000000000000000",
      "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
      "stateRoot": "0xdeadbeef00000000000000000000000000000000000000000000000000000000",
      "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
      "miner": "0x0000000000000000000000000000000000000000",
      "difficulty": "0x2",
      "extraData": "0x",
      "gasLimit": "0x8583b00",
      "gasUsed": "0x1c9c380",
      "timestamp": "0x66f4c3a8",
      "baseFeePerGas": "0x0"
    }
  }
}

Notification (logs):

{
  "jsonrpc": "2.0",
  "method": "eth_subscription",
  "params": {
    "subscription": "0x7f6e5d4c3b2a19080716253443526170",
    "result": {
      "address": "0x55d398326f99059fF775485246999027B3197955",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
      ],
      "data": "0x",
      "blockNumber": "0x7cb5e92",
      "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "transactionIndex": "0x0",
      "blockHash": "0x4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c",
      "logIndex": "0x0",
      "removed": false
    }
  }
}
jsonrpcstring
Always "2.0".
idinteger | string
Echoes the request id.
resultstring
Hex-encoded subscription id — pass to eth_unsubscribe and match against incoming params.subscription.

Errors

The server may send an application-level error frame immediately before closing the socket with one of the canonical close codes, or return a JSON-RPC error on the request itself (for subscribe-time validation). The channel shares the streaming close-code set; see the streaming overview.

CodeWhereWhen it happens
-32602JSON-RPC error on the subscribe replyA logs subscription was sent with an empty or all-wildcard filter.
-32005JSON-RPC error on the subscribe replyThe per-process logs subscription cap is reached.
-32601JSON-RPC error on the subscribe replyA subscription type is not enabled on this deployment (for example newPendingTransactions when the mempool source is not running).
4001 (unauthorized)WS closeMissing or invalid credentials on the upgrade / auth frame.
4003 (forbidden)WS closeSubscription type requires a higher tier, or the method is unknown. Frame carries current_tier, required_tier, method, and upgrade_url.
4029 (rate_limited)WS closePer-tier limit reached. Frame carries retry_after_sec and limit_rps.

Examples

JavaScript

WebSockets are not opened with fetch; use the browser/Node WebSocket API:

const ws = new WebSocket("wss://triport.io/ws/bsc", {
  headers: { Authorization: `Bearer ${process.env.TRIPORT_API_KEY}` },
});


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


ws.addEventListener("open", () => {
  ws.send(JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "eth_subscribe",
    params: ["newHeads"],
  }));
});


ws.addEventListener("message", (ev) => {
  const msg = JSON.parse(ev.data);
  if (msg.id === 1 && msg.result) {
    subs.set(msg.result, "newHeads"); // store the hex id
  } else if (msg.method === "eth_subscription") {
    console.log("block", msg.params.result.number, "from", msg.params.subscription);
  }
});

Notes

  • One id per subscription. Store the hex result from each acknowledgement; it is the only way to correlate notifications (params.subscription) and to eth_unsubscribe later.
  • logs needs a non-empty filter. This is a safeguard against a per-client firehose. Supply an address, a topics selector, or both.
  • No removed: true on reorg. Log notifications carry removed: false; the channel pushes the new canonical block's logs but does not retract orphaned ones.
  • Tier gating is per subscription type, not per connection — a free key can open newPendingTransactions but will be closed with 4003 if it tries newHeads.
  • Same-socket JSON-RPC. Non-subscribe methods and batch arrays are answered on the same socket by the /rpc/bsc backend, under the same scope and budget.
  • Reconnects: subscriptions end with their connection and events are not replayed; resubscribe and backfill over HTTPS after reconnecting. Ethereum exposes the same shape on /ws/eth, Polygon on /ws/poly. See the streaming overview for all channels.