Skip to content
New guideAgent Cookbook 12 copy-ready prompts for AI coding agents.See recipes
All blog posts
What Is an Archive Node? Full Node vs Archive Node, Which RPC Calls Need Archive State, and When a Full Node Is Enough

What Is an Archive Node? Full Node vs Archive Node, Which RPC Calls Need Archive State, and When a Full Node Is Enough

By Ben Chatwin14min read

You call eth_getBalance for a wallet at a block from 2023 and get this back:

JSON
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"historical state f48ab66c3b39fcf8415ba3f787a529900795b307337fc244949eed20f10de6dc is not available"}}

The block exists. The node returns it, with all 564 transactions, when you ask for it. What the node no longer has is the state at that block: the balances and contract storage as they were then. That is the difference between a full node and an archive node, and it decides which of your RPC calls work.

We ran the same requests against a Dwellir full endpoint and a Dwellir archive endpoint to find which calls need archive state. Every error and response quoted below came from those endpoints on 11 November 2026.

What is an archive node?

An archive node keeps the complete state of the chain at every block since genesis. That includes every account balance, nonce, contract code and storage slot, as it was at each height. It prunes nothing.

A full node validates every block and keeps the chain's history, but it keeps state only for a short window behind the head. The node deletes older state to save disk. Geth's documentation says: "Only the most recent 128 block states are stored in a full node."

Two terms explain the difference:

  • History is what happened: blocks, transactions, receipts and event logs. Full nodes and archive nodes both keep it on most clients.
  • State is what the chain looked like at a point in time: who held what, and what each contract's storage contained. Only an archive node keeps it for old blocks.

So "can a full node answer questions about 2023?" depends on the question. "Which transactions were in block 50,000,000?" works. "What was this contract's totalSupply() at block 50,000,000?" does not.

Full nodeArchive node
Blocks, transactions, receipts, logsKept (most clients)Kept
State at the latest blocksYesYes
State at old blocksNo, pruned after a short windowYes, back to genesis
Debug traces of old transactionsNoYes
Historical eth_getProofNoDepends on the client and its settings
Ethereum disk (Erigon docs)535 GB (keeps only 36 days of blocks)About 2.7 TB
Polygon disk (Dwellir fleet)7.8 TB13.8 TB
Bars from genesis to the chain head comparing what a full node and an archive node keep. Both keep blocks, transactions, receipts and logs from genesis. A full node keeps state only for the last few blocks. That is 128 blocks, about 3 minutes, on Dwellir's Polygon full endpoint, and about 6,200 blocks, about 30 minutes, on Dwellir's Arbitrum full endpoint. Erigon's full mode keeps 262,144 blocks, about 36 days. At block 50,000,000 the Polygon full node returned historical state not available and the archive returned the balance 0x68fd1eeaa3387b01cb7f29. An archive node keeps state at every block back to genesis.

How much state does a full node keep?

The window depends on the client, so "full node" does not mean the same thing on every chain. We measured it by calling eth_getBalance at increasing depths behind the head until the call failed.

EndpointClientState windowIn time
Polygon fullBor v2.11.0 (a Geth fork)128 blocks, measuredAbout 3 minutes at the 1.5 s average block time we measured
Arbitrum One fullNitro v3.12.2About 6,200 blocks, measuredAbout 30 minutes (6,186 blocks spanned 1,798 s)
Ethereum, Erigon full modeErigon 3262,144 blocks, per Erigon's pruning docsAbout 36 days

On Polygon the boundary was exact. In five runs in a row, the call answered at 128 blocks behind the head and failed from 129 or 130. The head moved by a block between requests. That matches Geth's 128-block default, which Bor inherits.

Erigon is the outlier in both directions. Its default full mode keeps 36 days of state, far longer than Geth, but it also drops block data older than that window. If you need old blocks and receipts from an Erigon node, it has to run in blocks or archive mode.

Which RPC calls need an archive node? We tested them

We sent identical requests to two Polygon endpoints on 11 November 2026. Both run Bor v2.11.0. One is a full node and one is an archive node:

  • Full: https://api-polygon-mainnet-full.n.dwellir.com/YOUR_API_KEY
  • Archive: https://api-polygon-mainnet.n.dwellir.com/YOUR_API_KEY

The target was block 50,000,000 (16 November 2023), about 47 million blocks behind the head, and the WPOL (formerly WMATIC) contract at 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270.

Call at block 50,000,000Full nodeArchive node
eth_getBalanceError: historical state not available0x68fd1eeaa3387b01cb7f29
eth_call (totalSupply())Error: historical state not available0x...68fd1eeaa3387b01cb7f29
eth_getStorageAt (slot 2)Error: historical state not available0x...12 (18 decimals)
eth_getCodeError: historical state not availableContract bytecode
debug_traceTransactionError: historical state is not availableCall trace
debug_traceBlockByNumberError: historical state is not availableTraces for the block
eth_getBlockByNumberBlock with 564 transactionsSame
eth_getTransactionByHashTransactionSame
eth_getTransactionReceiptReceipt with 1 logSame
eth_getLogs (one block)877 logsSame
eth_getProofError: historical state not availableError: missing trie node

