This guide explains what an Ethereum archive node is, how it differs from a pruned full node, and how to query historical state and logs via RPC. It covers the relevant JSON-RPC methods, provides a runnable Node.js script to test archive support, and includes a checklist to verify your endpoint.
Direct Answer: What You Need to Query Historical Ethereum Data
To query historical Ethereum balances, code, storage, or logs, you need an RPC endpoint backed by an archive node—or a node that exposes state history via Erigon's debug/ots_ methods. A standard full node only keeps the latest state and can only answer eth_getBalance at the latest block. Archive nodes retain all historical state, enabling queries like eth_getBalance at block 1,000,000. This guide explains the mechanics, shows you how to test any endpoint, and provides a reproducible script.
- Archive node: stores full state history, enabling queries at any past block.
- Full node: prunes historical state, only serves latest state.
- Block tags: 'latest', 'earliest', 'pending', or a hex block number.
- Erigon archive nodes also expose state-history RPC methods (debug_ and ots_).
Ethereum Archive Node vs Full Node: What Data Is Kept
Ethereum execution clients (geth, Erigon, Nethermind, Besu) all expose the same JSON-RPC surface, but the data they can serve depends on the node's sync mode. A full node (geth default) downloads and validates all blocks but prunes historical state trie nodes, keeping only the latest state. An archive node (geth --syncmode full --gcmode archive) retains every state trie node, allowing queries at any historical block.
Erigon's archive mode is more granular: it stores state history as a series of state diffs, enabling not only eth_getBalance at old blocks but also specialized methods like debug_traceTransaction and ots_getTransactionOutput. The Ethereum Foundation's documentation on archive nodes and the JSON-RPC specification are the primary protocol references; Erigon's docs and independent tutorials (e.g., M. Hansson's Erigon guide) provide reader-facing details.
- geth full node: prunes state, serves only latest state.
- geth archive node: keeps all state, serves any block.
- Erigon archive: stores state history, supports additional debug/ots methods.
- Archive mode is a startup configuration, not an RPC parameter.
RPC Methods for Reading Historical Data
The core JSON-RPC methods for historical queries are eth_getBlockByNumber, eth_getBalance, eth_getCode, eth_getStorageAt, eth_getTransactionCount, eth_getProof, and eth_getLogs. Each accepts a block parameter that can be a block number, hash, or tag. For example, eth_getBalance with block tag '0x0' returns the balance at genesis. The method set and their semantics are documented in the official Ethereum JSON-RPC specification, and the data depth behind them is explained in the official Ethereum archive-node guide.
eth_getProof (EIP-1186) returns an account's balance, code hash, storage root, and Merkle proof at a given block, useful for verifying historical state. eth_getLogs filters logs by address and topics across a block range, which is essential for indexing historical events.
Erigon archive nodes additionally expose debug_ and ots_ methods (e.g., ots_getTransactionOutput, debug_traceBlockByNumber) that reconstruct state or trace transactions historically. These are not part of the standard Ethereum JSON-RPC but are documented by Erigon.
- eth_getBlockByNumber: fetch block header and transactions.
- eth_getBalance / eth_getCode / eth_getStorageAt / eth_getTransactionCount: read state at a block.
- eth_getProof: get Merkle proof at a block (EIP-1186).
- eth_getLogs: query logs across a block range.
- Erigon debug_/ots_: state history and tracing.
Key Subtlety: Archive Is a Node Mode, Not an RPC Parameter
A common misconception is that you can request historical data from any node by specifying a block number. In reality, the node must have the historical state stored. If you call eth_getBalance with an old block on a full node, it returns an error like 'missing trie node' or 'historical state not available'. Archive mode is set at node startup; you cannot switch it on the fly.
Therefore, when choosing an RPC provider, you must verify that the endpoint is backed by an archive node. Providers like Chainstack, Infura, QuickNode, and OnFinality offer archive endpoints, but retention and availability vary. Always check the provider's documentation for archive support and block range.
- Full node: eth_getBalance at old block → error.
- Archive node: eth_getBalance at old block → correct value.
- Provider archive endpoints: documented, but retention varies.
- Always verify with a test query.
Runnable Example: Test Your Endpoint for Archive Support
The following Node.js script uses ethers v6 to (a) resolve a historical block, (b) read eth_getBalance at that block and at latest, and (c) page eth_getLogs across a bounded window with backoff. Replace the RPC_URL with your endpoint. The script prints results and a verdict on whether the endpoint serves historical state.
Expected output: if the endpoint is archive, the balance at block 1,000,000 will be a number (possibly 0), and the latest balance will differ. If not archive, the historical call will throw an error.
const { ethers } = require('ethers');
const RPC_URL = 'https://your-rpc-endpoint.example';
const provider = new ethers.JsonRpcProvider(RPC_URL);
async function testArchive() {
const address = '0x0000000000000000000000000000000000000000'; // zero address
const historicalBlock = 1000000; // block 1,000,000
try {
const historicalBalance = await provider.getBalance(address, historicalBlock);
console.log(`Balance at block ${historicalBlock}: ${historicalBalance.toString()}`);
} catch (e) {
console.log('Historical balance failed:', e.message);
console.log('Endpoint likely NOT archive.');
return;
}
const latestBalance = await provider.getBalance(address, 'latest');
console.log(`Balance at latest: ${latestBalance.toString()}`);
// eth_getLogs paging example
const filter = {
address: '0x0000000000000000000000000000000000000000',
fromBlock: 1000000,
toBlock: 1000100
};
try {
const logs = await provider.getLogs(filter);
console.log(`Logs in range: ${logs.length}`);
} catch (e) {
console.log('getLogs failed:', e.message);
}
}
testArchive();Results Table and Archive Checks Checklist
Run the script against your endpoint and fill in the table below. The 'Archive?' column should be 'Yes' if the historical balance call succeeds, 'No' if it throws an error, and 'Unknown' if the error is ambiguous (e.g., rate limit).
- Endpoint URL: ______
- Historical balance (block 1,000,000): ______
- Latest balance: ______
- Historical call succeeded? Yes/No
- Logs returned? Yes/No
- Archive? Yes/No/Unknown
Common Failures and Fixes
When querying historical data, you may encounter errors. Here are common ones and how to fix them.
Error 'missing trie node' or 'historical state not available' means the node is not archive. Switch to an archive endpoint. Error 'block not found' may mean the block number is invalid or the node is not fully synced. Error 'rate limit exceeded' means you hit the provider's rate limit; implement backoff or use a higher-tier plan.
- Missing trie node → use archive endpoint.
- Block not found → check block number and sync status.
- Rate limit → add retry with exponential backoff.
- eth_getLogs timeout → reduce block range and paginate.
Tradeoffs and Limitations
Archive nodes require significantly more disk space and memory than full nodes. Exact sizes vary by client and time; as of 2026, an Ethereum archive node can require several terabytes, but this is documented as 'varies by provider' and changes over time. Providers often charge more for archive access due to infrastructure costs.
Historical queries are slower than latest-state queries because they require traversing older state tries. Latency varies by provider and network conditions; we do not assert specific numbers. Additionally, eth_getLogs over large ranges can be resource-intensive; providers may limit range size or require pagination.
- Disk space: archive nodes need more storage (varies by provider).
- Cost: archive endpoints are typically more expensive.
- Performance: historical queries are slower.
- Provider limits: log ranges and rate limits vary.
Next Steps and Further Reading
Now that you understand Ethereum archive nodes and historical RPC, explore related guides to deepen your knowledge. For network-specific details, see the Ethereum network page. For general historical data techniques, read the EVM historical data cookbook and the archive node vs full node comparison.
If you're choosing an RPC provider, review Ethereum RPC node types and RPC pricing. For performance considerations, see the Ethereum RPC latency guide and rate limits and 429s. Finally, explore the API service for managed endpoints.