# Recent node data vs indexed history: when to use eth_getLogs and when to use the transfers API

> Original page: https://docs.blockvectra.com/en/guides/logs-vs-transfers/

## Two ways to read logs and transfers [#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 [#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_range` is the maximum number of blocks a single `eth_getLogs` request may span. It differs by chain — read it from `GET /v1/chains` (chains are listed on [Supported Chains](/en/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 except `eth_chainId` — returns `-32010`; the call is not forwarded and not billed.
* **State window**: the state window that `GET /v1/chains` reports as `state_window_blocks` applies to state-reading methods such as `eth_call` and `eth_getBalance`, not to `eth_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 [#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 [#pagination]

Both endpoints are keyset-paginated:

* `limit` defaults to 50; values above 500 are clamped to 500, and `0` or a non-integer returns `400 bad_request`.
* `next_cursor` appears only when there is another page. On the last page the key is absent entirely, never `null`.
* 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 [#coverage-and-finality]

* Both endpoints belong to the `transfers` capability. A chain that does not provide it returns `422 no_coverage`. Chains that provide this dataset are subject to the [Supported Chains](/en/chains/) page.
* `GET /v1/data/chains` reports each chain's `coverage` (`history_mode`, `from_block`, and `retention_days` on window chains). A block window entirely before the chain's first indexed block is `422 no_coverage`; one that starts before it is served as far as it goes, with `meta.coverage = "partial"`. On window chains `coverage.from_block` moves 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 trails `as_of_block` by a per-chain margin.
* For address transfers, a `to_block` above `finalized_block` is `409 finality_exceeded` unless `clamp=true` truncates it down to `finalized_block`; a window that is too wide is `409 window_too_large` unless `clamp=true`. Token transfers have no `clamp` escape.

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 [#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 [#querying-logs-with-eth_getlogs]

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    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"
        }]
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const res = await fetch("https://dev-api.blockvectra.network/v1/robinhood_mainnet", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "eth_getLogs",
        params: [{
          address: "0x1111111111111111111111111111111111111111",
          fromBlock: "latest",
          toBlock: "latest",
        }],
      }),
    });

    const { result } = await res.json();
    console.log(result);

    // npx tsx example.mts
    ```
  </Tab>

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

    res = requests.post(
        "https://dev-api.blockvectra.network/v1/robinhood_mainnet",
        headers={
            "Content-Type": "application/json",
            "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "eth_getLogs",
            "params": [{
                "address": "0x1111111111111111111111111111111111111111",
                "fromBlock": "latest",
                "toBlock": "latest",
            }],
        },
    )
    res.raise_for_status()
    print(res.json())
    ```
  </Tab>
</Tabs>

## Querying transfers with the Data API [#querying-transfers-with-the-data-api]

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    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"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    let cursor: string | undefined;

    do {
      const url = new URL(
        "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers",
      );
      url.searchParams.set("standard", "erc20");
      if (cursor) url.searchParams.set("cursor", cursor);

      const res = await fetch(url, {
        headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
      });
      const body = await res.json();
      console.log(body.data);
      cursor = body.next_cursor; // absent on the last page
    } while (cursor);

    // npx tsx example.mts
    ```
  </Tab>

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

    url = "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers"
    cursor = None

    while True:
        params = {"standard": "erc20"}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            url,
            params=params,
            headers={"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]},
        )
        res.raise_for_status()
        body = res.json()
        print(body["data"])
        cursor = body.get("next_cursor")  # absent on the last page
        if not cursor:
            break
    ```
  </Tab>
</Tabs>

To query by address instead, `from_block` and `to_block` are required:

```bash
# 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 [#cu-per-call]

Every method is billed by its CU weight. The weights below are read from the platform plans API at build time:

<LogsVsTransfersCu lang="en" />

For current prices and top-up options, see the [Pricing page](https://blockvectra.com/en/pricing/).

## 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.
