BlockVectra

Quickstart

Call the JSON-RPC endpoint and the Data API with your API key.

This page shows the minimum you need to make your first authenticated call: how to get an API key, how to call JSON-RPC with curl, and how to shape a Data API request. It reflects the current behavior of rpc-gateway (data plane) and the planned behavior of the Data API served by chain-indexer.

1. Get an API key

The console (self-service key management) has not shipped yet. Until it does, our operations team provisions accounts and keys manually. Go to Get an API key on the main site and we'll set you up.

Every key looks like rgw_ followed by 64 hex characters, for example rgw_1f2e... (truncated). Keep it secret — anyone with the key can spend your balance.

2. Call JSON-RPC

All JSON-RPC traffic goes through a single gateway entry point, https://dev-api.blockvectra.network/v1/. There is no WebSocket support and no CORS — the endpoint is meant to be called from a backend, not a browser.

You can pass the key in one of two ways.

Key in the URL path

API_KEY="rgw_your_api_key"

curl -s "https://dev-api.blockvectra.network/v1/$API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'

Key in a request header

API_KEY="rgw_your_api_key"

curl -s "https://dev-api.blockvectra.network/v1/" \
  -H 'Content-Type: application/json' \
  -H "x-api-key: $API_KEY" \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'

Don't drop the trailing slash

When you pass the key in a header, the path must be https://dev-api.blockvectra.network/v1/ with the trailing slash. A request to the same URL without the trailing slash gets redirected (301) to the version with the slash, and most HTTP clients turn the redirected request into a GET, which then fails. Passing the key in the URL path does not have this problem.

An Authorization: Bearer <api_key> header also works and is used if x-api-key is absent or empty. If both are present, a non-empty x-api-key wins.

Batch calls

Send an array to make several calls in one request (up to 100 per batch). This example reads the chain ID and an account balance in a single round trip:

curl -s "https://dev-api.blockvectra.network/v1/" \
  -H 'Content-Type: application/json' \
  -H "x-api-key: $API_KEY" \
  -d '[
    {"jsonrpc":"2.0","id":1,"method":"eth_chainId"},
    {"jsonrpc":"2.0","id":2,"method":"eth_getBalance","params":["0x1111111111111111111111111111111111111111","latest"]}
  ]'

Responses come back as an array, in the same order as the requests, matched by id.

3. Understand CU billing

Every billed call consumes Compute Units (CU): cheap calls like eth_blockNumber or eth_chainId cost 1 CU, common reads like eth_getBlockByNumber cost a handful, heavier calls like eth_call or eth_getLogs cost more, and debug_trace* calls cost the most. Cached responses are always billed at 1 CU. Settlement is floor(total CU / 1000) billing units per account per hour — the unit price itself is still being finalized by the business side.

The full method-by-method weight table, cache behavior, and error codes live in API Reference → JSON-RPC once it ships — this page only covers the shape of a request.

Common errors

What you didWhat comes back
Missing, unknown, or disabled API keyHTTP 404 with an empty body (the gateway does not distinguish the reason)
Balance is zero or negativeHTTP 402, JSON-RPC code -32020
Too many requests, or a single request exceeds your burst capacityHTTP 429, JSON-RPC code -32005
A method that isn't allowed (e.g. eth_subscribe, or anything outside eth_*/net_*/web3_*/debug_trace*)HTTP 200, JSON-RPC code -32601, not billed
Malformed JSON bodyHTTP 200, JSON-RPC code -32700, not billed
More than 100 calls in one batchHTTP 200, JSON-RPC code -32600 (batch too large), not billed

Gateway-side rejections (all of the above) are never billed. Only calls the upstream node actually answers are billed.

4. Call the Data API

The Data API exposes read-only chain data (blocks, transactions, balances, holders, DEX activity, and more) as REST/JSON. It's implemented by chain-indexer's serve-data process, which today only listens on loopback (127.0.0.1:8546) — the public path prefix it will be reachable under once rpc-gateway forwards to it has not been finalized yet. Always call https://dev-api.blockvectra.network/v1 from your environment rather than hardcoding a host or a prefix; it will resolve to the right thing once the forwarding ships.

