---
title: "整块订阅（blockreq_newBlocks）"
description: "通过 WebSocket 收到每个新块及其全部交易，收到链头后无需再多取一次块"
url: "https://blockreq.com/docs/zh-cn/build/blockreq-new-blocks/"
---

# 整块订阅（blockreq_newBlocks）

`blockreq_newBlocks` 是 BlockReq 为 `eth_subscribe` 提供的订阅类型。链上每出一个块，你会收到一条标准的 `eth_subscription` 通知，`params.result` 是带完整交易对象的整个块，与 `eth_getBlockByHash(hash, true)` 返回的对象一致，字段原样来自节点。

它替代了常见的两步循环：先订阅 `newHeads`，再对收到的每个链头调用 `eth_getBlockByHash`。

这是 BlockReq 的扩展订阅，不是以太坊标准订阅。ethers 和 viem 内置的区块监听不会用它，请像下面的示例那样直接发送原始 `eth_subscribe`。

## 支持的链

仅在 BlockReq 自建节点的主网上提供。其他链和所有测试网都会拒绝该订阅。

| 链 | Chain ID | WSS 端点 |
| --- | --- | --- |
| Ethereum | `1` | `wss://ethereum-rpc.blockreq.com/v1/rpc/BRQ1P0000000000000000000000000000000000000000000000000000000` |
| Base | `8453` | `wss://base-rpc.blockreq.com/v1/rpc/BRQ1P0000000000000000000000000000000000000000000000000000000` |
| Arbitrum One | `42161` | `wss://arbitrum-one-rpc.blockreq.com/v1/rpc/BRQ1P0000000000000000000000000000000000000000000000000000000` |
| BNB Smart Chain | `56` | `wss://bsc-rpc.blockreq.com/v1/rpc/BRQ1P0000000000000000000000000000000000000000000000000000000` |
| Robinhood | `4663` | `wss://robinhood-mainnet-rpc.blockreq.com/v1/rpc/BRQ1P0000000000000000000000000000000000000000000000000000000` |
| Arc | `5042` | `wss://arc-rpc.blockreq.com/v1/rpc/BRQ1P0000000000000000000000000000000000000000000000000000000` |

该订阅需要带 API key 的端点，公开端点（`/v1/rpc/public`）会拒绝。

## 订阅

只发送一个参数 `"blockreq_newBlocks"`，大小写必须完全一致。其他任何写法都会返回 `invalid params` 错误。

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_subscribe",
  "params": ["blockreq_newBlocks"]
}
```

和其他订阅一样，响应里是订阅 ID：

```json
{ "jsonrpc": "2.0", "id": 1, "result": "0x9a1c3f0e5b7d2a48c6e1f3b5d7a9c2e4" }
```

之后每个新块以通知的形式到达。示例经过截短：数值仅作示意，`logsBloom` 被截断，交易列表只放了该块的一笔交易。

```json
{
  "jsonrpc": "2.0",
  "method": "eth_subscription",
  "params": {
    "subscription": "0x9a1c3f0e5b7d2a48c6e1f3b5d7a9c2e4",
    "result": {
      "number": "0x1312d00",
      "hash": "0x5e1c0b7c2c4f0a9d6b3e8f21a7d4c9b05e6f1a2b3c4d5e6f708192a3b4c5d6e7",
      "parentHash": "0x3b9f4a1d8c7e6052f1a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f809",
      "timestamp": "0x6745a9c3",
      "miner": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97",
      "gasLimit": "0x1c9c380",
      "gasUsed": "0xb71b00",
      "baseFeePerGas": "0x3b9aca00",
      "logsBloom": "0x00a1…0004",
      "transactionsRoot": "0x7a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9",
      "stateRoot": "0x1f2e3d4c5b6a79880716253443526170899a8b7c6d5e4f30211203f4e5d6c7b8",
      "receiptsRoot": "0x9c8b7a69584736251403f2e1d0c9b8a7968574635241302f1e0d1c2b3a495867",
      "transactions": [
        {
          "hash": "0xa1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90",
          "blockHash": "0x5e1c0b7c2c4f0a9d6b3e8f21a7d4c9b05e6f1a2b3c4d5e6f708192a3b4c5d6e7",
          "blockNumber": "0x1312d00",
          "transactionIndex": "0x0",
          "from": "0x00000000219ab540356cbb839cbe05303d7705fa",
          "to": "0xdac17f958d2ee523a2206206994597c13d831ec7",
          "type": "0x2",
          "nonce": "0x2a",
          "gas": "0x186a0",
          "maxFeePerGas": "0x77359400",
          "maxPriorityFeePerGas": "0x59682f00",
          "value": "0x0",
          "input": "0xa9059cbb…",
          "chainId": "0x1",
          "accessList": []
        }
      ],
      "uncles": []
    }
  }
}
```

实际出现哪些字段取决于链及其最近一次硬分叉：通知携带的是节点对 `eth_getBlockByHash(hash, true)` 的返回，包含链自己的扩展字段。

用 `eth_unsubscribe` 退订，传入订阅 ID：

```json
{ "jsonrpc": "2.0", "id": 2, "method": "eth_unsubscribe", "params": ["0x9a1c3f0e5b7d2a48c6e1f3b5d7a9c2e4"] }
```

## 示例

把 `BRQ1P0000000000000000000000000000000000000000000000000000000` 换成你的端点 key，下面从环境变量 `BLOCKREQ_API_KEY` 读取。

### wscat

```bash
npx wscat -c "wss://ethereum-rpc.blockreq.com/v1/rpc/$BLOCKREQ_API_KEY" \
  -x '{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["blockreq_newBlocks"]}' -w 60
