# WebSocket サブスクリプション

> Source: https://docs.blockvectra.com/ja/guides/websocket-subscriptions/

BlockVectra は、標準的な JSON-RPC リクエストとともにリアルタイムの Ethereum イベント購読をストリーミングするためのセキュアな WebSocket 接続（`wss://`）を提供します。

## WebSocket、Webhook、またはポーリングの選択

アプリケーションが接続を維持できる場合は、リアルタイムの `newHeads` やフィルタリングされた `logs` に WebSocket を使用します。監視対象ウォレットのアクティビティを HTTPS エンドポイントで受信し、[生ボディの署名検証](https://docs.blockvectra.com/en/guides/webhook-push/#verify-signatures)、再試行、保持された一致イベントのリプレイを利用するには、[ブロックチェーン Webhook API](https://docs.blockvectra.com/en/guides/webhook-push/) を使用します。定期的な ERC-20 決済の監視や過去のログのバックフィルには、[HTTP ポーリング](https://docs.blockvectra.com/en/guides/stablecoin-payments/) を使用します。ステーブルコインガイドには [USDT / USDC Webhook 受信側](https://docs.blockvectra.com/en/guides/stablecoin-payments/#receive-payments-with-webhooks) の例も記載されています。開発者や AI エージェント向けのチェーン対応、受信側の要件、復旧のトレードオフに関するアーキテクチャの比較については、[Webhook、WebSocket、または RPC ポーリングの選択ガイド](https://docs.blockvectra.com/en/guides/webhook-vs-websocket/) を参照してください。

WebSocket のサポートは `GET /v1/chains` の `ws` と `subscriptions` で確認できます。Push のサポートは認証付きの `GET /v1/push/chains` リストで確認できます。WebSocket に対応していないチェーンでも、リストに含まれていればアドレス Webhook を利用できます。

WebSocket の切断時は再購読とバックフィルが必要であり、Push の制御イベントである `subscription.gap` や `chain.reorg` は発行されません。Webhook の場合、ギャップには範囲スキャンが必要であり、再編成（reorg）通知では、自動再配信される正規イベントを保持する前に、置き換えられたイベントをマークまたは破棄する必要があります。[Push のリプレイ](https://docs.blockvectra.com/en/guides/webhook-push/#delivery-retries-and-replay) は保持された一致イベントを再送するものであり、アドレスやチェーンが追加される前や購読がオフラインであった期間のデータを再送するものではありません。復旧を実装する際は、[課金ルール](https://docs.blockvectra.com/en/guides/billing-rules/) と [エラーリファレンス](https://docs.blockvectra.com/en/errors/) を確認してください。

## 利用可能なチェーン

ネットワーク上で WebSocket 購読がアクティブかどうかは、`GET /v1/chains` の `ws`（ブール値）および `subscriptions`（サポートされるタイプの配列）を読み取ることで確認できます。

以下の表は、WebSocket サポートが有効になっているネットワークを示しています：

| チェーン | WebSocket エンドポイント（パスに Key を含む） |
| --- | --- |
| Robinhood Chain | `wss://api.blockvectra.com/v1/robinhood_mainnet/{api_key}` |
| Robinhood Chain Testnet | `wss://api.blockvectra.com/v1/robinhood_testnet/{api_key}` |

## 接続と認証

クライアントはセキュアな TLS WebSocket 接続（`wss://`）を確立します。API key は次の 2 つの方法で指定できます：

* **パス指定のキー**：`wss://api.blockvectra.com/v1/{chain}/{api_key}`
* **ヘッダー指定のキー**：HTTP Upgrade ハンドシェイク時に `x-api-key: {api_key}` または `Authorization: Bearer {api_key}` ヘッダーを付与した `wss://api.blockvectra.com/v1/{chain}`。

パス指定のキーがある場合、パス指定のキーが使用され、両方の認証ヘッダーは無視されます。パス指定のキーがない場合、空でない `x-api-key` が `Authorization: Bearer` より優先されます。ブラウザの WebSocket API ではこれらのヘッダーを設定できないため、パス指定のキーを含む URL を使用してください。

### ハンドシェイクの入場チェック

ハンドシェイクは以下の場合に失敗することがあります：

* **認証**：API key が指定されていない場合は HTTP 401（[`missing_api_key`](https://docs.blockvectra.com/en/errors/#missing_api_key)）が返されます。API key が不明、無効、または失効している場合は HTTP 401（[`invalid_api_key`](https://docs.blockvectra.com/en/errors/#invalid_api_key)）が返されます。認証サービスが一時的に利用できない場合、応答は HTTP 503（[`auth_unavailable`](https://docs.blockvectra.com/en/errors/#auth_unavailable)）になります。
* **アカウント残高**：前払い残高がゼロまたはマイナスのアカウントには HTTP 402（[`balance_exhausted`](https://docs.blockvectra.com/en/errors/#balance_exhausted)）が返されます。課金ステータスを確認できない場合、応答は HTTP 503（[`billing_unavailable`](https://docs.blockvectra.com/en/errors/#billing_unavailable)）になります。
* **接続制限**：キーあたりの制限（20 接続）またはアカウントあたりの制限（50 接続）を超えると、HTTP 429（[`ws_connection_limit`](https://docs.blockvectra.com/en/errors/#ws_connection_limit)）が返されます。
* **チェーンの利用可能性**：不明または未提供のチェーンを要求すると、HTTP 404（[`unknown_chain`](https://docs.blockvectra.com/en/errors/#unknown_chain)）が返されます。
* **サーバー容量**：サーバーがビジーまたは過負荷の場合、ハンドシェイクは `Retry-After` ヘッダー付きで HTTP 503（[`overloaded`](https://docs.blockvectra.com/en/errors/#overloaded)）を返します。

接続が確立されると、クライアントは標準の JSON-RPC 2.0 リクエスト（`eth_blockNumber` や `eth_call` など）および UTF-8 テキストフレームとしてフォーマットされた購読制御メソッドを送信できます。

## 課金ルール

* 接続の確立、アイドル接続の維持、および ping/pong ハートビートは課金されません。
* `false` を返す unsubscribe を含め、成功した `eth_subscribe` および `eth_unsubscribe` の呼び出しは課金されます。失敗した呼び出しは課金されません。通常の JSON-RPC 呼び出しは [JSON-RPC 課金ルール](https://docs.blockvectra.com/en/guides/billing-rules/) に従います。
* `newHeads` 通知は、その接続がいくつの `newHeads` 購読を持っているかに関係なく、接続あたりのブロックハッシュごとに 1 回カウントされます。
* `logs` 通知は、一致するログがあるブロックハッシュおよびフェーズごとに、購読あたり 1 回カウントされます。一致のないブロックは課金されません。同じブロックおよびフェーズ内で複数のログが一致しても、課金額が増加することはありません。フィルターが重複していても、個別の購読は別々にカウントされます。再編成ログ（`removed: true`）は別個のユニットを構成します。同じ高さの置き換えブロックは異なるハッシュを持ち、異なるユニットとなります。
* 通知は、ソケット送信バッファに正常にフラッシュされた後にのみ課金されます。キューに入れられたままフラッシュされなかった通知や破棄された通知は課金されません。`eth_unsubscribe` の応答前にキューに入れられた通知は、フラッシュされた場合にカウントされます。WebSocket メッセージには HTTP 課金ヘッダーが付与されません。計測された CU についてはアカウントの使用状況を参照してください。

## 購読メソッド

API は標準的な Ethereum の pub/sub インターフェースである `eth_subscribe` および `eth_unsubscribe` を実装しています。

### `newHeads`

新しいブロックがチェーンヘッドに追加されるたびに、新しいブロックヘッダーオブジェクトを発行します。

* **購読リクエスト**：
  ```json
  {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
  ```
* **購読レスポンス**：不透明な 16 進数の購読識別子を返します：
  ```json
  {"jsonrpc":"2.0","id":1,"result":"0x1"}
  ```
* **プッシュ通知フレーム**：
  ```json
  {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":{"number":"0x1b4","hash":"0x..."}}}
  ```

### `logs`

指定されたフィルター条件に一致するログイベントを発行します。

* **フィルター要件**：すべての `logs` 購読フィルターには、`address`（コントラクトアドレスまたはアドレスの配列）または `topic0`（最初のトピック位置、null 不可）を**必ず**指定する必要があります。どちらも指定されていないフィルター（`{}` や `{"topics":[null,"0x..."]}` など）は、エラーコード `-32602`（[`logs_filter_required`](https://docs.blockvectra.com/en/errors/#logs_filter_required)）で拒否されます。

* **フィルター制限**：最大 100 アドレス。最大 4 つのトピック位置（位置ごとに最大 16 個の候補ハッシュ）。

* **フィルター容量**：アクティブなログフィルターが上限容量に達した場合、購読はエラーコード `-32022`（[`ws_filter_capacity`](https://docs.blockvectra.com/en/errors/#ws_filter_capacity)）を返します。

* **チェーンの再編成**：チェーンの再編成によりブロックが削除された場合、削除されたログのログ通知には `"removed": true` が付与されます。

* **購読リクエスト**：
  ```json
  {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}
  ```

### `eth_unsubscribe`

購読識別子を使用してアクティブな購読を終了します。

* **購読解除リクエスト**：
  ```json
  {"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
  ```
* **購読解除レスポンス**：
  ```json
  {"jsonrpc":"2.0","id":3,"result":true}
  ```

## 実行可能なサンプル

**viem v2 (TypeScript)**

[viem](https://viem.sh) v2 を使用し、`createPublicClient` と `webSocket` トランスポートを介して接続します。`{chain}` を対象チェーンの識別子に、`{api_key}` をご自身の API key に置き換えてください：

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

const apiKey = process.env.BLOCKVECTRA_API_KEY || '{api_key}';
const url = `wss://api.blockvectra.com/v1/robinhood_mainnet/${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)**

`websocat` や `wscat` などのコマンドラインツールを使用して接続し、生の JSON-RPC フレームを送信します：

```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}"
```

対話型セッションに購読コマンドを送信します：

```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"]}
```


## クローズコードとクライアントのアクション

サーバーが WebSocket セッションを終了する場合、特定のクローズコードと短い理由を含む Close フレームを送信します。以下の表は、サーバーから発行されるクローズコードと推奨されるアクションの一覧です：

|                  クローズコード | 理由文字列                            | 説明                                                                                        | 再試行可能 | クライアントのアクション                                                                                                                            |
| -----------------------: | -------------------------------- | ----------------------------------------------------------------------------------------- | :---: | --------------------------------------------------------------------------------------------------------------------------------------- |
| [1001](https://docs.blockvectra.com/en/errors/#1001) | `idle`                           | 3600 秒（1 時間）購読やメッセージがない非アクティブな接続                                                          |   はい  | 必要に応じて再接続します。                                                                                                                           |
| [1003](https://docs.blockvectra.com/en/errors/#1003) | `binary frames are not accepted` | バイナリ WebSocket フレームを受信。UTF-8 テキストフレームのみ対応                                                 |  いいえ  | 自動再接続は行わないでください。テキストフレームを送信するようにクライアントを更新してください。                                                                                        |
| [1009](https://docs.blockvectra.com/en/errors/#1009) | `message too large`              | 受信ペイロードが 1 MiB を超過                                                                        |  いいえ  | 自動再接続は行わないでください。大きなリクエストを分割するか、ペイロードサイズを縮小してください。                                                                                       |
| [1012](https://docs.blockvectra.com/en/errors/#1012) | `service restart`                | サーバーの再起動、またはセッションが最大有効期間（24 時間）に到達                                                        |   はい  | ランダム化されたジッターバックオフを使用して再接続し、購読を再確立して、取り逃したデータをバックフィルします。                                                                                 |
| [1013](https://docs.blockvectra.com/en/errors/#1013) | `chain unavailable`              | チェーンが利用不可                                                                                 |   はい  | フルジッター指数バックオフを使用して再接続し、購読を再確立して、取り逃したデータをバックフィルします。                                                                                     |
| [1013](https://docs.blockvectra.com/en/errors/#1013) | `overloaded`                     | サーバーの一時的な過負荷                                                                              |   はい  | フルジッター指数バックオフを使用して再接続し、購読を再確立して、取り逃したデータをバックフィルします。                                                                                     |
| [4402](https://docs.blockvectra.com/en/errors/#4402) | `insufficient balance`           | アカウント残高不足                                                                                 |  いいえ  | 自動再接続は行わないでください。[残高をチャージしてから再接続してください](https://docs.blockvectra.com/en/guides/billing-rules/)。                                                                      |
| [4404](https://docs.blockvectra.com/en/errors/#4404) | `invalid api key`                | API key が不明、無効、または失効                                                                      |  いいえ  | 自動再接続は行わないでください。再接続する前に、コンソールで API key を確認またはローテーションしてください。                                                                             |
| [4408](https://docs.blockvectra.com/en/errors/#4408) | `slow consumer`                  | サーバーはプッシュキューが 512 KiB を超えたセッションをクローズし、保留中の通知を破棄。クライアントはクローズフレームを受信しない場合あり（ブラウザは 1006 を報告） |   はい  | 予期せぬ切断（クローズフレームを受信できず、ブラウザが 1006 を報告）を 4408 と同様に扱います：バックオフを伴って再接続し、購読を再確立して、破棄されたデータを `eth_getLogs` でバックフィルします。購読数を減らすか、読み取り速度を上げてください。 |
| [4429](https://docs.blockvectra.com/en/errors/#4429) | `push rate exceeded`             | 通知レートが 1,000 プッシュ/秒を超過                                                                    |   はい  | 購読を減らすかフィルターを絞り込みます。バックオフを伴って再接続し、再購読して、バックフィルします。                                                                                      |
| [4503](https://docs.blockvectra.com/en/errors/#4503) | `billing unavailable`            | 課金サービスが一時的に利用不可                                                                           |   はい  | 一時的な状態です。フルジッター指数バックオフを使用して再接続してください。                                                                                                   |

## 再接続と指数バックオフ

接続が切断された際に同期的な再接続スパイクを防ぐため、クライアントはフルジッターを伴う指数バックオフを実装する必要があります：

* **バックオフ計算式**：n 回目の再接続試行（n = 0, 1, 2, ...）の前に、一様ランダムに選択された待機時間だけ待機します：
  ```
  delay = random(0, min(20s, 0.5s * 2^n))
  ```
* **カウンターのリセット**：中断のない安定した接続を少なくとも `60 秒` 間維持した後にのみ、再試行カウンター n を 0 にリセットします。
* **クローズコード 1012**：同期的な再接続スパイクを回避するため、初回の再接続試行の前にランダム化された初期遅延を導入します。
* **再試行不可のコード**：[4402](https://docs.blockvectra.com/en/errors/#4402)、[4404](https://docs.blockvectra.com/en/errors/#4404)、[1003](https://docs.blockvectra.com/en/errors/#1003)、または [1009](https://docs.blockvectra.com/en/errors/#1009) の場合は自動再接続を行わないでください。

### 再接続後の取り逃したデータのバックフィル

WebSocket 購読は接続をまたいで持続しません。切断中に発行された通知がサーバー側に保持されることはありません。再接続後、クライアントは以下のキャッチアップ戦略を実行する必要があります：

1. **`eth_getLogs` によるログのバックフィル**：
   * 正常に処理された最大のブロック番号（`last_processed_block`）を永続化しておきます。
   * 再接続時に直ちに `eth_subscribe("logs", ...)` を呼び出して、リアルタイムイベントをキャプチャします。
   * `fromBlock: last_processed_block + 1` および `toBlock: "latest"`（またはリアルタイムストリームから受信した最初のブロック）を指定して `eth_getLogs` で取り逃したブロックを照会します。
   * 切断ギャップがネットワークの `max_logs_block_range`（`GET /v1/chains` より取得）を超える場合は、その制限を超えないチャンクにクエリを分割します。
   * 一意のタプル `(blockHash, transactionHash, logIndex)` を使用して、クエリ境界をまたぐログエントリの重複を排除します。
2. **`eth_getBlockByNumber` によるブロックヘッダーのバックフィル**：
   * 切断前に受信した最新のブロック番号とハッシュを記録しておきます。
   * `newHeads` を再購読します。
   * `eth_getBlockByNumber("latest", false)` を照会し、欠落している中間ブロックを順次取得します。親ハッシュ（`parentHash`）のチェーン連続性を検証して再編成を検出します。

## 制限事項

| 制限項目                            | 値                                            | 超過時の結果                                                              |
| ------------------------------- | -------------------------------------------- | ------------------------------------------------------------------- |
| WebSocket 接続あたりの購読数             | 100                                          | `-32022` [`subscription_limit`](https://docs.blockvectra.com/en/errors/#subscription_limit)     |
| WebSocket 接続あたりの `newHeads` 購読数 | 4                                            | `-32022` [`subscription_limit`](https://docs.blockvectra.com/en/errors/#subscription_limit)     |
| `logs` 購読フィルター要件                | `address` または `topic0`（`topics` の先頭位置）の指定が必須 | `-32602` [`logs_filter_required`](https://docs.blockvectra.com/en/errors/#logs_filter_required) |

## 次のステップ

* [データセットディレクトリを閲覧](https://blockvectra.com/en/data/) して、BlockVectra がインデックス化しているすべてのデータセットを確認できます。
* [無料プランと料金](https://blockvectra.com/en/pricing/#free) で、アカウントに含まれる内容を確認できます。
* [コンソールにログイン](https://console.blockvectra.com/login/?next=%2Fkeys%2F) して、API key を作成してください。
