minimumLedgerSlot returns the lowest slot number a Solana node currently holds in its ledger, effectively its retention floor. By pairing it with getSlot (the current tip) and getFirstAvailableBlock, you can build an availability window [minimumLedgerSlot, getSlot] and detect nodes that look healthy but have a shallow floor. Indexers and backfill jobs should pre-flight each endpoint: call minimumLedgerSlot, compare it against the target block range, and refuse or route the query when the range predates the floor. This article explains the mechanism, shows runnable Node.js probes, and provides a reproducible comparison table for your own endpoints. It also covers the honest limitations: load balancers may route to nodes with different floors, retention is operator-configurable, and a deep floor does not guarantee every intermediate slot is complete.
The Retention Floor and Why It Matters
Every Solana RPC node keeps a ledger of recent blocks, but the oldest slot it retains is an operator configuration, not a protocol constant. The minimumLedgerSlot JSON-RPC method returns the lowest slot number the node currently holds in its ledger. That value is the node's retention floor: any request for a slot below it will fail rather than return data.
This matters because a node can appear perfectly healthy on generic health checks while having a shallow floor. It answers getSlot and getHealth instantly, but a historical getBlock for a slot from last week may return an error. The Monitoring RPC endpoints guide covers generic health metrics; minimumLedgerSlot adds the data-availability dimension that generic checks miss.
The method is documented in the Solana minimumLedgerSlot reference. It takes no parameters and returns a single slot number. Treat it as a capability probe you run before issuing any historical read, not as a metric you scrape once and cache forever.
- A node that prunes aggressively returns a recent floor, often only a few thousand slots behind the tip.
- An archive or long-retention node returns a much older floor, potentially millions of slots behind.
- The floor is a property of the specific node you reached, not of the provider's brand or the endpoint URL.
Ledger Retention Versus State Availability
Ledger retention answers the question: which block data does this node keep? State availability answers a different question: which account and state snapshots can this node serve? minimumLedgerSlot speaks to the former. It tells you the oldest block the node can return via getBlock, getBlockTime, or getTransaction for transactions in that block.
A node can retain a deep ledger but still be unable to serve an arbitrary historical account state query, because state queries depend on snapshots and index configuration. Conversely, a node with a shallow ledger floor may still serve recent state queries fine. Keeping these two concepts separate prevents the common mistake of assuming a deep minimumLedgerSlot guarantees every historical method will work.
For a broader treatment of which methods serve historical data and how they differ, see Querying Solana historical data over RPC. That article complements this one: it explains the method surface, while this article explains how to verify a specific node's floor before you rely on it.
Building the Availability Window with getSlot and getFirstAvailableBlock
minimumLedgerSlot alone gives you the floor. To understand the full window a node can serve, pair it with getSlot, which returns the current tip, and getFirstAvailableBlock, which returns the lowest confirmed block the node has available. The Solana getFirstAvailableBlock and getSlot references document both methods.
The availability window is conceptually [minimumLedgerSlot, getSlot]. The span, getSlot - minimumLedgerSlot, is the retention depth in slots. A node with a span of a few thousand slots is a recent-data node; a node with a span of tens of millions is an archive-class node. The exact thresholds vary by provider and are documented as operator configuration, so measure rather than assume.
getFirstAvailableBlock is useful as a cross-check. In some node configurations it may differ slightly from minimumLedgerSlot because of how the ledger store and blockstore are indexed. When they disagree, trust the more conservative (higher) floor for pre-flight decisions, because a query below either value risks failure.
- minimumLedgerSlot: lowest slot in the ledger store.
- getFirstAvailableBlock: lowest confirmed block available for block queries.
- getSlot: current tip, the upper bound of the window.
- Retention span = getSlot - minimumLedgerSlot.
Pre-Flighting Historical Queries Before They Fail
When getBlock or getTransaction is called for a slot below the node's floor, the node returns an error rather than fabricated data. That is the correct behavior: the JSON-RPC 2.0 specification defines an error object with a code and message, and the Ethereum JSON-RPC specification establishes the same convention for blockchain methods. A well-behaved client should treat that error as a signal to route elsewhere or refuse the request, not to retry blindly.
The pre-flight pattern is straightforward. Before issuing a historical read for a target slot range, call minimumLedgerSlot on the endpoint you intend to use. If the target range's lowest slot is below the floor, the endpoint cannot serve it. Either route the query to an endpoint with a deeper floor or fail fast with a clear message.
A silent fallback to a different endpoint with a deeper floor is dangerous because it hides capacity problems and can produce inconsistent results if the fallback node is on a different fork or has different data. Make the fallback explicit: log which endpoint served the query and why the primary was rejected. The Solana RPC timeouts and retries guide covers retry semantics; pre-flight checks reduce the number of retries you need in the first place.
- Call minimumLedgerSlot on the target endpoint before the historical read.
- Compare the target range's lowest slot against the floor.
- If below the floor, route to a deeper endpoint or fail with a clear error.
- Log the routing decision so silent fallbacks do not hide capacity issues.
Runnable Node.js Probe for Floor, Tip, and Span
The following Node.js script queries minimumLedgerSlot and getSlot, computes the retention span in slots, and estimates the covered time window using the slot duration. Solana's target slot time is approximately 400 milliseconds, but actual slot times vary, so treat the time estimate as approximate and label it as such in your logs.
The script uses the standard fetch API available in modern Node.js. Replace the endpoint URL with your own. The output is a single JSON object you can log or feed into a routing decision.
const ENDPOINT = 'https://your-solana-rpc-endpoint';
const SLOT_MS = 400; // approximate target slot duration
async function rpc(method, params = []) {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
});
const json = await res.json();
if (json.error) throw new Error(`${method}: ${json.error.message}`);
return json.result;
}
async function probe() {
const [floor, tip, firstBlock] = await Promise.all([
rpc('minimumLedgerSlot'),
rpc('getSlot'),
rpc('getFirstAvailableBlock')
]);
const span = tip - floor;
const spanHours = (span * SLOT_MS) / 1000 / 60 / 60;
return {
endpoint: ENDPOINT,
minimumLedgerSlot: floor,
getSlot: tip,
getFirstAvailableBlock: firstBlock,
retentionSpanSlots: span,
estimatedWindowHours: Number(spanHours.toFixed(2))
};
}
probe().then(console.log).catch(console.error);Routing Historical Reads Across Endpoints with Different Floors
In a multi-endpoint setup, run the same probe against each endpoint and load-balance historical reads only to nodes whose floor is low enough for the target range. This is a routing problem, not a health-check problem. A node can be healthy and still be ineligible for a deep historical query.
The following script probes a list of endpoints, filters to those whose floor is at or below a target slot, and returns the eligible set. It also reports the ineligible endpoints with the reason, so operators can see which nodes are shallow. This pattern is useful for indexers and backfill jobs that need to know where to send work.
For a broader view of how OnFinality structures Solana access, see the Solana network page and the Solana API guide. Those resources describe endpoint options; this script describes how to verify retention before you commit a query.
const ENDPOINTS = [
'https://endpoint-a.example',
'https://endpoint-b.example',
'https://endpoint-c.example'
];
async function rpc(endpoint, method, params = []) {
const res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
});
const json = await res.json();
if (json.error) throw new Error(`${method}: ${json.error.message}`);
return json.result;
}
async function probeAll(targetSlot) {
const results = await Promise.all(ENDPOINTS.map(async (endpoint) => {
try {
const floor = await rpc(endpoint, 'minimumLedgerSlot');
const tip = await rpc(endpoint, 'getSlot');
const eligible = floor <= targetSlot && targetSlot <= tip;
return { endpoint, floor, tip, eligible, reason: eligible ? 'ok' : 'below floor or above tip' };
} catch (err) {
return { endpoint, eligible: false, reason: err.message };
}
}));
return {
eligible: results.filter(r => r.eligible).map(r => r.endpoint),
ineligible: results.filter(r => !r.eligible)
};
}
probeAll(250000000).then(r => console.log(JSON.stringify(r, null, 2)));Reproducible Comparison Table for Your Endpoints
The only reliable way to know your endpoints' retention is to measure them. Run the probe script above against each endpoint you use, at the same time, and record the results. Because retention is operator-configurable and can change, re-run the probe periodically and treat the table as a snapshot, not a permanent fact.
Fill in the table below with your own measurements. Do not rely on provider marketing claims for retention depth; measure the specific endpoint you actually reach. If your endpoint is behind a load balancer, run the probe multiple times and record the range of floors you observe.
- Endpoint URL: the exact URL you call.
- minimumLedgerSlot: the floor returned by the probe.
- getSlot: the tip returned at the same time.
- Retention span (slots): tip minus floor.
- Estimated window (hours): span times slot duration, labeled approximate.
- Observed floor range across repeated probes: min and max, to detect load-balancer variance.
Limitations, Tradeoffs, and Honest Caveats
minimumLedgerSlot reflects the node you actually reached. Behind a load balancer, different requests may hit nodes with different floors. A probe that returns a deep floor does not guarantee the next request will hit the same node. Run the probe repeatedly and record the range, or use a session-affinity mechanism if your provider offers one.
Retention is an operator configuration that can change at any time. A node that served a deep range yesterday may prune today. Do not cache a floor value indefinitely; re-probe before large backfill jobs and periodically during long-running ones.
A deep floor does not guarantee that every intermediate slot is complete. Skipped slots still exist, and a node may have gaps in its ledger even within the retained range. Pair retention checks with gap detection, as described in Solana getBlocks and skipped-slot gap detection. Retention tells you the outer bounds; gap detection tells you whether the interior is solid.
Finally, minimumLedgerSlot is a single number and does not describe state availability. A node may retain blocks but not serve arbitrary historical account state. Treat it as one signal among several, not a complete capability description.
- Load balancers can route to nodes with different floors.
- Retention is operator-configurable and can change without notice.
- A deep floor does not imply a gap-free ledger.
- Ledger retention is not the same as state availability.
Troubleshooting Common Retention Probe Failures
If minimumLedgerSlot returns an error, the endpoint may not support the method or may be temporarily unavailable. Check the error code and message against the JSON-RPC 2.0 specification. A method-not-found error suggests the endpoint is not a full Solana RPC node or is behind a proxy that filters methods.
If the probe succeeds but historical reads still fail, verify that the target slot is within [minimumLedgerSlot, getSlot] and that the slot is not skipped. A slot can be within the window but have no block if it was skipped. Use getBlocks to check for gaps in the range before issuing block queries.
If different probes return wildly different floors, you are likely behind a load balancer with heterogeneous nodes. Either accept the variance and route conservatively using the highest observed floor, or request a dedicated endpoint with stable retention. The API service page describes dedicated options.
If the probe is slow or times out, the endpoint may be under load. The Solana RPC timeouts and retries guide covers timeout handling. A slow probe is itself a signal about endpoint health.
- Method-not-found: endpoint may not be a full RPC node.
- Read fails within window: check for skipped slots with getBlocks.
- Varying floors: load balancer with heterogeneous nodes.
- Slow probe: endpoint under load, treat as a health signal.
Next Steps for Reliable Historical Reads
Start by running the probe script against every Solana endpoint you use. Record the results in the comparison table and re-run periodically. This gives you a measured baseline for routing decisions.
Then integrate the pre-flight check into your indexer or backfill job. Before issuing a historical read, call minimumLedgerSlot and compare it against the target range. Route to an eligible endpoint or fail fast with a clear error. Log the routing decision so silent fallbacks do not hide capacity problems.
Finally, pair retention checks with gap detection and timeout handling. Retention tells you the outer bounds; gap detection tells you whether the interior is complete; timeout handling keeps the job resilient. Together, these three checks form a robust pre-flight routine for any historical Solana workload.
For more context on Solana access patterns and pricing, see the OnFinality Learn hub, the RPC pricing page, and the Solana API guide. These resources help you choose endpoints and understand the cost model for historical queries.
- Probe every endpoint and record floors in a table.
- Integrate pre-flight checks into indexers and backfill jobs.
- Pair retention checks with gap detection and timeout handling.
- Review pricing and endpoint options for historical workloads.