Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Network & Protocol Guides12 min read

Polkadot and Substrate Archive Nodes: Querying Historical State Over RPC

Learn how archive nodes on Polkadot and Substrate chains store full historical state and how to query past storage values via state_getStorage and related RPC methods.

TL;DR

This article explains what an archive node is on Polkadot and Substrate-based chains, how it differs from a pruned full node, and how to query historical state over RPC using methods like state_getStorage and state_queryStorage. It includes a reproducible JavaScript example, common failure modes, and a decision guide for when to use archive nodes.

Direct Answer: How to Query Historical State on Polkadot

To query historical state on a Polkadot or Substrate-based chain, you need an archive node that retains the full state trie at every block. Over RPC, you first resolve the block hash for a given block number using chain_getBlockHash, then call state_getStorage with that hash to read a storage value as it existed at that block. On a pruned full node, such queries either fail or return the latest value, because historical state is not retained.

This guide walks through the Substrate storage model, the exact RPC methods, and a runnable example you can verify yourself. It also covers common pitfalls and tradeoffs, so you can decide when an archive node is necessary and how to access one.

What Is an Archive Node on Polkadot and Substrate?

Polkadot and other Substrate-based chains store their state as a key-value database exposed through the runtime. The current state is a Merkle trie, and each block produces a state root that commits to the entire state at that point. A pruned full node keeps only recent state (typically the last few hundred blocks) plus enough historical data to verify finality and serve headers. An archive node, in contrast, retains the full state trie for every block, allowing you to query the exact storage values at any historical block.

The Polkadot documentation distinguishes node types: an archive node stores all historical state, while a pruned node does not. This is a documented behavior of Substrate-based nodes, not a provider-specific feature. The tradeoff is disk space and sync time, which vary by chain and configuration; no universal figures are provided here because they depend on the chain's size and pruning settings.

For a deeper comparison of archive and full nodes in a broader blockchain context, see our archive node vs full node guide.

  • Archive node: retains full state trie for every block, enabling historical state queries.
  • Pruned full node: keeps recent state only; historical queries are limited or unavailable.
  • Substrate state is key-value storage, not just account balances; any storage item can be queried historically.

The Substrate Storage Model and RPC Methods

Substrate exposes state through a set of storage keys. Each key is a hash of a pallet prefix and a storage item name. For example, the balance of an account is stored under a key derived from the System pallet's Account storage. To read a value, you provide the storage key and optionally a block hash. The RPC methods you need are documented in the polkadot.js RPC docs and the Substrate RPC documentation.

Key methods for historical queries:

chain_getBlockHash – maps a block number to its hash. state_getStorage – reads a storage value at a given block hash (or the latest block if omitted). state_getKeys / state_getPairs – lists storage keys or key-value pairs at a specific block. state_queryStorage – queries storage changes over a range of blocks, useful for tracking when a value changed.

These methods work on any Substrate node, but historical data is only available if the node is an archive node. The Polkadot node infrastructure documentation confirms that archive nodes are required for historical state queries.

  • chain_getBlockHash – block number to hash
  • state_getStorage – read a storage value at a block hash
  • state_getKeys / state_getPairs – enumerate storage at a block
  • state_queryStorage – query storage changes over a block range

Runnable Example: Querying Historical Balance

The following JavaScript example uses the @polkadot/api library to connect to a Polkadot archive node and query the balance of a known account at a past block. You can run it with Node.js after installing the dependency. Replace the endpoint with your own archive node URL.

The example resolves block number 10,000,000 to a hash, then reads the System.Account storage for a specific account. On an archive node, you get the true historical balance; on a pruned node, you may get an error or the latest value.

To verify the result independently, you can cross-check with a block explorer that provides historical balance data, such as Polkascan or Subscan, though these are third-party services.

// Install: npm install @polkadot/api
const { ApiPromise, WsProvider } = require('@polkadot/api');

