Recent node data vs indexed history: when to use eth_getLogs and when to use the transfers API
Compare the JSON-RPC eth_getLogs method with the Data API transfers endpoints: block ranges, pagination, coverage, and finality limits, and which one fits typical tasks.
Two ways to read logs and transfers
eth_getLogs is a JSON-RPC method: it returns block logs through the JSON-RPC endpoint. The Data API exposes token transfer history through two chain-scoped endpoints:
GET /{chain}/addresses/{address}/transfers— transfers involving an address.GET /{chain}/tokens/{token}/transfers— transfers for a single token contract.
Both use the same API key and are metered in CU by method weight (see the weights below). Which one fits depends on how recent the data is, whether you need a block window, and how you paginate.
Limits that apply to eth_getLogs
eth_getLogs is bounded by per-chain limits that the public GET /v1/chains response publishes:
- Block span:
max_logs_block_rangeis the maximum number of blocks a singleeth_getLogsrequest may span. It differs by chain — read it fromGET /v1/chains(chains are listed on Supported Chains) instead of hardcoding it. A wider range is rejected with JSON-RPC error-32602 eth_getLogs block range too large(not billed). - Node sync: while a chain's node is not synced,
eth_getLogs— like every method excepteth_chainId— returns-32010; the call is not forwarded and not billed. - State window: the state window that
GET /v1/chainsreports asstate_window_blocksapplies to state-reading methods such aseth_callandeth_getBalance, not toeth_getLogs. - Node pruning: block and log reads are not limited by the state window, but they are limited by the node's retained history. Data that has been pruned returns
4444 pruned history unavailable(not billed).
When the fromBlock and toBlock filter fields are omitted or null, they default to latest.
WebSocket subscriptions are not supported: eth_subscribe returns -32601 method not available. To follow new events, poll eth_getLogs over the newest blocks.
What the Data API transfers endpoints provide
The two endpoints require different parameters:
| Endpoint | standard | Block window |
|---|---|---|
GET /{chain}/addresses/{address}/transfers | Required: erc20 or erc721. erc1155 returns 422 no_coverage | from_block and to_block are both required. Results are ordered by (block_number, log_index) descending. direction (in, out, or any; default any) filters by direction, and token optionally restricts the results to one contract. |
GET /{chain}/tokens/{token}/transfers | Required: erc20, erc721, or erc1155 | from_block and to_block are optional. An absent to_block defaults to finalized_block; an explicit value above it is a hard 409, with no clamp escape. |
Pagination
Both endpoints are keyset-paginated:
limitdefaults to 50; values above 500 are clamped to 500, and0or a non-integer returns400 bad_request.next_cursorappears only when there is another page. On the last page the key is absent entirely, nevernull.- Pass the returned value back as
cursor, unchanged, to fetch the next page. A cursor is valid only for the chain, endpoint, and query parameters that issued it.
Coverage and finality
- Both endpoints belong to the
transferscapability. A chain that does not provide it returns422 no_coverage. Chains that provide this dataset are subject to the Supported Chains page. GET /v1/data/chainsreports each chain'scoverage(history_mode,from_block, andretention_dayson window chains). A block window entirely before the chain's first indexed block is422 no_coverage; one that starts before it is served as far as it goes, withmeta.coverage = "partial". On window chainscoverage.from_blockmoves forward — read it at runtime.- Block-scoped responses only serve data at or below
meta.finalized_block, the reorg-safety watermark (not a consensus finality signal), which trailsas_of_blockby a per-chain margin. - For address transfers, a
to_blockabovefinalized_blockis409 finality_exceededunlessclamp=truetruncates it down tofinalized_block; a window that is too wide is409 window_too_largeunlessclamp=true. Token transfers have noclampescape.
Each transfer item contains token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index, and log_index. ERC-20 items add amount; ERC-721 items add token_id; ERC-1155 items add operator, token_id, value, and batch_index.
Which one to use
| Typical task | Better fit | Why |
|---|---|---|
| Events in the most recent few hundred blocks | eth_getLogs | One request can cover a recent range as long as it does not exceed that chain's max_logs_block_range. Unlike the transfers endpoints, it is not limited to blocks at or below finalized_block. |
| An address's historical transfers | GET /{chain}/addresses/{address}/transfers | Address-scoped query with a from_block/to_block window, direction and token filters, and cursor pagination; results stop at finalized_block. |
| All transfers of a token | GET /{chain}/tokens/{token}/transfers | Token-contract-scoped query covering erc20, erc721, and erc1155, with an optional window and cursor pagination for the full result set. |
| Live monitoring of new events | eth_getLogs (polling) | WebSocket subscriptions are not supported (eth_subscribe returns -32601), and the transfers endpoints only serve data at or below finalized_block. Poll eth_getLogs over the newest blocks. |
Querying logs with eth_getLogs
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# fromBlock / toBlock default to latest. Set an explicit recent range to follow
# new events, and keep its span within the chain's 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": "latest",
"toBlock": "latest"
}]
}'Querying transfers with the Data API
export BLOCKVECTRA_API_KEY=rgw_your_api_key
# from_block / to_block are optional here; omitting to_block defaults to finalized_block.
curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers?standard=erc20" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"To query by address instead, from_block and to_block are required:
# clamp=true truncates a too-wide window, or a to_block above finalized_block,
# instead of returning 409.
curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/transfers?standard=erc20&from_block=0&to_block=73000000&direction=any&clamp=true" \
-H "x-api-key: $BLOCKVECTRA_API_KEY"CU per call
Every method is billed by its CU weight. The weights below are read from the platform plans API at build time:
CU weight per call
| Method | CU per call |
|---|---|
eth_getLogs | 30 |
data.address_transfers | 25 |
data.token_transfers | 25 |
Weights are read from the platform plans API at build time.
For current prices and top-up options, see the Pricing page.
Next steps
- Browse the datasets directory to see every dataset BlockVectra indexes.
- See the free plan and pricing to check what your account includes.
- Log in to the console to create an API key.
Connect an AI agent to BlockVectra: llms.txt, OpenAPI and public JSON
Integration guide for AI agents and LLM tools: discover capabilities, query public metadata, and call BlockVectra APIs using llms.txt, OpenAPI specs, and public endpoints.
Per-method pricing: reading CU and price per million calls
Understand BlockVectra's CU metering, method weight resolution rules, price per million calls formula, and how to estimate per-cycle usage and costs.