Every call that reads state at the old block failed on the full node. Every call that reads history worked on both.

State reads: eth_getBalance, eth_call, eth_getStorageAt, eth_getCode

These four take a block parameter and look up state at that block. Here is eth_call reading totalSupply():

Bash
curl -s https://api-polygon-mainnet-full.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270","data":"0x18160ddd"},"0x2FAF080"]}'

Full node:

JSON
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"historical state f48ab66c3b39fcf8415ba3f787a529900795b307337fc244949eed20f10de6dc is not available"}}

Archive node, same request:

JSON
{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000068fd1eeaa3387b01cb7f29"}

The hash in the error is the state root of block 50,000,000. This one hash commits to every balance and storage slot at that block. The full node knows which state you asked for. It no longer stores it. eth_getTransactionCount and eth_estimateGas at an old block fail the same way, because they read the same state.

Traces: debug_traceTransaction and debug_traceBlockByNumber

A trace re-executes a transaction, so it needs the state from just before that transaction. On the full node, a trace of the first transaction in block 50,000,000 returned:

JSON
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"historical state is not available"}}

The archive node returned the full call tree, starting {"from":"0x2c61d22af7b615d7d41def680b6edac29076709d","gas":"0xf424e","gasUsed":"0x104e3",...}.

Traces are not archive-only by nature. The full node traced a block 10 behind the head and returned 91 transaction traces, then failed at 300 behind the head. A trace needs archive only when the transaction is older than the full node's state window. For production tracing patterns, see debug_traceTransaction in production.

trace_block failed on both endpoints with "the method trace_block does not exist/is not available". Bor does not ship the trace_ namespace. Which trace methods exist is a client question, separate from full versus archive.

History reads that work on a full node

eth_getBlockByNumber, eth_getTransactionByHash, eth_getTransactionReceipt and eth_getLogs all returned identical results from both endpoints at block 50,000,000. Blocks, receipts and logs are stored data. The node does not re-execute anything to serve them.

So historical log scans do not require an archive node. Block range caps and provider pricing limit large eth_getLogs backfills. eth_getLogs archive cost traps covers both.

The same test on Arbitrum

We repeated the test on Arbitrum One at block 100,000,000 (11 June 2023), using the WETH contract at 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1. Both endpoints run Nitro v3.12.2.

The full endpoint returned a different error text for the same problem:

JSON
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"missing trie node 59324b82a5ec0686110764836df1bb80456e5b1aefa553f1536052456dd7d7b6 (path ) state 0x59324b82a5ec0686110764836df1bb80456e5b1aefa553f1536052456dd7d7b6 is not available, not found"}}

debug_traceBlockByNumber failed with "required historical state unavailable (reexec=128)". The reexec=128 part means the node tried to rebuild the old state by re-executing up to 128 blocks and gave up. The archive endpoint answered eth_getBalance and eth_call with 0x42f1b0958e1c7945551d and returned the block's traces.

Arbitrum showed one more full-node limit. eth_getBlockByNumber, eth_getBlockReceipts and eth_getLogs all worked at block 100,000,000, but eth_getTransactionByHash and eth_getTransactionReceipt returned null for a transaction in that block. The full node's index from transaction hash to block covered about the last year. Lookups worked from about block 397 million (early November 2025) onward and returned null for older transactions. The exact first block moved between our runs, so treat it as approximate. The archive endpoint found the transaction. Here null means the transaction is missing from this node's index. The transaction can still exist, so look it up by block number when you know the block.

Error messages that mean you need archive state

The wording depends on the client. All of these came from the tests above. On state reads and traces, they mean the node pruned the state you asked for:

ErrorSeen on
historical state <root> is not availablePolygon full (Bor), state reads
historical state is not availablePolygon full (Bor), traces
missing trie node <root> (path ) state <root> is not available, not foundArbitrum full (Nitro), state reads
required historical state unavailable (reexec=128)Arbitrum full (Nitro), traces

Retrying will not help. The same request at a block inside the state window works, and the same request to an archive endpoint works.

One exception: archive nodes return these same strings for historical eth_getProof, which they may not support (see the next section). If you get one of these errors from an endpoint you expected to be archive, call eth_getBalance at the same block first. If that works, the archive is fine and the method is the problem. If it fails too, report the block number to your provider.

What an archive node does not guarantee

Archive means "keeps state at every block". It does not mean every method works at every block.

Historical proofs. eth_getProof failed on both archive endpoints at old blocks. On Polygon it worked 20 blocks behind the head and returned missing trie node at 1,000 behind the head and at block 50,000,000. On Arbitrum it returned historical state ... is not available at block 100,000,000 while eth_getBalance at the same block worked. Both archives behaved like Geth's path-based archive. Geth's archive docs explain the design. A path-based archive keeps the old value of every account and storage slot. That is enough for eth_getBalance and eth_call. A proof also needs the trie nodes that link each value to the state root. Geth keeps those only with --history.trienode. A hash-based archive supports proofs at every block, but Geth says its database can exceed 20 TB on Ethereum mainnet. If your application verifies state with Merkle proofs, read eth_getProof in production and test at your oldest block.

