# WebSocket Subscriptions

> Original page: https://docs.blockvectra.com/en/guides/websocket-subscriptions/

BlockVectra provides secure WebSocket connections (`wss://`) for streaming real-time Ethereum event subscriptions alongside standard JSON-RPC requests.

## Available chains

WebSocket support is served dynamically per chain. You can check whether WebSocket subscriptions are active on a network by reading `ws` (boolean) and `subscriptions` (array of supported types) in the public `GET /v1/chains` endpoint.

The table below reflects networks where WebSocket support is enabled:

| Chain | {chain} | WebSocket Endpoint (Path Key) | Supported Subscriptions |
| --- | --- | --- | --- |
| Robinhood Chain | `robinhood_mainnet` | `wss://api.blockvectra.com/v1/robinhood_mainnet/{api_key}` | `newHeads, logs` |

## Connection and authentication

Clients establish a secure TLS WebSocket connection (`wss://`). The API key can be supplied in two ways:

* **Path key**: `wss://api.blockvectra.com/v1/{chain}/{api_key}`
* **Header key**: `wss://api.blockvectra.com/v1/{chain}` with the `x-api-key: {api_key}` or `Authorization: Bearer {api_key}` header during the HTTP Upgrade handshake.

### Handshake admission checks

During the HTTP Upgrade handshake, the server evaluates admission gates before establishing the WebSocket session:

