# 단일 키로 다중 체인 이용: 예제를 다른 체인으로 전환하기

> Source: https://docs.blockvectra.com/ko/guides/one-key-many-chains/

## 1. 모든 지원 체인에서 사용 가능한 단일 키

동일한 API key가 JSON-RPC의 모든 지원 체인에서 작동하며, Data API가 제공되는 체인에서도 작동합니다. 키는 계정에 귀속되며 특정 체인에 종속되지 않으므로 각 네트워크마다 별도의 API key를 생성할 필요가 없습니다.

크레딧과 속도 제한은 모든 네트워크 전반에 걸쳐, 그리고 JSON-RPC API와 Data API 간에 공유되며 네트워크별로 분할되지 않습니다. 자세한 청구 규칙은 [요금 페이지](https://blockvectra.com/en/pricing/)를 참조하세요.

* **통합 잔액**: 유료 충전 및 무료 크레딧은 모든 체인에 걸쳐 적용됩니다. 어느 체인에서 발생한 호출이든 동일한 계정 잔액에서 차감됩니다.
* **통합 속도 제한**: Compute Unit(CU) 리필 속도와 버스트 용량은 주어진 키에 대해 모든 체인에 걸쳐 적용됩니다. 무료 플랜의 초당 호출 수 한도는 체인별로 나누어지는 것이 아니라 모든 지원 체인 전체에서 통합 적용됩니다.
* **업그레이드 경로**: 충전을 완료하면 더 이상 무료 플랜의 초당 호출 수 한도에 구속되지 않으며, 각 키는 [JSON-RPC 문서](https://docs.blockvectra.com/en/api/json-rpc/#method-policy)에 설명된 대로 CU 속도 및 버스트 한도의 적용을 계속 받습니다.

## 2. URL 구조 및 `{chain}` 파라미터

체인 범위의 모든 요청은 URL 경로에서 `{chain}`을 사용하여 대상 네트워크를 지정합니다. `{chain}` 파라미터는 체인의 소문자 slug 식별자입니다(예: `robinhood_mainnet`).

| 서비스       | 인증 방식        | URL 템플릿                      | 설명                                    |
| --------- | ------------ | ---------------------------- | ------------------------------------- |
| JSON-RPC  | URL 경로에 키 포함 | `POST /v1/{chain}/{api_key}` | curl 및 HTTP 클라이언트에 적합한 가장 간단한 형태      |
| JSON-RPC  | 요청 헤더에 키 포함  | `POST /v1/{chain}`           | `x-api-key: {api_key}` 요청 헤더를 통해 키 전달 |
| Data API  | REST 라우트     | `GET /v1/data/{chain}/…`     | `x-api-key: {api_key}` 요청 헤더를 통해 키 전달 |
| 공개 체인 목록  | 인증 불필요       | `GET /v1/chains`             | 체인 공개 목록 및 정적 정보 (미과금)                |
| 공개 서비스 상태 | 인증 불필요       | `GET /v1/status`             | 현재 서비스 상태 및 체인 헤드 (미과금)               |

`GET /v1/chains`는 각 체인에 대해 `jsonrpc` 및 `data` 플래그를 보고합니다. 체인이 JSON-RPC를 제공하는 경우 JSON-RPC URL을 사용하고, `data` 플래그가 `true`인 경우 `GET /v1/data/{chain}/…`을 사용하세요(Data API는 해당 체인에서만 제공됩니다).

> **팁**: 요청 헤더를 통해 키를 전달할 때는 끝에 슬래시를 **붙이지 않고** 체인 이름으로 URL이 끝나도록 구성하세요. JSON-RPC는 `/v1/{chain}` 및 `/v1/{chain}/{api_key}`에서만 독점적으로 제공됩니다. 끝에 슬래시가 붙은 요청(예: `/v1/{chain}/`)이나 체인 세그먼트가 누락된 요청은 빈 본문과 함께 HTTP 404를 반환합니다. 알 수 없는 `{chain}`에 대한 요청은 `error.data.reason: "unknown_chain"`과 함께 HTTP 404를 반환합니다(미과금).

## 3. 프로그래밍 방식의 체인 및 기능 탐색

지원되는 체인과 해당 기능은 동적으로 제공됩니다. 애플리케이션에 정적인 체인 목록을 하드코딩하지 마세요. 대신 런타임에 사용 가능한 네트워크와 기능을 탐색하세요:

### `GET /v1/chains`를 통한 정적 정보 탐색

이 공개 엔드포인트는 인증이 필요 없고 과금되지 않으며, 공개적으로 사용 가능한 모든 체인을 반환합니다:

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

응답 예시:

```json
{
  "chains": [
    {
      "chain": "robinhood_mainnet", "name": "Robinhood Chain", "chain_id": 4663,
      "jsonrpc": true,
      "data": true,
      "methods": {
        "allow": ["eth_blockNumber", "eth_call", "eth_chainId", "debug_traceTransaction"],
        "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
    }
  ]
}
```

필드 레퍼런스:

* `chain`: 체인 식별자 slug (URL의 `{chain}`에 사용됨)
* `name`: 사람이 읽을 수 있는 표시 이름
* `chain_id`: EIP-155 체인 ID (10진수 정수)
* `jsonrpc`: JSON-RPC 활성화 여부
* `data`: Data API 활성화 여부
* `methods`: `allow`(허용된 메서드) 및 `deny`(명시적으로 거부된 메서드)를 포함한 해당 체인의 JSON-RPC 메서드 정책
* `max_logs_block_range`: 단일 `eth_getLogs` 요청에서 허용되는 최대 블록 범위
* `state_window_blocks`: 블록 단위의 과거 상태 보존 윈도우 크기; 제한이 없는 경우 `null`

### `GET /v1/status`를 통한 운영 상태 확인

이 공개 엔드포인트는 인증이 필요 없고 과금되지 않으며, 서비스 준비 상태 및 체인 헤드 정보를 반환합니다:

```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. 유의해야 할 체인별 차이점

체인 간에 전환할 때 `GET /v1/chains`에서 제공되는 필드를 검토하세요:

1. **메서드 허용 및 정책 (`methods.allow` / `methods.deny`)**: 사용 가능한 JSON-RPC 메서드는 해당 네트워크의 메서드 정책에 따라 달라집니다. 허용되지 않은 메서드를 요청하면 JSON-RPC 오류 코드 `-32601`(`method not available`, 미과금)과 함께 HTTP 200이 반환됩니다.
2. **로그 블록 범위 (`max_logs_block_range`)**: `eth_getLogs` 쿼리의 최대 블록 범위는 체인마다 다릅니다. 체인의 제한을 초과하면 JSON-RPC 오류 코드 `-32602`(`eth_getLogs block range too large`, 미과금)와 함께 HTTP 200이 반환됩니다.
3. **상태 보존 윈도우 (`state_window_blocks`)**: 전체 기록을 보존하는 체인은 `null`을 반환합니다. 상태 가지치기가 적용되는 체인의 경우 윈도우 외부의 과거 상태를 쿼리하면 JSON-RPC 오류 코드 `-32011`(`historical state is not available beyond the most recent <N> blocks`, 미과금)과 함께 HTTP 200이 반환됩니다.
4. **Data API 기능 및 지원 범위 (`data` / `data_features`)**: 특정 데이터셋을 제공하는 체인은 [지원 체인](https://docs.blockvectra.com/en/chains/) 페이지에 나열되어 있습니다. 체인이 지원하지 않는 데이터셋이나 인덱싱된 범위 이전의 블록을 쿼리하면 HTTP `422`(`error.code` `no_coverage`, 미과금)가 반환됩니다. 체인이 혼잡한 경우 등 서비스가 일시적으로 이용 불가능한 경우 요청은 `Retry-After` 헤더와 함께 HTTP `503`을 반환합니다(미과금).

## 5. 코드 예제

완전한 스타터 템플릿: [blockvectra/multichain-viem](https://github.com/blockvectra/multichain-viem)

체인 변수를 업데이트(또는 `GET /v1/chains`에서 읽어오기)하는 것만으로 정확히 동일한 코드가 여러 체인에서 실행되며, JSON-RPC를 통해 `eth_blockNumber`를 쿼리하고 Data API를 통해 데이터셋 최신성(freshness)을 쿼리합니다:

**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 헤더에 포함).
RPC_URL="https://api.blockvectra.com/v1/$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://api.blockvectra.com/v1/data/$CHAIN/status/freshness" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```


  **TypeScript**

```ts
// 다른 체인을 대상으로 하도록 이 변수를 변경하거나, GET /v1/chains에서 동적으로 읽어옵니다
const chain = "robinhood_mainnet";
const apiKey = process.env.BLOCKVECTRA_API_KEY!;

// 1. JSON-RPC: eth_blockNumber 호출 (POST /v1/{chain})
const rpcUrl = `https://api.blockvectra.com/v1/${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://api.blockvectra.com/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);
```


  **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})
rpc_url = f"https://api.blockvectra.com/v1/{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://api.blockvectra.com/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"))
```


### 응답 예시

JSON-RPC `eth_blockNumber` 성공 응답 (해당 메서드의 CU 가중치로 과금됨):

```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": null,
      "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": null,
      "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,
    "safe_block": 72313100,
    "finalized_block": 72313000,
    "coverage": "full",
    "refreshed_at": "2026-09-28T03:41:10Z"
  }
}
```

## 다음 단계

* [데이터셋 디렉터리 둘러보기](https://blockvectra.com/en/data/): BlockVectra가 인덱싱하는 모든 데이터셋 확인.
* [무료 플랜 및 요금 확인](https://blockvectra.com/en/pricing/#free): 내 계정에 포함된 혜택 확인.
* [콘솔 로그인](https://console.blockvectra.com/login/?next=%2Fkeys%2F): API key 생성.
