# eth_getLogs 区块跨度上限与分段查询

> 原文地址: https://docs.blockvectra.com/zh/guides/getlogs-block-range/

## eth\_getLogs 的区块跨度上限 [#eth_getlogs-的区块跨度上限]

在调用 JSON-RPC 方法 `eth_getLogs` 时，单次请求的区块跨度按照 `toBlock − fromBlock + 1` 计算，不能超过目标链通过 `GET /v1/chains` 公布的 `max_logs_block_range` 上限。

该限制因链而异——各链的参数通过公开接口 `GET /v1/chains` 发布（链列表见[支持的链](/zh/chains/)）。该接口免鉴权、不计费、不限流。在编写客户端代码时，请在运行时通过该接口动态读取目标链的 `max_logs_block_range`，不要将上限数值写死在代码中。

根据规格，过滤参数 `fromBlock` 与 `toBlock` 的取值规则如下：

* **缺省与 null**：`fromBlock` 与 `toBlock` 缺省或为 `null` 时，按 `latest` 处理。
* **标签解析**：`latest` 等标签按服务最近一次轮询的高度解析。
* **不检查场景**：带有 `blockHash` 的过滤器、无法解析的值，以及 `toBlock < fromBlock` 的情形，不做跨度检查，直接交给节点处理。

## 超出跨度上限的表现 [#超出跨度上限的表现]

当单次请求的区块跨度 `toBlock − fromBlock + 1` 超过该链的 `max_logs_block_range` 时，请求会被服务直接拒绝，返回 HTTP 200 与 JSON-RPC 错误：

* **错误码**：`-32602`
* **错误信息**：`eth_getLogs block range too large: max <N> blocks`
* **计费状态**：该错误属于服务自身拒绝，**不计费**（`billed: false`）。

### 规格示例请求 [#规格示例请求]

以下为规格中的超限请求示例（`reqLogsTooWide`）：

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_getLogs",
  "params": [
    {
      "fromBlock": "0x45a2409",
      "toBlock": "0x45a27f1"
    }
  ]
}
```

### 规格示例返回 [#规格示例返回]

超出上限时返回的错误响应示例（`errLogsRangeTooLarge`）：

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "eth_getLogs block range too large: max <N> blocks"
  }
}
```

其中 `<N>` 为该链的 `max_logs_block_range`（通过 `GET /v1/chains` 发布）。

在包含多个调用的批量请求中，如果其中某个 `eth_getLogs` 跨度过大，该调用同样会在对应位置返回上述 `-32602` 错误，且该调用不计费。

## 分段查询实现 [#分段查询实现]

当需要检索跨越较长区间的日志时，应先读取目标链的 `max_logs_block_range`，再将目标区间按照 `[from, from + max - 1]` 划分为若干段闭区间，按顺序逐段请求并合并结果。