* **Authentication**: An absent API key returns HTTP 401 ([`missing_api_key`](https://docs.blockvectra.com/en/errors/#missing_api_key)); an unknown, disabled, or revoked API key returns HTTP 401 ([`invalid_api_key`](https://docs.blockvectra.com/en/errors/#invalid_api_key)); if key table synchronization is temporarily unavailable, the response is HTTP 503 ([`auth_unavailable`](https://docs.blockvectra.com/en/errors/#auth_unavailable)).
* **Account balance**: An account with zero or negative prepaid balance returns HTTP 402 ([`balance_exhausted`](https://docs.blockvectra.com/en/errors/#balance_exhausted)); if billing state cannot be confirmed, the response is HTTP 503 ([`billing_unavailable`](https://docs.blockvectra.com/en/errors/#billing_unavailable)).
* **Connection limits**: Exceeding the per-key limit (20 connections per server instance) or per-account limit (50 connections per server instance) returns HTTP 429 ([`ws_connection_limit`](https://docs.blockvectra.com/en/errors/#ws_connection_limit)).
* **Chain availability**: Requesting an unknown or unserved chain returns HTTP 404 ([`unknown_chain`](https://docs.blockvectra.com/en/errors/#unknown_chain)).
* **Instance capacity**: When a server instance is at capacity or queued notifications exceed threshold, the handshake returns HTTP 503 ([`overloaded`](https://docs.blockvectra.com/en/errors/#overloaded)) with a `Retry-After` header.

Once connected, clients can send standard JSON-RPC 2.0 requests (such as `eth_blockNumber` or `eth_call`) and subscription control methods formatted as UTF-8 text frames.

## Subscription methods

The API implements the standard Ethereum pub/sub interface: `eth_subscribe` and `eth_unsubscribe`.

### `newHeads`

Emits a new block header object whenever a new block is appended to the chain head.

* **Subscribe request**:
  ```json
  {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
  ```
* **Subscribe response**: Returns an opaque hexadecimal subscription identifier:
  ```json
  {"jsonrpc":"2.0","id":1,"result":"0x1"}
  ```
* **Push notification frame**:
  ```json
  {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":{"number":"0x1b4","hash":"0x..."}}}
  ```

### `logs`

Emits log events matching specified filter criteria.

* **Filter requirement**: Every `logs` subscription filter **must** specify an `address` (a contract address or array of addresses) or a `topic0` (the first topic position, non-null). A filter specifying neither (such as `{}` or `{"topics":[null,"0x..."]}`) is rejected with error code `-32602` ([`logs_filter_required`](https://docs.blockvectra.com/en/errors/#logs_filter_required)).

* **Filter limits**: At most 100 addresses; at most 4 topic positions with at most 16 candidate hashes per position.

* **Instance capacity**: If active log filters on the server instance reach capacity, the subscription returns error code `-32022` ([`ws_filter_capacity`](https://docs.blockvectra.com/en/errors/#ws_filter_capacity)).

* **Chain reorganizations**: If a block is removed due to a chain reorg, log notifications for removed logs carry `"removed": true`.

* **Subscribe request**:
  ```json
  {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}
  ```

### `eth_unsubscribe`

Terminates an active subscription using its subscription identifier.

* **Unsubscribe request**:
  ```json
  {"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
  ```
* **Unsubscribe response**:
  ```json
  {"jsonrpc":"2.0","id":3,"result":true}
  ```

## Runnable examples

**viem v2 (TypeScript)**

Connect using [viem](https://viem.sh) v2 via `createPublicClient` and the `webSocket` transport. Replace `{chain}` with the target chain identifier and `{api_key}` with your API key:

```ts
import { createPublicClient, webSocket } from 'viem';

const apiKey = process.env.BLOCKVECTRA_API_KEY || '{api_key}';
const url = `wss://api.blockvectra.com/v1/{chain}/${apiKey}`;

const client = createPublicClient({
  transport: webSocket(url),
});

// 1. Subscribe to new block headers (newHeads)
const unwatchBlocks = client.watchBlocks({
  onBlock: (block) => {
    console.log('New block header received:', block.number, block.hash);
  },
  onError: (error) => {
    console.error('watchBlocks error:', error);
  },
});

// 2. Subscribe to contract event logs (filter requires address or topic0)
const unwatchEvents = client.watchEvent({
  address: '0x1234567890123456789012345678901234567890',
  onLogs: (logs) => {
    console.log('Matching logs received:', logs);
  },
  onError: (error) => {
    console.error('watchEvent error:', error);
  },
});
```


  **Command line (websocat / wscat)**

Connect using command-line tools like `websocat` or `wscat` and send raw JSON-RPC frames:

```bash
# Connect with path-based API key using websocat
websocat "wss://api.blockvectra.com/v1/{chain}/{api_key}"

# Alternatively, pass the key via request header
websocat -H="x-api-key: {api_key}" "wss://api.blockvectra.com/v1/{chain}"

# Or connect using wscat
wscat -c "wss://api.blockvectra.com/v1/{chain}/{api_key}"
```

Send subscription commands into the interactive session:

```json
{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
{"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}
{"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
```


## Close codes and client actions

When the server terminates a WebSocket session, it sends a Close frame with a specific close code and short reason. The table below lists the close codes emitted by the server and recommended actions:

|               Close code | Reason string                    | Description                                                                                                                                                                                                                                                                                                                                                                                               | Retryable | Client action                                                                                                                                                                                                                |
| -----------------------: | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [1001](https://docs.blockvectra.com/en/errors/#1001) | `idle`                           | Inactive connection without subscriptions or messages for 3600 seconds (1 hour)                                                                                                                                                                                                                                                                                                                           |    Yes    | Reconnect as needed.                                                                                                                                                                                                         |
| [1003](https://docs.blockvectra.com/en/errors/#1003) | `binary frames are not accepted` | Binary WebSocket frame received; UTF-8 text frames only                                                                                                                                                                                                                                                                                                                                                   |     No    | Do not reconnect automatically. Update client to send text frames.                                                                                                                                                           |
| [1009](https://docs.blockvectra.com/en/errors/#1009) | `message too large`              | Inbound payload exceeded 1 MiB (1,048,576 bytes)                                                                                                                                                                                                                                                                                                                                                          |     No    | Do not reconnect automatically. Split large requests or reduce payload size.                                                                                                                                                 |
| [1012](https://docs.blockvectra.com/en/errors/#1012) | `service restart`                | Server instance restarting, or session reached maximum lifetime (24 hours ± 10% jitter)                                                                                                                                                                                                                                                                                                                   |    Yes    | Reconnect using randomized jitter backoff, re-establish subscriptions, and backfill missed data.                                                                                                                             |
| [1013](https://docs.blockvectra.com/en/errors/#1013) | `chain unavailable`              | Chain WebSocket listener unavailable, or upstream subscription source disconnected                                                                                                                                                                                                                                                                                                                        |    Yes    | Reconnect using full-jitter exponential backoff, re-establish subscriptions, and backfill missed data.                                                                                                                       |
| [1013](https://docs.blockvectra.com/en/errors/#1013) | `overloaded`                     | Server instance queued notifications buffer (256 MiB) reached capacity                                                                                                                                                                                                                                                                                                                                    |    Yes    | Reconnect using full-jitter exponential backoff, re-establish subscriptions, and backfill missed data.                                                                                                                       |
| [4402](https://docs.blockvectra.com/en/errors/#4402) | `insufficient balance`           | Account balance exhausted                                                                                                                                                                                                                                                                                                                                                                                 |     No    | Do not reconnect automatically. [Top up your balance, then reconnect](https://docs.blockvectra.com/en/guides/billing-rules/).                                                                                                                            |
| [4404](https://docs.blockvectra.com/en/errors/#4404) | `invalid api key`                | API key is unknown, disabled, or revoked                                                                                                                                                                                                                                                                                                                                                                  |     No    | Do not reconnect automatically. Verify or rotate API key in the console before reconnecting.                                                                                                                                 |
| [4408](https://docs.blockvectra.com/en/errors/#4408) | `slow consumer`                  | The server closes a session whose push queue passes 512 KiB (524,288 bytes) and drops pending notifications. The 4408 close frame reaches the client only if it is still reading (it is sent after the pushes already buffered, with a 2 s limit); a client that stopped reading or reads far slower than the push rate will instead see the connection drop without a close frame (browsers report 1006) |    Yes    | Treat unexpected disconnects (no close frame received, browser reports 1006) like 4408: reconnect with backoff, re-establish subscriptions, and backfill dropped data with `eth_getLogs`; subscribe to less, or read faster. |
| [4429](https://docs.blockvectra.com/en/errors/#4429) | `push rate exceeded`             | Notification rate exceeded 1,000 pushes/second (burst 10,000)                                                                                                                                                                                                                                                                                                                                             |    Yes    | Reduce subscriptions or narrow filters; reconnect with backoff, re-subscribe, and backfill.                                                                                                                                  |
| [4503](https://docs.blockvectra.com/en/errors/#4503) | `billing unavailable`            | Billing state or key table synchronization temporarily stale (> 60 seconds)                                                                                                                                                                                                                                                                                                                               |    Yes    | Transient state; reconnect using full-jitter exponential backoff.                                                                                                                                                            |

## Reconnection and exponential backoff

To prevent synchronized reconnection storms when instances restart or connections drop, clients must implement exponential backoff with full jitter as defined in API Specification §15.4:

* **Backoff formula**: Before the n-th reconnection attempt (n = 0, 1, 2, ...), wait for a duration chosen uniformly at random:
  ```
  delay = random(0, min(20s, 0.5s * 2^n))
  ```
* **Parameters (API Specification §15.4)**:
  * **Base initial backoff**: `0.5 s` (`0.5 s * 2^0 = 0.5 s`)
  * **Exponential multiplier**: `2^n` (`0.5 s`, `1.0 s`, `2.0 s`, `4.0 s`, ...)
  * **Maximum backoff cap**: `20 s`
  * **Full jitter**: Uniform pseudo-random value between `0` and `min(20 s, 0.5 s * 2^n)`
  * **Reset counter**: Reset retry counter n to 0 only after maintaining an uninterrupted, stable connection for at least `60 seconds`
  * **Close code 1012**: Introduce randomized initial delay before first reconnect attempt to avoid synchronized reconnection spikes
  * **Non-retryable codes**: Do not reconnect automatically on [4402](https://docs.blockvectra.com/en/errors/#4402), [4404](https://docs.blockvectra.com/en/errors/#4404), [1003](https://docs.blockvectra.com/en/errors/#1003), or [1009](https://docs.blockvectra.com/en/errors/#1009).
  * **Unexpected disconnects**: Treat unexpected disconnects (no close frame received, browser reports 1006) like [4408](https://docs.blockvectra.com/en/errors/#4408): reconnect with backoff, and reduce subscriptions or read faster.

### Backfilling missed data after reconnection

WebSocket subscriptions do not persist across connections; notifications emitted during a disconnection are not retained on the server. Following reconnection, clients should execute a catch-up strategy as outlined in API Specification §15.5:

1. **Backfill logs with `eth_getLogs`**:
   * Persist the highest block number successfully processed (`last_processed_block`).
   * Immediately call `eth_subscribe("logs", ...)` on reconnect to capture live events.
   * Query missed blocks via `eth_getLogs` with `fromBlock: last_processed_block + 1` and `toBlock: "latest"` (or the first block received from the live stream).
   * If the disconnection gap exceeds 1,000 blocks (`max_logs_block_range: 1000`), partition queries into chunks of at most 1,000 blocks each.
   * Deduplicate log entries across the query boundary using the unique tuple `(blockHash, transactionHash, logIndex)`.
2. **Backfill block headers with `eth_getBlockByNumber`**:
   * Record the latest block number and hash received before disconnect.
   * Re-subscribe to `newHeads`.
   * Query `eth_getBlockByNumber("latest", false)` and fetch missing intermediate blocks sequentially. Verify `parentHash` chain continuity to detect reorgs.

## Limits

| Limit                                                                    | Value                                                                | Result when exceeded                                                                                                                                                                                                                                                                                                       |
| ------------------------------------------------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Concurrent WebSocket connections per API key                             | 20 per server instance                                               | Upgrade handshake returns 429 ([`ws_connection_limit`](https://docs.blockvectra.com/en/errors/#ws_connection_limit))                                                                                                                                                                                                                                   |
| Concurrent WebSocket connections per account                             | 50 per server instance                                               | Upgrade handshake returns 429 ([`ws_connection_limit`](https://docs.blockvectra.com/en/errors/#ws_connection_limit))                                                                                                                                                                                                                                   |
| Subscriptions per WebSocket connection                                   | 100                                                                  | `-32022` [`subscription_limit`](https://docs.blockvectra.com/en/errors/#subscription_limit)                                                                                                                                                                                                                                                            |
| `newHeads` subscriptions per WebSocket connection                        | 4                                                                    | `-32022` [`subscription_limit`](https://docs.blockvectra.com/en/errors/#subscription_limit)                                                                                                                                                                                                                                                            |
| `logs` subscription filter requirements                                  | Must specify an `address` or a `topic0` (first position in `topics`) | `-32602` [`logs_filter_required`](https://docs.blockvectra.com/en/errors/#logs_filter_required)                                                                                                                                                                                                                                                        |
| Inbound WebSocket message size                                           | 1 MiB (1048576 bytes)                                                | Connection closed with code [1009](https://docs.blockvectra.com/en/errors/#1009)                                                                                                                                                                                                                                                                       |
| Push notification queue buffer per connection                            | 512 KiB (524288 bytes)                                               | Connection closed with code [4408](https://docs.blockvectra.com/en/errors/#4408) (`slow consumer`)                                                                                                                                                                                                                                                     |
| Push notifications queued per server instance (all connections together) | 256 MiB (268435456 bytes)                                            | The connection whose next notification does not fit is closed with code [1013](https://docs.blockvectra.com/en/errors/#1013) (`overloaded`); past half of it, a new WebSocket handshake gets 503 ([`overloaded`](https://docs.blockvectra.com/en/errors/#overloaded), with `Retry-After`) and `eth_subscribe` gets `-32026` [`ws_push_overloaded`](https://docs.blockvectra.com/en/errors/#ws_push_overloaded) |
| Answers not yet read per WebSocket connection                            | 16 MiB                                                               | The server reads no further message from the connection until the client has read enough of them                                                                                                                                                                                                                           |
| WebSocket client not reading                                             | 30 s blocked on one write                                            | Connection dropped without a close frame                                                                                                                                                                                                                                                                                   |
| Push notification rate per connection                                    | 1000 pushes/second (burst 10000)                                     | Connection closed with code [4429](https://docs.blockvectra.com/en/errors/#4429)                                                                                                                                                                                                                                                                       |
| Idle WebSocket connection                                                | 3600 s (1 hour) with no subscriptions and no messages                | Connection closed with code [1001](https://docs.blockvectra.com/en/errors/#1001)                                                                                                                                                                                                                                                                       |
| WebSocket connection maximum age                                         | 24 hours (with ±10% jitter)                                          | Connection closed with code [1012](https://docs.blockvectra.com/en/errors/#1012)                                                                                                                                                                                                                                                                       |

## Next steps

* [Browse the datasets directory](https://blockvectra.com/en/data/) to see every dataset BlockVectra indexes.
* [See the free plan and pricing](https://blockvectra.com/en/pricing/#free) to check what your account includes.
* [Log in to the console](https://console.blockvectra.com/en/login/?next=%2Fen%2Fkeys%2F) to create an API key.