Every trace method. As above, trace_* depends on the client, not the node type.

Genesis coverage. Some archives start from a snapshot or a migration point. Polygon's archive answered at block 1: the native token contract at 0x0000000000000000000000000000000000001010 had a balance of 0x204fcce2c5a141f7f9a00000, about 10 billion MATIC. Test the oldest block you need.

Dwellir's archive nodes docs say the same: test the specific method at the oldest block your application needs.

How big is an archive node?

Archive state costs disk, and the amount depends on the client as much as on the chain. For what these sizes cost to run, see The Hidden Costs of Archive Nodes.

Chain and clientFull nodeArchive nodeSource
Ethereum, Erigon 3535 GB2.25 TB datadir, about 2.7 TB with the receipts cache Erigon 3.7 enables by defaultErigon docs, measured 17 Sep 2026
Ethereum, GethAbout 2 TB path-based, about 6.5 TB with historical trie data, over 20 TB hash-basedGeth docs
Polygon, Bor (execution) and Heimdall (consensus)7.8 TB13.8 TBDwellir fleet, 3 Nov 2026
Base, op-rethNot measured9.3 TBDwellir fleet, 3 Nov 2026
Arbitrum One, Nitro (path-based)4.4 TB, plus 0.7 to 1.4 TB for the Arbitrum Classic nodeDwellir nodes, 6 Nov 2026
Optimism, op-reth3.2 TBDwellir fleet, 3 Nov 2026

On Ethereum, Erigon's archive is about 2.7 TB and its full mode is 535 GB. That full mode also drops blocks older than 36 days, so not all of the gap is state. A hash-based Geth archive of the same chain can exceed 20 TB. Polygon gives a cleaner comparison because both nodes run Bor and keep every block. The full node is 7.8 TB, and archive state adds about 6 TB on top.

When a full node is enough

A full node covers you when every state read uses latest or a block from the last few minutes. That includes:

  • Wallets showing current balances and nonces
  • Sending transactions, eth_estimateGas and gas price lookups
  • Trading bots and MEV searchers reading current pool state
  • Indexers that follow the head and build their data from events with eth_getLogs
  • Looking up old blocks, transactions by block number, receipts and logs

When you need an archive node

You need archive state when you read or re-execute state at a past block:

  • Historical balances for tax, accounting or portfolio history
  • Contract state at past blocks: TVL over time, pool reserves, oracle prices, governance snapshots
  • Debugging or tracing transactions older than a few minutes (debug_trace*, trace_*)
  • Backfilling an indexer that reads contract storage or calls view functions at each block
  • Simulating a call against the state at an old block with eth_call

If you are not sure, run your own queries against both endpoint types at the oldest block you need.

A quick check for any endpoint

This script asks an endpoint for a balance just inside a Geth-style state window and far outside it. A full node answers the first and errors on the second. An archive node answers both.

Bash
#!/usr/bin/env bash
# usage: ./is-archive.sh https://api-polygon-mainnet.n.dwellir.com/YOUR_API_KEY
URL="$1"
ADDR=0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270   # any account or contract on the chain

rpc() { curl -s "$URL" -H 'Content-Type: application/json' -d "$1"; }

HEAD=$(rpc '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' | jq -r .result)
for DEPTH in 64 1000000; do
  BLOCK=$(printf '0x%x' $(( HEAD - DEPTH )))
  echo -n "balance $DEPTH blocks back: "
  rpc "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_getBalance\",\"params\":[\"$ADDR\",\"$BLOCK\"]}" | jq -c '.result // .error.message'
done

On the Polygon full endpoint it printed a balance for 64 blocks back and "historical state ... is not available" for 1,000,000 back. On the archive endpoint it printed two balances. The script needs curl and jq. The second depth must be older than the full node's state window and newer than genesis. A depth of 1,000,000 works for Polygon, Arbitrum and an Erigon full node. On a client that keeps fewer than 64 blocks of state, lower the first depth. Swap the address for one that exists on the chain you test.

Archive and full endpoints on Dwellir

Dwellir lists the node type for every endpoint in the supported chains catalog and in the CLI (dwellir endpoints search polygon). On 11 November 2026 the CLI listed 40 EVM mainnet endpoints as archive and 8 as full. Some chains have both, such as api-polygon-mainnet (archive) and api-polygon-mainnet-full, or api-arbitrum-mainnet-archive and api-arbitrum-mainnet-full.

Dwellir bills archive calls and traces like any other call. Each response is 1 API credit, with no compute-unit multiplier for historical state or debug_* methods. See archive nodes and tracing in the docs, or create a free account and run the script above against both Polygon endpoints. If you need archive state on a chain where the catalog shows only a full node, contact the team about a dedicated cluster.

Start free.1 response = 1 credit.

100,000 responses a day at 20 per second, on 150+ networks. Upgrade when you need more.