Skip to content
0%
Overview page background

Solana archival data: how to query full block and transaction history

Uttam Singh

Written by Uttam Singh

Published on July 22, 20269 min read

Solana archival data: querying full block and transaction history

Sooner or later, every team indexing Solana hits the same wall. You ask a node for a transaction from a few months back and get a "block cleaned up" error, because the node deleted that stretch of the ledger weeks ago. A standard Solana RPC node holds roughly the last couple of days of the chain and prunes everything older to stay inside its disk budget. Given that Solana produces over four petabytes of data a year at peak speeds, no single machine was ever going to hold all of it.

Archival data is how you reach everything the node threw away. Before anything else, get one definition straight. Solana archival data is old blocks and transactions, not past account state. If you're coming from Ethereum, where an archive node can report any account's balance at any historical block, that difference matters the first time you design a pipeline around a method that doesn't exist here. The rest is practical: where Solana's full history actually lives, which RPC methods reach it, how to query them, and how to backfill an index from genesis without gaps.

Why can't a standard Solana node serve full history?

A validator writes the ledger to a local database, and operators run it with the --limit-ledger-size flag so the disk doesn't fill. With the flag set, the node purges the oldest data first. Its default value is 200 million shreds, the chunks Solana splits blocks into for network propagation, which keeps the ledger under roughly 500 GB. Without the flag, the node keeps everything it receives until it runs out of disk, which at Solana's data rate is not a long wait.

Anza, the team that maintains the Agave validator client, is blunt about the reason. Six months of transaction data cannot practically be stored in a validator's local ledger, so the history a node carries is "on the order of days." Everything older has to live somewhere else.

You can find any node's floor by calling minimumLedgerSlot, which returns the oldest slot it still holds. Watch it for a while and the number only climbs, because pruning never stops. Query below it and you get the "block cleaned up" error instead of data. A bigger disk doesn't change any of this. Serving the chain tip and serving deep history are different infrastructure problems, and archival systems exist because the second one outgrew what a node can do.

What does archival mean on Solana, and how is it different from Ethereum?

An Ethereum archive node keeps every historical version of the state trie. You can ask what a contract's storage or a wallet's balance looked like at any past block, and the node answers from data it kept around for exactly that purpose. Teams arriving from Ethereum tend to assume Solana has an equivalent. It doesn't, and this one assumption wrecks more historical data plans than anything else.

Solana overwrites account state in place. When an account changes, the new version replaces the old, and a background cleaning process in AccountsDB garbage-collects superseded versions once a later slot is finalized. No record survives of what an account held three months ago. That's why the Solana RPC API has no "balance at slot N" method, and why querying historic account state has sat open as a feature request in the Solana repo for years.

What archival infrastructure preserves is the ledger itself, meaning blocks and the transactions inside them. An archive can hand you block 150,000,000, or every transaction that ever touched an address. It cannot hand you a wallet's USDC balance from last March. That question is still answerable, but you answer it by replaying the wallet's transaction history through an indexer, not by asking a node for state it never kept.

Which RPC methods need archival data?

A method becomes an archival read the moment the slot it targets falls below the node's local floor. These are the ones that reach into long-term storage.

Method
What it returns
Limit to know

getBlock

Full block and its transactions at a slot

Only maxSupportedTransactionVersion: 0 is accepted

getTransaction

A confirmed transaction by signature

Rejects processed commitment; returns null if not found

getSignaturesForAddress

Signatures that reference an address, newest first

Limit 1 to 1,000 per call; paginate with before and until

getBlocks

Confirmed slots in a range

Range capped at 500,000 slots

getBlockTime

Estimated production time of a block

Returns null if no timestamp was recorded

getFirstAvailableBlock

Lowest slot available from storage

The archive's floor, not the node's

Most historical pipelines are built on two of these. Page backward through an address's history with getSignaturesForAddress, then fetch each transaction's detail with getTransaction. Since the signature call returns at most 1,000 results per call, walking a busy address back to its first transaction means a long chain of paginated calls, and nearly every one of them past the node's minimum ledger slot is served from the archive.

