# Query Historical EVM State Within Supported Windows

> Original page: https://docs.blockvectra.com/en/guides/evm-historical-state/

Historical `eth_call` depends on the chain's state window, not its `eth_getLogs` block-range limit. Check the state window, the endpoint's authentication mode and the target block before reading an earlier contract value.

## Three different historical limits

| Field in [GET /v1/chains](https://api.blockvectra.com/v1/chains) | What it controls                                                                                                            | What to check                                                                                                                                   |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `state_window_blocks`                                            | How far back authenticated state reads such as `eth_call`, `eth_getBalance`, `eth_getCode` and `eth_getStorageAt` can query | With head `H` and declared window `W`, a numbered block older than `H − W` is outside the window. Check `methods.allow` and `methods.deny` too. |
| `public.history_blocks`                                          | Historical block references through the keyless `public.url`                                                                | Use only `public.methods`. For state reads, the smaller of the public history and the declared state window applies.                            |
| `max_logs_block_range`                                           | The number of blocks in one authenticated `eth_getLogs` request                                                             | Count `toBlock − fromBlock + 1`. A permitted span does not establish that old contract state or logs are available.                             |

These limits are in blocks, not days. A `null` or undeclared state window does not establish archive coverage. Keyless method availability is separate from authenticated method availability: a log span alone does not enable public `eth_getLogs`.

## Compare per-chain state windows

The table shows published state windows, keyless history, log spans and declared Data API datasets from the public snapshot. For a request now, read [GET /v1/chains](https://api.blockvectra.com/v1/chains) and [GET /v1/status](https://api.blockvectra.com/v1/status) again.

Developers and AI Agents should check state windows and log query spans separately. A null state window does not establish archive coverage. Public history applies only to the declared public methods.

| Chain | Chain slug | Authenticated state window: state_window_blocks (blocks) | Keyless history: public.history_blocks (blocks) | Authenticated log query span: max_logs_block_range (blocks) | Declared Data API datasets |
| --- | --- | --- | --- | --- | --- |
| Arbitrum One | `arb_mainnet` | 6,000 | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Base | `base_mainnet` | 10,000 | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| BNB Smart Chain | `bsc_mainnet` | 100 | 100 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Ethereum | `eth_mainnet` | 250,000 | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Ethereum Sepolia | `eth_sepolia` | Not declared | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| HyperEVM | `hyperevm_mainnet` | Not declared | 1,000 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, balances, holders, nfts, freshness |
| Polygon | `polygon_mainnet` | 126 | 126 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, freshness |
| Robinhood Chain | `robinhood_mainnet` | 900 | 900 | 1,000 | blocks, transactions, address_transactions, transfers, token_metadata, balances, holders, nfts, dex_swaps, dex_prices, stocks, traces, freshness |
| Robinhood Chain Testnet | `robinhood_testnet` | 1,023 | 1,000 | 1,000 | Data API unavailable |

[GET /v1/chains](https://api.blockvectra.com/v1/chains) · Sampled (UTC): 2026-10-08

[GET /v1/status](https://api.blockvectra.com/v1/status) · Sampled (UTC): 2026-10-08

## Choose a block tag

Use `latest` for the current value. For a historical comparison, read `eth_blockNumber` once and convert a chosen block number to a hexadecimal quantity such as `0x18efa2f`. Keep that number fixed for every call in the comparison; repeated `latest` calls can use different blocks.

For state reads, `earliest`, `safe` and `finalized` return `-32011` under the state-window policy. Choose an explicit block number within the declared window instead. A block-hash form is not a way to obtain additional history: keyless state reads reject it, and an authenticated request still depends on available state.

A block number may refer to a different block after a reorganization. Record the block hash with `eth_getBlockByNumber` if you need to identify the result's block. An in-window number also needs a synced chain and an existing contract at that height.

## Fixed-block contract read

On Ethereum, WETH at `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` exposes `decimals()` with selector `0x313ce567`. A keyless call sampled on 2026-10-08 (UTC) at block `0x18efa2f` returned HTTP 200 with this result:

Request to the chain's `public.url`:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "eth_call",
  "params": [
    { "to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "data": "0x313ce567" },
    "0x18efa2f"
  ]
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000012"
}
```

The ABI-encoded integer is 18. The result is a decimals value, not a balance, and does not establish availability at other heights. That fixed block will age out of a bounded window; use a recent block when running the following example later.

Save the example as `historical-state.mjs` and run `node historical-state.mjs` with Node.js 24 or later and your `BLOCKVECTRA_API_KEY` environment variable set. It uses the authenticated endpoint, keeps the same contract and calldata, and compares `latest`, one fixed recent block and a block outside the published authenticated window. Each output includes the actual HTTP status and JSON-RPC body; an HTTP 200 can still contain an error. It stops on an unexpected response instead of treating it as a successful read.

```js
const key = process.env.BLOCKVECTRA_API_KEY;
if (!key) throw new Error('Set BLOCKVECTRA_API_KEY');
const chainsUrl = 'https://api.blockvectra.com/v1/chains';
const catalogResponse = await fetch(chainsUrl, { signal: AbortSignal.timeout(15_000) });
if (!catalogResponse.ok) throw new Error(`Chains HTTP ${catalogResponse.status}`);
const catalog = await catalogResponse.json();
const chain = catalog.chains.find(item => item.chain === 'eth_mainnet');
const matches = (method, pattern) => pattern.endsWith('*')
  ? method.startsWith(pattern.slice(0, -1)) : method === pattern;
if (!chain?.jsonrpc || !['eth_call', 'eth_blockNumber'].every(method =>
  chain.methods?.allow?.some(pattern => matches(method, pattern)) &&
  !chain.methods?.deny?.some(pattern => matches(method, pattern)))) {
  throw new Error('Required methods are unavailable');
}
const window = chain.state_window_blocks;
if (!Number.isSafeInteger(window) || window < 10) {
  throw new Error('This example needs a declared state window of at least 10 blocks');
}
const rpcUrl = new URL('./eth_mainnet', chainsUrl).href;
let id = 0;
async function rpc(method, params) {
  const response = await fetch(rpcUrl, {
    method: 'POST', redirect: 'error', signal: AbortSignal.timeout(15_000),
    headers: { 'Content-Type': 'application/json', 'x-api-key': key },
    body: JSON.stringify({ jsonrpc: '2.0', id: ++id, method, params }),
  });
  return { http: response.status, body: await response.json() };
}
const headResponse = await rpc('eth_blockNumber', []);
if (headResponse.http !== 200 || headResponse.body.error ||
    !/^0x[0-9a-f]+$/i.test(headResponse.body.result ?? '')) {
  throw new Error(`Cannot read head: ${JSON.stringify(headResponse)}`);
}
const head = BigInt(headResponse.body.result);
if (head <= BigInt(window)) throw new Error('Head is too low for an out-of-window block');
const hex = value => `0x${value.toString(16)}`;
const fixedBlock = hex(head - 10n);
const outsideBlock = hex(head - BigInt(window) - 1n);
const call = { to: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', data: '0x313ce567' };
for (const block of ['latest', fixedBlock, outsideBlock]) {
  const reply = await rpc('eth_call', [call, block]);
  console.log(JSON.stringify({ head: hex(head), block, ...reply }));
  if (block === outsideBlock) {
    if (reply.http !== 200 || reply.body.error?.code !== -32011 ||
        reply.body.error?.data?.reason !== 'state_window') {
      throw new Error('Expected state_window; inspect the actual response above');
    }
  } else if (reply.http !== 200 || reply.body.error ||
      reply.body.result !== '0x0000000000000000000000000000000000000000000000000000000000000012') {
    throw new Error('Expected the WETH decimals result; inspect the actual response above');
  }
}
```

The recorded response above uses `public.url`; the script uses an API key. To make a keyless read, take the URL directly from `public.url`, omit the key, and choose a block within `public.history_blocks` as well as the state window. Changing authentication can change the permitted history, even for the same contract and calldata.

## Diagnose an out-of-window error

On the same keyless endpoint, a call sampled on 2026-10-08 (UTC) changing only the target block to `0x18ef650` (and the request ID) returned HTTP 200 with `error.code: -32011`, `error.data.reason: state_window` and `error.data.retryable: false`. Its message was `block reference is outside the public history window`. This is a public-history failure; the authenticated endpoint has its own state window.

Use these fields from the [state\_window error entry](https://docs.blockvectra.com/en/errors/#state_window) to recognize the failure rather than relying on a particular window number in the message:

| Field                  | Documented value or meaning                                                                                                         |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| HTTP status            | `200`; inspect the JSON-RPC `error` even when HTTP succeeds                                                                         |
| `error.code`           | `-32011`                                                                                                                            |
| `error.message`        | Authenticated state-window errors describe the most recent supported block count; public-history errors can use a different message |
| `error.data.reason`    | `state_window`                                                                                                                      |
| `error.data.docs_url`  | Link to the error catalog's `state_window` explanation                                                                              |
| `error.data.retryable` | `false`: sending the same request later does not restore older state                                                                |

Choose a newer numbered block or use `latest` if the task needs the current value. Reducing an `eth_getLogs` span does not recover historical `eth_call` state. Other `-32011` reasons have different actions: [range\_not\_indexed](https://docs.blockvectra.com/en/errors/#range_not_indexed) requires a covered range; [history\_not\_ready](https://docs.blockvectra.com/en/errors/#history_not_ready) allows retry after indexing catches up. Inspect `error.data.reason`, not only the numeric code.

Underlying state can also be unavailable with `-32000`, or pruned block history with `4444`; see the [error catalog](https://docs.blockvectra.com/en/errors/). Do not retry an old block unchanged or assume a larger declared window guarantees every response.

## Choose the next query

For a complete workload checklist and self-tests, start with [How to choose an RPC provider](https://docs.blockvectra.com/en/guides/choose-rpc-provider/).

When choosing a provider for repeated contract reads, [compare daily and cycle budgets for EVM reads](https://docs.blockvectra.com/en/guides/infura-alternative/). Check the required historical blocks first, then plan the task's daily distribution and throughput; fitting a credit budget does not establish state coverage.

When comparing providers for historical reads, first confirm that both can serve the target block. The [full-request overage comparison](https://docs.blockvectra.com/en/guides/chainstack-alternative/) compares extra RU prices with method-based costs, separates included quota from extra usage, and explains full versus archive billing classes.

For earlier indexed blocks, transactions, transfers or other datasets, check the table's declared Data API datasets and the [Data API reference](https://docs.blockvectra.com/en/api/data/). Indexed records do not provide arbitrary historical contract execution or imply that every chain has historical balances.

* [eth\_call method reference](https://docs.blockvectra.com/en/api/json-rpc/methods/eth_call/) for call parameters and return encoding.
* [eth\_getLogs block range and chunked queries](https://docs.blockvectra.com/en/guides/getlogs-block-range/) for event-log history.
* [Wallet custom RPC setup](https://docs.blockvectra.com/en/guides/wallet-custom-rpc/) for wallet connections and dedicated keys.
* [Supported chains](https://docs.blockvectra.com/en/chains/) for network availability and [CU pricing](https://docs.blockvectra.com/en/guides/reading-cu-pricing/) for method costs.
