# 一个 key 切链：把示例从一条链换到另一条链

> 原文地址: https://docs.blockvectra.com/zh/guides/one-key-many-chains/

## 1. 一个 key 适用于所有链 [#1-一个-key-适用于所有链]

同一个 API key 适用于所有支持链的 JSON-RPC；Data API 在已开放的链上可用。key 属于账户，不绑定特定链，无需为每条链单独申请 key。

服务额度和限流在所有网络之间、以及 JSON-RPC API 与 Data API 之间共用，不按网络区分。具体计费规则见[定价页](https://blockvectra.com/zh/pricing/)。

* **余额跨链合计**：账户充值与免费额度在所有链之间共用，所有链上的调用消耗同一账户余额。
* **限流跨链合计**：同一个 key 的 CU 速率与突发容量跨链共用；免费套餐每秒调用次数上限在所有支持链上合计计算，不分链拆算。
* **升级路径**：充值后不再受免费套餐的每秒调用次数上限约束；每个 key 仍有 CU 速率与突发上限，见 [JSON-RPC 文档](/zh/api/json-rpc/#方法策略)。

## 2. URL 结构与链名位置 [#2-url-结构与链名位置]

所有链相关请求都在 URL 中通过 `{chain}` 标明目标网络。`{chain}` 是全小写的链标识符（例如 `robinhood_mainnet`）。

| 服务类型     | 鉴权方式       | URL 模板                       | 说明                                  |
| -------- | ---------- | ---------------------------- | ----------------------------------- |
| JSON-RPC | key 放在路径中  | `POST /v1/{chain}/{api_key}` | key 直接作为 URL 路径段                    |
| JSON-RPC | key 放在请求头中 | `POST /v1/{chain}`           | 在 `x-api-key: {api_key}` 请求头中传入 key |
| Data API | REST 路由    | `GET /v1/data/{chain}/…`     | 在 `x-api-key: {api_key}` 请求头中传入 key |
| 公开链列表    | 免鉴权        | `GET /v1/chains`             | 返回公开链列表与各链静态参数（不计费、不限流）             |
| 公开服务状态   | 免鉴权        | `GET /v1/status`             | 返回各链运行状态与链头（不计费、不限流）                |

`GET /v1/chains` 会返回每条链的 `jsonrpc` 与 `data` 两个布尔字段：`jsonrpc` 为 `true` 的链用 JSON-RPC 地址，`data` 为 `true` 的链用 `GET /v1/data/{chain}/…`（Data API 只在这些链上提供）。

> **提示**：用请求头传 key 时，URL 以链名结尾，**不带**结尾斜杠。JSON-RPC 仅在 `/v1/{chain}` 和 `/v1/{chain}/{api_key}` 两种路径上提供；带结尾斜杠（如 `/v1/{chain}/`）或不带链段的请求返回 HTTP 404 且响应体为空。向未知 `{chain}` 发起的请求返回 HTTP 404 与 `error.data.reason: "unknown_chain"`（在读取请求体和检查 key 之前判定，不计费且不占限流）。

## 3. 程序化发现链与可用能力 [#3-程序化发现链与可用能力]

支持的链与各链能力是动态提供的，请不要在应用里硬编码链列表。可通过公开端点在运行时查询链名与能力：

### 通过 `GET /v1/chains` 查询静态参数 [#通过-get-v1chains-查询静态参数]

该端点无需鉴权、不计费、不限流，返回各已公开链及其参数：

```http
GET /v1/chains
```

响应示例（来自规格）：

```json
{
  "chains": [
    {
      "chain": "robinhood_mainnet", "name": "Robinhood Chain", "chain_id": 4663,
      "jsonrpc": true,
      "data": true,
      "methods": {
        "allow": ["eth_*", "net_*", "web3_*", "debug_trace*"],
        "deny": ["eth_newFilter", "eth_newBlockFilter", "eth_newPendingTransactionFilter", "eth_getFilterLogs", "eth_getFilterChanges", "eth_uninstallFilter", "eth_subscribe", "eth_unsubscribe"]
      },
      "max_logs_block_range": 1000,
      "state_window_blocks": 900,
      "info": {}
    }
  ]
}
```

字段说明：

* `chain`：链标识符（slug，传给 URL 中的 `{chain}`）
* `name`：人类可读显示名
* `chain_id`：EIP-155 链 ID
* `jsonrpc`：是否对外提供 JSON-RPC 服务
* `data`：是否对外提供 Data API 服务
* `methods`：该链的 JSON-RPC 方法策略，包含 `allow`（允许的方法或前缀通配符）与 `deny`（明确拒绝的方法列表）
* `max_logs_block_range`：单次 `eth_getLogs` 请求允许的最大区块跨度
* `state_window_blocks`：历史状态窗口大小（区块数）；全历史链为 `null`
* `info`：扩展信息对象

### 通过 `GET /v1/status` 查询运行状态 [#通过-get-v1status-查询运行状态]

该端点无需鉴权、不计费、不限流，反映服务就绪状态与各链链头：

```http
GET /v1/status
```

响应示例（来自规格）：

```json
{
  "checked_at": "2026-09-28T12:00:00Z",
  "gateway": {
    "status": "ok"
  },
  "chains": [
    {
      "chain": "robinhood_mainnet", "name": "Robinhood Chain", "chain_id": 4663,
      "jsonrpc": true,
      "data": true,
      "data_features": ["blocks", "transactions", "address_transactions", "transfers", "token_metadata", "freshness"],
      "status": "ok",
      "head": {
        "block": 73017329,
        "time": "2026-09-28T11:59:58Z",
        "lag_seconds": 2
      }
    }
  ]
}
```

字段说明：

* `gateway.status`：底层接入服务当前是否就绪（`ok` 或 `degraded`）
* `chains[].data_features`：Data API 为该链提供的能力列表
* `chains[].status`：该链的节点状态（`ok` 或 `unavailable`）
* `chains[].head`：最近一次成功轮询的链头信息（`block`、`time`、`lag_seconds`）

## 4. 切链时需要注意的差异 [#4-切链时需要注意的差异]

各链在规格与 `/v1/chains` 字段中体现的差异如下：

1. **方法允许与拒绝策略（`methods.allow` / `methods.deny`）**：不同链支持的 JSON-RPC 方法集合由其方法策略决定。若请求了该链不允许的方法，返回 HTTP 200 与 JSON-RPC 错误码 `-32601`（`method not available`，不计费）。
2. **日志区块跨度（`max_logs_block_range`）**：单次 `eth_getLogs` 允许跨越的区块数量因链而异。超出该链上限时返回 HTTP 200 与 JSON-RPC 错误码 `-32602`（`eth_getLogs block range too large`，不计费）。
3. **历史状态窗口（`state_window_blocks`）**：全历史链该字段为 `null`；若链设有状态保留窗口，查询超出窗口范围的历史状态将返回 HTTP 200 与 JSON-RPC 错误码 `-32011`（`historical state is not available beyond the most recent <N> blocks`，不计费）。
4. **Data API 特性与覆盖（`data` / `data_features`）**：提供该数据集的链以[支持的链](/zh/chains/)页面为准。请求该链未支持的数据集或超出历史覆盖范围的数据时，Data API 返回 HTTP `422`（`error.code` 为 `no_coverage`，不计费）。服务暂时不可用（例如某条链繁忙）时，返回 HTTP `503`（带 `Retry-After`，不计费）。

## 5. 跨链调用示例代码 [#5-跨链调用示例代码]

同一段代码只需修改链名变量（或从 `GET /v1/chains` 动态获取），即可在两条链上分别发起 JSON-RPC `eth_blockNumber` 调用与 Data API 数据新鲜度端点查询：

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

    # 修改链名变量即可切换网络，例如换为支持的链里的任一 {chain}
    CHAIN="robinhood_mainnet"

    # 1. JSON-RPC: 调用 eth_blockNumber（POST /v1/{chain}，key 放在 x-api-key 请求头）
    # 默认链端点以链名结尾；把最后一段替换为 $CHAIN。
    RPC_URL="https://dev-api.blockvectra.network/v1/robinhood_mainnet"
    RPC_URL="${RPC_URL%/*}/$CHAIN"
    curl -s "$RPC_URL" \
      -H "Content-Type: application/json" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY" \
      -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'

    # 2. Data API: 查询该链数据新鲜度 (GET /v1/data/{chain}/status/freshness)
    curl -s "https://dev-api.blockvectra.network/v1/data/$CHAIN/status/freshness" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    // 修改链名变量即可切换目标链，亦可从 GET /v1/chains 动态读取
    const chain = "robinhood_mainnet";
    const apiKey = process.env.BLOCKVECTRA_API_KEY!;

    // 1. JSON-RPC: 调用 eth_blockNumber (POST /v1/{chain})
    // 默认链端点以链名结尾；把最后一段替换为 `chain`。
    const defaultEndpoint = "https://dev-api.blockvectra.network/v1/robinhood_mainnet";
    const rpcUrl = `${defaultEndpoint.slice(0, defaultEndpoint.lastIndexOf("/"))}/${chain}`;
    const rpcResponse = await fetch(rpcUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": apiKey,
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "eth_blockNumber",
        params: [],
      }),
    });
    const rpcResult = await rpcResponse.json();
    console.log(`[${chain}] JSON-RPC blockNumber:`, rpcResult.result);

    // 2. Data API: 查询数据新鲜度 (GET /v1/data/{chain}/status/freshness)
    const dataUrl = `https://dev-api.blockvectra.network/v1/data/${chain}/status/freshness`;
    const dataResponse = await fetch(dataUrl, {
      headers: {
        "x-api-key": apiKey,
      },
    });
    const dataResult = await dataResponse.json();
    console.log(`[${chain}] Data API freshness:`, dataResult.data);
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    # 修改链名变量即可切换目标链，亦可从 GET /v1/chains 动态读取
    chain = "robinhood_mainnet"
    api_key = os.environ["BLOCKVECTRA_API_KEY"]

    # 1. JSON-RPC: 调用 eth_blockNumber (POST /v1/{chain})
    # 默认链端点以链名结尾；把最后一段替换为 `chain`。
    default_endpoint = "https://dev-api.blockvectra.network/v1/robinhood_mainnet"
    rpc_url = f"{default_endpoint.rsplit('/', 1)[0]}/{chain}"
    headers = {
        "Content-Type": "application/json",
        "x-api-key": api_key,
    }
    rpc_payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "eth_blockNumber",
        "params": [],
    }
    rpc_resp = requests.post(rpc_url, json=rpc_payload, headers=headers)
    print(f"[{chain}] JSON-RPC blockNumber:", rpc_resp.json().get("result"))

    # 2. Data API: 查询数据新鲜度 (GET /v1/data/{chain}/status/freshness)
    data_url = f"https://dev-api.blockvectra.network/v1/data/{chain}/status/freshness"
    data_resp = requests.get(data_url, headers={"x-api-key": api_key})
    print(f"[{chain}] Data API freshness:", data_resp.json().get("data"))
    ```
  </Tab>
</Tabs>

### 示例响应（来自规格） [#示例响应来自规格]

JSON-RPC `eth_blockNumber` 成功响应（按方法权重计费）：

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

Data API `GET /v1/data/{chain}/status/freshness` 成功响应（按 CU 计费，仅对 2xx 成功响应计费）：

```json
{
  "data": [
    {
      "dataset": "blocks",
      "category": "raw",
      "max_block_number": 72313256,
      "max_day": null,
      "max_time": "2026-09-28T03:41:07Z",
      "seconds_behind": 0,
      "blocks_behind": 0,
      "days_behind": null,
      "checked_at": "2026-09-28T03:41:10Z"
    },
    {
      "dataset": "traces",
      "category": "raw",
      "max_block_number": 72313256,
      "max_day": null,
      "max_time": "2026-09-28T03:41:07Z",
      "seconds_behind": 0,
      "blocks_behind": 0,
      "days_behind": null,
      "coverage_from_block": 72050949,
      "coverage_to_block": 72313256,
      "coverage_complete": true,
      "checked_at": "2026-09-28T03:41:10Z"
    },
    {
      "dataset": "dex_prices",
      "category": "derived",
      "max_block_number": null,
      "max_day": "2026-09-27",
      "max_time": "2026-09-27T00:00:00Z",
      "seconds_behind": 99667,
      "blocks_behind": null,
      "days_behind": 1,
      "checked_at": "2026-09-28T03:41:10Z"
    }
  ],
  "meta": {
    "chain": "robinhood_mainnet",
    "chain_slug": "ROBINHOOD_MAINNET",
    "chain_external_id": "eip155:4663",
    "as_of_block": 72313256,
    "finalized_block": 72313000,
    "coverage": "full",
    "refreshed_at": "2026-09-28T03:41:10Z"
  }
}
```

## 下一步 [#下一步]

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