This is also where a weak archive gets exposed. If the provider's long-term storage has holes, some call deep into your backfill returns a "slot skipped" or "missing in long-term storage" error, and unless you're checking for it, your index comes up short without anyone noticing. Every provider exposes the same method names. What varies is whether the storage behind them has gaps, and how fast it answers when you're paging through it thousands of calls at a time.

How do you query full block and transaction history?

The queries themselves are ordinary JSON-RPC. There is no separate archival API and no special parameter that unlocks history. You call the same methods you would use at the chain tip, and the provider's archive answers whenever the slot falls below the node's local floor.

Fetching one block from deep history looks like this:

bash
Copied
curl https://solana-mainnet.g.alchemy.com/v2/YOUR_API_KEY \ -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [150000000, { "maxSupportedTransactionVersion": 0, "transactionDetails": "full", "rewards": false }] }'

The response carries the whole block, every transaction in it, and each transaction's status and metadata. Keep maxSupportedTransactionVersion: 0 in the params on every call. Without it, the call errors on any block that contains versioned transactions, which on mainnet is most of them.

Walking an address's complete history is the two-method pattern from the table above, run in a loop. Page backward with getSignaturesForAddress until it comes back empty, then fetch each transaction's detail:

typescript
Copied
import { createSolanaRpc, address, type Signature } from "@solana/kit"; const rpc = createSolanaRpc( "https://solana-mainnet.g.alchemy.com/v2/YOUR_API_KEY" ); const target = address("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"); let before: Signature | undefined; const signatures: Signature[] = []; while (true) { const page = await rpc .getSignaturesForAddress(target, { before, limit: 1000 }) .send(); if (page.length === 0) break; signatures.push(...page.map((entry) => entry.signature)); before = page[page.length - 1].signature; } // Resolve each signature to full transaction detail const tx = await rpc .getTransaction(signatures[0], { maxSupportedTransactionVersion: 0, encoding: "jsonParsed", }) .send();

For a single wallet this loop is all you need, and it runs fine from a laptop. It stops being enough when the address is a busy program with millions of signatures, or when you need history for every address on the chain. That's the backfill problem, and the sections below cover where the data comes from and how to load it without gaps.

How is Solana's full history actually stored?

No validator holds the whole ledger, so long-term history lives off the node entirely. Two systems handle it in practice.

The long-standing one is the warehouse pattern. A dedicated node continuously uploads finalized blocks to Google Bigtable, and when an RPC node receives a query for a slot it no longer holds, it falls back to that store. Recent slots come from the node's local database and everything older comes from Bigtable. Most production Solana RPC has served deep history this way for years. The cost is concentration, since the entire chain's past ends up inside one proprietary cloud database.

The newer one is Old Faithful, the open-source archive led by Triton and the Yellowstone project. It packs every block since genesis into content-addressed CAR files, spreads them across IPFS, Filecoin, and S3-compatible storage, and serves them over standard Solana JSON-RPC and gRPC. The project exists so that full Solana history doesn't depend on any single company's database, and teams that need verifiable from-genesis coverage treat it as the reference archive.

Whichever backend sits behind your provider, the thing to internalize is that the archive is a separate system from the node. Depth and completeness are properties of that system, and they're worth asking about directly instead of assuming.

How do you get account and token history at scale?

Teams that want "Solana history at scale" rarely mean raw blocks. They want a wallet's balance over time, every transfer an address has made, or a token's full holder history, served fast enough to power a dashboard or a tax export. None of that exists in the ledger in queryable form. It has to be derived.

The recipe barely changes between projects. Pull an address's complete transaction history from archival RPC, replay those transactions in order, and compute the state you care about along the way: balances after each transaction, ownership changes, transfer flows. Write the results into your own database so the expensive replay happens once instead of on every request. On any chain this is a blockchain indexer's job. On Solana it's the only route to historical state at all, because the node keeps none.