The paths below (/blocks/{number}, /status/freshness, /addresses/{address}/balances) are exactly what serve-data itself defines. Every success response uses the same envelope: data (the payload), next_cursor (only present when there's another page — otherwise the key is absent entirely, never null), and meta (as_of_block, finalized_block, coverage, refreshed_at, rows_read when known, query_ms). Every error response is {"error":{"code","message"}} — exactly those two fields, nothing else. Values that can exceed 2^53 (balances, token amounts) are decimal strings, never JSON numbers.

Look up a block by number:

curl -s "https://dev-api.blockvectra.network/v1/blocks/72838701"
{
  "data": {
    "number": 72838701,
    "hash": "0x9f2c1e7a4b6d3f805e1c9a72b4d6f1e0a3c8b5d7e2f4a1c6b9d3e7f0a2c4b6d8",
    "parent_hash": "0x1a3c5e7f9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c5e7b9d1f3a",
    "timestamp": "2026-09-26T05:41:07Z",
    "miner": "0x0000000000000000000000000000000000a4b05",
    "gas_limit": 32000000,
    "gas_used": 4821932,
    "base_fee_per_gas": "100000000",
    "state_root": "0x2b4d6f8a1c3e5b7d9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d",
    "transactions_root": "0x3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c5e",
    "receipts_root": "0x4d6f8a1c3e5b7d9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f",
    "tx_count": 239,
    "size": 48213,
    "l1_block_number": null,
    "extra": {}
  },
  "meta": {
    "as_of_block": 72838957,
    "finalized_block": 72838701,
    "coverage": "full",
    "refreshed_at": "2026-09-27T02:15:03Z",
    "rows_read": 1,
    "query_ms": 4
  }
}

A number with no live row (never indexed, or rolled back by a reorg) is 404 (error.code: "not_found"); a number above finalized_block is 409 (error.code: "finality_exceeded") — finality lags the indexed head by 256 blocks.

Check data freshness (how far every tracked table lags behind the chain head — useful for a status page or a pre-flight check before you trust a query):

curl -s "https://dev-api.blockvectra.network/v1/status/freshness"
{
  "data": [
    {
      "database": "robinhood",
      "table_name": "blocks",
      "category": "chain",
      "key_kind": "block",
      "max_block_number": 72838957,
      "max_day": null,
      "max_time": "2026-09-27T02:15:01Z",
      "seconds_behind": 6,
      "blocks_behind": 0,
      "days_behind": null,
      "rows_estimate": 72838958,
      "bytes_estimate": 12345678901,
      "follow_last_block": 72838957,
      "follow_lag_blocks": 0,
      "trace_min_block": null,
      "trace_max_block": null,
      "trace_gap_ranges": null,
      "trace_gap_blocks": null,
      "checked_at": "2026-09-27T02:15:07Z"
    }
  ],
  "meta": {
    "as_of_block": 72838957,
    "finalized_block": 72838701,
    "coverage": "full",
    "refreshed_at": "2026-09-27T02:15:07Z",
    "query_ms": 412
  }
}

If the underlying data_freshness view isn't installed yet on a given environment, this returns 422 (error.code: "dataset_not_installed") instead of a partial result.

List an address's ERC-20 balances (a snapshot refreshed every 6 hours, filtered to non-zero balances, ordered by token):

curl -s "https://dev-api.blockvectra.network/v1/addresses/0x1111111111111111111111111111111111111111/balances"
{
  "data": [
    { "token": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", "balance": "500000000", "symbol": "WBTC", "decimals": 8 },
    { "token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "balance": "1250000000", "symbol": "USDC", "decimals": 6 }
  ],
  "meta": {
    "as_of_block": 72838957,
    "finalized_block": 72838701,
    "coverage": "full",
    "refreshed_at": "2026-09-27T02:10:00Z",
    "rows_read": 2,
    "query_ms": 9
  }
}

An address with no non-zero balances still returns 200 with data: [] — never 404. Pass ?limit= (default 50, max 500) and the returned next_cursor to page through more.

Full endpoint coverage — blocks, transactions, addresses, tokens, NFTs, DEX, tokenized stocks — will ship in API Reference → Data API, generated from the same pinned OpenAPI document these examples come from.

Where to next

On this page