async function main() {
  // Use your own archive node endpoint
  const provider = new WsProvider('wss://rpc.polkadot.io');
  const api = await ApiPromise.create({ provider });

  // Block number to query
  const blockNumber = 10000000;
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  console.log('Block hash:', blockHash.toHex());

  // Account to query (example: a well-known address)
  const address = '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5';
  const accountKey = api.query.system.account.key(address);

  // Read storage at the historical block
  const accountInfo = await api.rpc.state.getStorage(accountKey, blockHash);
  if (accountInfo.isEmpty) {
    console.log('No data found (likely pruned node or account did not exist).');
  } else {
    const data = api.registry.createType('AccountInfo', accountInfo);
    console.log('Free balance at block', blockNumber, ':', data.data.free.toString());
  }

  await api.disconnect();
}

main().catch(console.error);

// Expected output (example):
// Block hash: 0x...
// Free balance at block 10000000 : 1234567890

Common Failures and Fixes

When querying historical state, you may encounter several issues. Here are the most common and how to resolve them.

If you get an error like State already discarded for ..., the node is pruned and does not have the historical state. The fix is to use an archive node. If you get the latest value instead of the historical one, the node may be ignoring the block hash parameter due to a misconfiguration; ensure you are passing the hash correctly and that the node is not behind a proxy that strips parameters.

Another issue is using an incorrect storage key. Storage keys are hashed; you must use the correct key format. The example above uses the API's key() method to generate the key, which is reliable. If you construct keys manually, double-check the hashing.

Finally, rate limits or timeouts can occur on public endpoints. For production use, consider a dedicated endpoint via our RPC Assistant or a managed service.

  • Error 'State already discarded' – node is pruned; switch to archive node.
  • Returns latest value instead of historical – verify block hash parameter and node configuration.
  • Invalid storage key – use API's key generation methods.
  • Rate limits/timeouts – use a dedicated or managed endpoint.

Tradeoffs and Limitations

Archive nodes are essential for certain use cases, but they come with significant costs. Disk space and sync time are much higher than pruned nodes. The exact figures vary by chain and configuration; for Polkadot, the archive node size is documented to be several times larger than a pruned node, but we do not provide specific numbers here because they change over time.

Even on an archive node, not all historical data is available. For example, storage that has been migrated or removed may not be accessible. Also, querying very old blocks can be slow because the node must traverse the trie. For complex historical analysis, you might need an external indexer or snapshot service.

When deciding whether to run your own archive node or use a managed one, consider your query frequency and latency requirements. Managed services like OnFinality's API service offer archive nodes with high availability, but you should verify their retention policies with the provider.

For more on RPC performance, see our Polkadot RPC latency guide.

  • Disk space and sync time are significantly higher for archive nodes.
  • Historical state may be unavailable for migrated or removed storage.
  • Querying very old blocks can be slow.
  • Consider external indexers for complex historical queries.

Decision Guide: When to Use an Archive Node

Use an archive node when you need to answer questions like 'What was the balance of this account at block N?' or 'What was the total issuance at that time?' These queries are common in audits, research, and building dApps that need historical context.

If you only need current state or recent history, a pruned full node is sufficient and more cost-effective. For event logs or transaction history, you might not need an archive node; an indexer like SubQuery can provide that data more efficiently.

For a quick start, you can use a public archive endpoint, but be aware of rate limits. For production, consider a dedicated endpoint through RPC pricing or a managed provider. Always test your queries against a known archive node to ensure correctness.

  • Use archive node for historical storage queries at specific blocks.
  • Use pruned node for current state and recent blocks.
  • Use indexers for event/transaction history.
  • Test queries on a known archive node before relying on them.

Next Steps and Further Reading

Now that you understand how to query historical state on Polkadot, you can explore more advanced topics. If you're new to Polkadot, start with our Polkadot network overview. For more RPC techniques, see our guides on querying historical blockchain data and Polkadot RPC endpoints.

If you're considering running your own node, review the official Polkadot documentation on node infrastructure. For managed solutions, check our API service and RPC pricing. And don't forget to explore the OnFinality Learn hub for more tutorials.

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started