以下示例以 `robinhood_mainnet` 为例演示分段查询流程：

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY="rgw_your_api_key"

    # 1. 从公开接口读取目标链的 max_logs_block_range（免鉴权、不计费）
    curl -s "https://dev-api.blockvectra.network/v1/chains"

    # 2. 发起单次合规请求：区块跨度（toBlock - fromBlock + 1）不超过该链的 max_logs_block_range
    curl -s "https://dev-api.blockvectra.network/v1/robinhood_mainnet" \
      -H 'Content-Type: application/json' \
      -H "x-api-key: $BLOCKVECTRA_API_KEY" \
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "eth_getLogs",
        "params": [{
          "address": "0x1111111111111111111111111111111111111111",
          "fromBlock": "0x45a2409",
          "toBlock": "0x45a246c"
        }]
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const CHAIN = "robinhood_mainnet";
    const RPC_ENDPOINT = "https://dev-api.blockvectra.network/v1/robinhood_mainnet";

    // 1. 从公开端点 GET /v1/chains 读取该链允许的最大区块跨度（免鉴权、不计费）
    const chainsUrl = new URL("/v1/chains", RPC_ENDPOINT);
    const chainsRes = await fetch(chainsUrl);
    const { chains } = (await chainsRes.json()) as {
      chains: Array<{ chain: string; max_logs_block_range: number }>;
    };

    const targetChain = chains.find((c) => c.chain === CHAIN);
    if (!targetChain) {
      throw new Error(`Chain ${CHAIN} not found`);
    }

    const maxRange = targetChain.max_logs_block_range;

    // 2. 按 [from, from + max - 1] 切段顺序查询并合并结果（闭区间包含两端）
    const fromBlock = 0x45a2409;
    const toBlock = 0x45a2900;

    const allLogs: unknown[] = [];
    let cur = fromBlock;

    while (cur <= toBlock) {
      const chunkEnd = Math.min(cur + maxRange - 1, toBlock);

      const res = await fetch(RPC_ENDPOINT, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
        },
        body: JSON.stringify({
          jsonrpc: "2.0",
          id: 1,
          method: "eth_getLogs",
          params: [
            {
              address: "0x1111111111111111111111111111111111111111",
              fromBlock: "0x" + cur.toString(16),
              toBlock: "0x" + chunkEnd.toString(16),
            },
          ],
        }),
      });

      const body = (await res.json()) as {
        result?: unknown[];
        error?: { code: number; message: string };
      };

      if (body.error) {
        throw new Error(`eth_getLogs error ${body.error.code}: ${body.error.message}`);
      }

      if (body.result) {
        allLogs.push(...body.result);
      }

      cur = chunkEnd + 1;
    }

    console.log(`Fetched ${allLogs.length} logs across blocks`);

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    from urllib.parse import urljoin
    import requests

    CHAIN = "robinhood_mainnet"
    RPC_ENDPOINT = "https://dev-api.blockvectra.network/v1/robinhood_mainnet"

    # 1. 从公开端点 GET /v1/chains 读取该链允许的最大区块跨度（免鉴权、不计费）
    chains_url = urljoin(RPC_ENDPOINT, "/v1/chains")
    chains_res = requests.get(chains_url)
    chains_res.raise_for_status()

    chains = chains_res.json().get("chains", [])
    target_chain = next((c for c in chains if c["chain"] == CHAIN), None)
    if not target_chain:
        raise RuntimeError(f"Chain {CHAIN} not found")

    max_range = target_chain["max_logs_block_range"]

    # 2. 按 [from, from + max - 1] 切段顺序查询并合并结果（闭区间包含两端）
    from_block = 0x45a2409
    to_block = 0x45a2900

    all_logs: list = []
    cur = from_block

    while cur <= to_block:
        chunk_end = min(cur + max_range - 1, to_block)

        res = requests.post(
            RPC_ENDPOINT,
            headers={
                "Content-Type": "application/json",
                "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
            },
            json={
                "jsonrpc": "2.0",
                "id": 1,
                "method": "eth_getLogs",
                "params": [
                    {
                        "address": "0x1111111111111111111111111111111111111111",
                        "fromBlock": hex(cur),
                        "toBlock": hex(chunk_end),
                    }
                ],
            },
        )
        res.raise_for_status()
        body = res.json()

        if "error" in body:
            err = body["error"]
            raise RuntimeError(f"eth_getLogs error {err.get('code')}: {err.get('message')}")

        all_logs.extend(body.get("result", []))
        cur = chunk_end + 1

    print(f"Fetched {len(all_logs)} logs across blocks")
    ```
  </Tab>
</Tabs>

## 批量请求注意事项 [#批量请求注意事项]

若考虑将切分后的多个分段调用打包进单个 JSON-RPC 批量请求，需要遵循规格中的批量与突发上限规则：

* **批量大小限制**：批量请求支持 1～100 个调用；超过 100 个调用会被直接拒绝，返回 HTTP 200 与错误码 `-32600 batch too large: max 100 calls`（不计费）。
* **单请求突发上限**：请求到达服务时，会先按请求内所有调用的满权重之和进行预扣。如果单个请求内调用的满权重之和超过突发上限，服务会立即返回 HTTP 429 与错误码 `-32022 request cost <N> CU exceeds burst capacity <M> CU`（不计费）。该错误等待多久都不会成功，必须将请求拆分成更小的批次或逐个调用。
* **令牌桶余量不足**：若单请求满权重之和未超过突发上限，但当前令牌桶内可用余量不足，服务返回 HTTP 429 与错误码 `-32005 rate limit exceeded`（带 `Retry-After`），详见[哪些请求不计费：错误码与计费规则](/zh/guides/billing-rules/)。

因此，在大跨度日志查询时，建议采用顺序逐段请求；若使用批量请求，应严格控制批内调用数量，避免满权重之和超出突发上限。

## 相关接口与计费规则 [#相关接口与计费规则]

* 关于 `eth_getLogs` 与 Data API 转账接口（按地址转账与代币转账）的功能定位、覆盖范围及最终性差异，请参阅[节点近况与已索引历史：什么时候用 eth\_getLogs，什么时候用转账接口](/zh/guides/logs-vs-transfers/)。
* 关于方法权重、计算单元（CU）结算与各类错误码计费判定的完整说明，请参阅[哪些请求不计费：错误码与计费规则](/zh/guides/billing-rules/)。

## 下一步 [#下一步]

* [浏览数据集目录](https://blockvectra.com/zh/data/)，查看 BlockVectra 索引的全部数据集。
* [查看免费额度与定价](https://blockvectra.com/zh/pricing/#free)，确认账户可用的方案。
* [登录控制台](https://console.blockvectra.com/zh/login/?next=%2Fzh%2Fkeys%2F)创建 API key。
