# One key, many chains: switching an example to another chain

> Original page: https://docs.blockvectra.com/en/guides/one-key-many-chains/

## 1. One key across all supported chains [#1-one-key-across-all-supported-chains]

The same API key works on all supported chains for JSON-RPC, and for the Data API on chains where it is available. Keys belong to your account and are not bound to a specific chain; there is no need to generate separate API keys for each network.

Credits and rate limits are shared across all networks and across the JSON-RPC API and the Data API; they are not split by network. For detailed billing rules, see the [Pricing page](https://blockvectra.com/en/pricing/).

* **Pooled balance**: Paid top-ups and free credits apply across all chains. Calls on any chain draw from the same account balance.
* **Pooled rate limits**: Compute Unit (CU) refill rates and burst capacities apply across all chains for a given key. Free Plan per-second call limits are pooled across all supported chains rather than split per chain.
* **Upgrade path**: After topping up, you are no longer constrained by the Free Plan's per-second call limit; each key remains subject to CU rate and burst limits, as described in the [JSON-RPC documentation](/en/api/json-rpc/#method-policy).

## 2. URL structure and the `{chain}` parameter [#2-url-structure-and-the-chain-parameter]

Every chain-scoped request specifies its target network in the URL path using `{chain}`. The `{chain}` parameter is the lowercase slug identifier of the chain (for example `robinhood_mainnet`).

| Service           | Authentication        | URL template                 | Description                                                           |
| ----------------- | --------------------- | ---------------------------- | --------------------------------------------------------------------- |
| JSON-RPC          | Key in URL path       | `POST /v1/{chain}/{api_key}` | Simplest form, suitable for curl and HTTP clients                     |
| JSON-RPC          | Key in request header | `POST /v1/{chain}`           | Pass key via `x-api-key: {api_key}` request header                    |
| Data API          | REST routes           | `GET /v1/data/{chain}/…`     | Pass key via `x-api-key: {api_key}` request header                    |
| Public chain list | Unauthenticated       | `GET /v1/chains`             | Public list of chains and static facts (not billed, not rate-limited) |
| Public status     | Unauthenticated       | `GET /v1/status`             | Current service status and chain heads (not billed, not rate-limited) |

`GET /v1/chains` reports a `jsonrpc` and a `data` flag for each chain. Address a chain with the JSON-RPC URLs when it serves JSON-RPC, and with `GET /v1/data/{chain}/…` when its `data` flag is `true` (the Data API serves only those chains).

> **Tip**: When passing your key via request headers, format the URL to end with the chain name, **without** a trailing slash. JSON-RPC is served exclusively at `/v1/{chain}` and `/v1/{chain}/{api_key}`. Requests with a trailing slash (such as `/v1/{chain}/`) or missing a chain segment return HTTP 404 with an empty body. Requests to an unknown `{chain}` return HTTP 404 with `error.data.reason: "unknown_chain"` (evaluated before reading the request body or key check, not billed, and not rate-limited).

## 3. Programmatic chain discovery and capabilities [#3-programmatic-chain-discovery-and-capabilities]

Supported chains and their capabilities are served dynamically. Do not hardcode a static list of chains in your application. Instead, discover available networks and their capabilities at runtime:

### Discover static facts via `GET /v1/chains` [#discover-static-facts-via-get-v1chains]

This public endpoint is unauthenticated, not billed, and not rate-limited, returning all publicly available chains:

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

Example response (from OpenAPI specification):

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

Field reference:

* `chain`: Chain identifier slug (used for `{chain}` in URLs)
* `name`: Human-readable display name
* `chain_id`: EIP-155 chain ID (decimal integer)
* `jsonrpc`: Whether JSON-RPC is enabled
* `data`: Whether the Data API is enabled
* `methods`: JSON-RPC method policy for the chain, including `allow` (allowed methods or prefix wildcards) and `deny` (explicitly denied methods)
* `max_logs_block_range`: Maximum block range allowed in a single `eth_getLogs` request
* `state_window_blocks`: Historical state window size in blocks; `null` for full-history archive chains
* `info`: Reserved object for extended network information

### Check operational health via `GET /v1/status` [#check-operational-health-via-get-v1status]

This public endpoint is unauthenticated, not billed, and not rate-limited, returning service readiness and chain head information:

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

Example response (from OpenAPI specification):

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

Field reference:

* `gateway.status`: Service readiness status (`ok` or `degraded`)
* `chains[].data_features`: Capabilities provided by the Data API for this chain
* `chains[].status`: Node operational status (`ok` or `unavailable`)
* `chains[].head`: Most recently polled block head (`block`, `time`, `lag_seconds`)

## 4. Per-chain differences to keep in mind [#4-per-chain-differences-to-keep-in-mind]

When switching between chains, review the fields provided in the specification and `GET /v1/chains`:

1. **Method allowance and policy (`methods.allow` / `methods.deny`)**: Available JSON-RPC methods vary by network according to their method policy. Requesting a disallowed method returns HTTP 200 with JSON-RPC error code `-32601` (`method not available`, not billed).
2. **Log block range (`max_logs_block_range`)**: Maximum block spans for `eth_getLogs` queries differ by chain. Exceeding the chain's limit returns HTTP 200 with JSON-RPC error code `-32602` (`eth_getLogs block range too large`, not billed).
3. **State retention window (`state_window_blocks`)**: Full-history chains return `null`. On chains with state pruning, historical state queries outside the window return HTTP 200 with JSON-RPC error code `-32011` (`historical state is not available beyond the most recent <N> blocks`, not billed).
4. **Data API features and coverage (`data` / `data_features`)**: The chains that provide a dataset are listed on the [Supported Chains](/en/chains/) page. Querying a dataset a chain does not support, or a block before its indexed coverage, returns HTTP `422` (`error.code` `no_coverage`, not billed). When the service is temporarily unavailable — for example, when a chain is busy — requests return HTTP `503` with a `Retry-After` header (not billed).

## 5. Code examples [#5-code-examples]

The exact same code runs across different chains by updating the chain variable (or reading it from `GET /v1/chains`), querying `eth_blockNumber` via JSON-RPC and dataset freshness via the Data API:

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

    # Change the chain variable to target another chain from Supported Chains
    CHAIN="robinhood_mainnet"

    # 1. JSON-RPC: Query eth_blockNumber (POST /v1/{chain}, key in the x-api-key header).
    # The default-chain endpoint ends with its chain slug; swap that last segment for $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: Query dataset freshness (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
    // Change this variable to target another chain, or read it dynamically from GET /v1/chains
    const chain = "robinhood_mainnet";
    const apiKey = process.env.BLOCKVECTRA_API_KEY!;

    // 1. JSON-RPC: Call eth_blockNumber (POST /v1/{chain})
    // The default-chain endpoint ends with its chain slug; swap that last segment for `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: Query freshness (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

    # Change this variable to target another chain, or read it dynamically from GET /v1/chains
    chain = "robinhood_mainnet"
    api_key = os.environ["BLOCKVECTRA_API_KEY"]

    # 1. JSON-RPC: Call eth_blockNumber (POST /v1/{chain})
    # The default-chain endpoint ends with its chain slug; swap that last segment for `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: Query freshness (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>

### Example responses (from specifications) [#example-responses-from-specifications]

JSON-RPC `eth_blockNumber` successful response (billed at the method's CU weight):

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

Data API `GET /v1/data/{chain}/status/freshness` successful response (billed in CU, only 2xx successful responses are billed):

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

## Next steps [#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.