For the sharpest version of the question, what a wallet held at a specific slot, we now ship a direct answer. getTokenAccountsByOwnerAtSlot keeps the syntax of the standard getTokenAccountsByOwner and adds a slot parameter, returning the wallet's exact token balances at that point in history in a single call. It's backed by a continuously maintained historical index rather than replay, so for point-in-time holdings the whole reconstruction pipeline above disappears. The historical Solana token balances launch post walks through the mechanics.

For the other common derived shapes, balances over time, transfers, and token metadata, Data APIs serve them pre-computed and you can skip the indexing project. Build your own indexer when you need custom derived data or full control of the pipeline. Use the managed methods when the standard shapes cover you. What cannot work is asking a plain node for past account state, because the node never kept it. Every working answer is an index built over the history.

How do you backfill an index from genesis without falling over?

The hard part of a historical pipeline is the cold start. Your index is empty, hundreds of millions of slots need to be loaded, and the chain keeps producing new blocks the whole time you're loading them. If the backfill and the live feed don't meet cleanly, a gap opens between where one ended and the other began.

Plenty of teams start by polling archival RPC for the whole backfill, and at genesis scale it gets painful: rate limits, per-request cost across hundreds of millions of slots, and no clean way to prove nothing was missed. The pattern that survives contact with production uses a different source for each phase. Bulk history comes straight from an archive, and tools like Jetstreamer stream it directly out of Old Faithful. The chain tip comes from a live stream instead of more polling.

For the live half, a Yellowstone-compatible Solana gRPC stream pushes new transactions and account updates to your indexer as they happen, filtered by account, program, or signature. The seam between backfill and stream is where pipelines usually leak, and replay is what seals it. Our gRPC lets a client reconnect with a from_slot parameter and re-receive the slots it missed while it was down, so a dropped connection doesn't leave a hole in your data. We also built the streaming layer to ride through failovers without dropping messages, which is the work that otherwise becomes a separate gap-detection service you run beside your indexer. Once the archive covers the past and the stream covers the tip, replay keeps the two sewn together.

What should you look for in a Solana archival provider?

Depth is the first thing to pin down, and it's worth asking any provider in exactly these words: do you index from genesis, or from a more recent height forward? "Full historical data" with an unstated floor is common, and you usually discover the floor when your backfill dies on it.

The rest of the checklist follows from how a backfill actually runs. Completeness matters because one gap corrupts the index you built on top of it. Speed matters because a full signature-history walk is thousands of sequential archive reads, so per-call latency multiplies into hours or days of wall-clock time. Standard JSON-RPC matters because a proprietary historical endpoint ties your pipeline to one vendor, while plain RPC needs no rewrite at all. And price matters because deep history is read-heavy by nature.

We built our archival access against that checklist. It covers complete block and transaction history from genesis over standard JSON-RPC, and our benchmarks measured historical getTransaction running up to 20x faster than other providers, alongside up to 3x faster getBlock and up to 10x faster on heavy calls like getProgramAccounts, with no code changes or proprietary methods involved. The engineering story of how we built the fastest archival methods on Solana covers the architecture behind those numbers. For a full side-by-side on depth, uptime, pricing, and tooling, the nine best Solana RPC providers decision guide covers the whole field. Whoever you pick, get the genesis-depth and completeness answers in writing before you start the backfill.

Build your Solana historical pipeline on Alchemy

A working historical pipeline needs two things, an archive deep enough to backfill from genesis and a stream fast enough to keep pace with the tip. We run both as part of Alchemy's Solana platform. Archival reads cover full block and transaction history over standard JSON-RPC, so pointing your existing Solana client at an Alchemy endpoint is the entire migration. Solana gRPC streaming is Yellowstone-compatible with replay on reconnect, priced pay-as-you-go at $75 per TB with no monthly minimum and no plan prerequisite.

Start on the free tier, with no contract and no sales call. Teams building Solana infrastructure can also apply for up to $25,000 in credits through our $20M Solana Fund. Once your pipeline is live, the history your node throws away stops being your problem.

Background gradient

Build blockchain magic

Alchemy combines the most powerful web3 developer products and tools with resources, community and legendary support.