```

### Node.js

需要 Node.js 22 或更新版本，无需安装任何包：

```js
const ws = new WebSocket(`wss://ethereum-rpc.blockreq.com/v1/rpc/${process.env.BLOCKREQ_API_KEY}`);

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

let lastNumber = null;
ws.addEventListener("message", (event) => {
  const message = JSON.parse(event.data);
  if (message.error) {
    console.error(message.error.code, message.error.message);
    ws.close();
    return;
  }
  if (message.method !== "eth_subscription") {
    console.log("subscription id:", message.result);
    return;
  }
  const block = message.params.result;
  const number = Number(block.number);
  if (lastNumber !== null && number > lastNumber + 1) {
    console.warn(`blocks ${lastNumber + 1}..${number - 1} were not pushed; fetch them with eth_getBlockByNumber`);
  }
  lastNumber = number;
  console.log(number, block.hash, `${block.transactions.length} transactions`);
});
```

### Python

需要 Python 3.9 或更新版本，并执行 `pip install websockets`：

```python
import asyncio
import json
import os

import websockets

URL = f"wss://ethereum-rpc.blockreq.com/v1/rpc/{os.environ['BLOCKREQ_API_KEY']}"

async def main() -> None:
    async with websockets.connect(URL, max_size=None) as ws:
        await ws.send(json.dumps({"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["blockreq_newBlocks"]}))
        last_number = None
        async for raw in ws:
            message = json.loads(raw)
            if "error" in message:
                print(message["error"]["code"], message["error"]["message"])
                return
            if message.get("method") != "eth_subscription":
                print("subscription id:", message["result"])
                continue
            block = message["params"]["result"]
            number = int(block["number"], 16)
            if last_number is not None and number > last_number + 1:
                print(f"blocks {last_number + 1}..{number - 1} were not pushed; fetch them with eth_getBlockByNumber")
            last_number = number
            print(number, block["hash"], f"{len(block['transactions'])} transactions")

asyncio.run(main())
```

`max_size=None` 解除 `websockets` 库默认的 1 MiB 单条消息上限，以太坊的一个块可能超过它。

## 错误

被拒绝的订阅不计 Request。

| 错误码 | 何时出现 | 怎么办 |
| --- | --- | --- |
| `-32601` | 该链不提供此订阅，或连接没有 API key（公开端点）。消息为 `subscription blockreq_newBlocks is not available on this network or endpoint`。 | 在[支持的链](#支持的链)上使用带 key 的端点。 |
| `-32000` | 这个网络的订阅容量暂时已满。消息以 `blockreq_newBlocks subscriptions are temporarily unavailable on this network` 开头，并建议稍后再试。 | 稍后再订阅一次。 |

## Request 计数

- `eth_subscribe` 本身计 **1** 个 Request，与其他请求相同。
- 推送给你的每条整块通知计 **2** 个 Request：它替代的那条 `newHeads` 通知，加上你原本要自己发出的取块调用。没有推送给你的块不计数。
- 被拒绝的订阅不计数。

通用规则见 [Request Counting](https://blockreq.com/docs/zh-cn/pricing/request-counting/index.md)。

## 重组与缺口

该订阅跟随 `newHeads`：

- 发生重组后，替换它的新块会在到达时照常推送，所以你可能收到两个 `number` 相同的块。请用 `number` 和 `parentHash` 判断哪些块在你的链上。
- 如果节点没能在时限内给出某个块的内容，这个块就不会推送，你会看到块号跳变。被跳过的块之后不会补发，重新连接后也不会回放。需要每个块都不漏时，请把每条通知的 `number` 与上一条比较，并用 `eth_getBlockByNumber` 补取缺失的块。

## 消息体积

整块比链头大得多。Ethereum 的一个块约 0.5–0.7 MB JSON，BNB Smart Chain 约 0.1 MB，其余链的块从几 KB 到几十 KB。消息不压缩。读得太慢的客户端会被断开，规则与其他订阅相同，所以请在消息到达时就读走，把耗时的处理放到套接字循环之外。

## 与 newHeads 的区别

| | `newHeads` | `blockreq_newBlocks` |
| --- | --- | --- |
| 以太坊标准订阅 | 是 | 否，BlockReq 扩展 |
| 通知内容 | 区块头 | 带完整交易对象的整个块 |
| 获取交易 | 每个链头再调用一次 `eth_getBlockByHash` | 已在通知里 |
| 每条通知计的 Request | 1 | 2 |
| 支持的链 | 所有支持 WSS 的链 | [上面列出的六条链](#支持的链) |
| 访问方式 | 公开端点或带 key 端点 | 仅带 key 端点 |
| 消息体积 | 区块头 | 整个块，最大约 0.7 MB |

只需要知道有新块时用 `newHeads`；要读取每个新块的交易时用 `blockreq_newBlocks`。
