diff --git a/content/api-reference/yellowstone/historical-replay.mdx b/content/api-reference/yellowstone/historical-replay.mdx
new file mode 100644
index 000000000..8119329ec
--- /dev/null
+++ b/content/api-reference/yellowstone/historical-replay.mdx
@@ -0,0 +1,282 @@
+---
+title: Yellowstone gRPC Historical Replay
+description: Backfill missing Solana blockchain data with Yellowstone gRPC's historical replay
+slug: reference/yellowstone-grpc-historical-replay
+---
+
+# Historical replay
+
+
+ **Never miss a beat**: Yellowstone gRPC's historical replay lets you recover from disconnections and backfill missing data from the last \~40 minutes of Solana blockchain activity.
+
+
+## What is historical replay?
+
+Historical replay is a Yellowstone gRPC feature that lets you resume streaming from any slot within the last **6000 slots** (approximately 40 minutes of blockchain activity). This is useful for handling brief disconnections and keeping data continuous in real-time apps without opening a separate backfill pipeline.
+
+
+ **Limited time window**: Historical replay is limited to the last 6000 slots (\~40 minutes) of blockchain activity. You cannot replay data from arbitrary points further in the past.
+
+
+
+
+ Recover data lost during brief disconnections (up to \~40 minutes).
+
+
+
+ Start applications with recent context from the last few minutes.
+
+
+
+ Review recent transactions and account changes.
+
+
+
+ Use real recent data for testing and development.
+
+
+
+## How it works
+
+
+
+ Set the `from_slot` field on `SubscribeRequest` to your replay starting slot. It must fall within the last \~6000 slots.
+
+
+
+ Yellowstone gRPC delivers all matching events from your specified slot forward.
+
+
+
+ Historical data streams until you reach the current slot.
+
+
+
+ The connection transitions seamlessly into real-time streaming; no reconnect required.
+
+
+
+## Quickstart
+
+`from_slot` must fall within the last \~6000 slots (\~40 minutes), so the example below derives it dynamically by calling [`getSlot`](/docs/chains/solana/solana-api-endpoints/get-slot) on the Solana RPC and rewinding \~1000 slots (\~7 minutes). Do not hard-code an absolute slot number, since any fixed value ages out of the replay window almost immediately.
+
+```rust
+use anyhow::Result;
+use futures::{sink::SinkExt, stream::StreamExt};
+use serde_json::json;
+use std::collections::HashMap;
+use yellowstone_grpc_client::{ClientTlsConfig, GeyserGrpcClient};
+use yellowstone_grpc_proto::geyser::{
+ CommitmentLevel, SubscribeRequest, SubscribeRequestFilterTransactions,
+};
+
+async fn get_current_slot(api_key: &str) -> Result {
+ let body = json!({
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "getSlot",
+ "params": [{ "commitment": "confirmed" }],
+ });
+
+ let res = reqwest::Client::new()
+ .post(format!("https://solana-mainnet.g.alchemy.com/v2/{}", api_key))
+ .json(&body)
+ .send()
+ .await?
+ .json::()
+ .await?;
+
+ Ok(res["result"].as_u64().unwrap_or_default())
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+ let endpoint = "https://solana-mainnet.g.alchemy.com";
+ let api_key = "ALCHEMY_API_KEY"; // Replace with your Alchemy API key
+
+ let mut client = GeyserGrpcClient::build_from_shared(endpoint)?
+ .tls_config(ClientTlsConfig::new().with_native_roots())?
+ .x_token(Some(api_key))? // API key passed as X-Token header
+ .connect()
+ .await?;
+
+ let (mut tx, mut stream) = client.subscribe().await?;
+
+ // Derive from_slot from the current slot so it always sits inside the
+ // ~6000 slot replay window. Here we rewind ~1000 slots (~7 minutes);
+ // tune the offset to how much history you want to replay.
+ let current_slot = get_current_slot(api_key).await?;
+ let from_slot = current_slot.saturating_sub(1_000);
+
+ tx.send(SubscribeRequest {
+ transactions: HashMap::from([(
+ "all_transactions".to_string(),
+ SubscribeRequestFilterTransactions {
+ vote: Some(false), // Exclude vote transactions
+ failed: Some(false), // Exclude failed transactions
+ ..Default::default()
+ },
+ )]),
+ commitment: Some(CommitmentLevel::Confirmed as i32),
+ from_slot: Some(from_slot), // Start streaming from this historical slot
+ ..Default::default()
+ })
+ .await?;
+
+ while let Some(Ok(update)) = stream.next().await {
+ println!("Received update: {:?}", update);
+ }
+
+ Ok(())
+}
+```
+
+## Configuration
+
+### `from_slot`
+
+The slot number to start replaying from, as a `u64`. Must be within the replay window (last \~6000 slots from the current slot).
+
+**Type**: `optional uint64`
+
+**Example**: `current_slot - 1000` (replays roughly the last 7 minutes)
+
+**Behavior**:
+
+* If specified, streaming begins from this slot number.
+* All matching data from `from_slot` onwards is sent, then the connection transitions to real-time.
+* If not specified, streaming begins from the current slot.
+* If the requested slot is older than the \~6000 slot window, the server rejects the request.
+
+See the [Subscribe request](/docs/reference/yellowstone-grpc-subscribe-request) reference for the full `SubscribeRequest` message definition.
+
+## Use cases
+
+
+
+ When your application reconnects after a short disconnection (under \~40 minutes), historical replay makes sure no data is missed. The example below tracks the last processed slot in a `RwLock`; persist it however suits your app (Redis, Postgres, a file, etc.) so the next reconnect can pick up where you left off.
+
+ ```rust
+ use anyhow::Result;
+ use serde_json::json;
+
+ async fn get_current_slot(api_key: &str) -> Result {
+ let body = json!({
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "getSlot",
+ "params": [{ "commitment": "confirmed" }],
+ });
+
+ let res = reqwest::Client::new()
+ .post(format!("https://solana-mainnet.g.alchemy.com/v2/{}", api_key))
+ .json(&body)
+ .send()
+ .await?
+ .json::()
+ .await?;
+
+ Ok(res["result"].as_u64().unwrap_or_default())
+ }
+
+ // Load the last slot you processed from wherever you store it.
+ let mut last_processed_slot: u64 = load_from_persistent_store().unwrap_or(0);
+
+ // Check whether it's still within the replay window.
+ let current_slot = get_current_slot("ALCHEMY_API_KEY").await?;
+ let max_replay_slot = current_slot.saturating_sub(6_000);
+
+ if last_processed_slot < max_replay_slot {
+ eprintln!("Disconnection too long, some data may be lost");
+ last_processed_slot = max_replay_slot;
+ }
+
+ tx.send(SubscribeRequest {
+ // ... your subscription config
+ from_slot: Some(last_processed_slot),
+ ..Default::default()
+ })
+ .await?;
+ ```
+
+
+
+ Start your application with recent context from the last few minutes:
+
+ ```rust
+ let current_slot = get_current_slot("ALCHEMY_API_KEY").await?;
+ let start_slot = current_slot.saturating_sub(1_500); // ~10 minutes ago
+
+ tx.send(SubscribeRequest {
+ // ... your subscription config
+ from_slot: Some(start_slot),
+ ..Default::default()
+ })
+ .await?;
+ ```
+
+
+
+ Use recent historical data for testing (limited to the last \~40 minutes):
+
+ ```rust
+ use yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof;
+
+ let current_slot = get_current_slot("ALCHEMY_API_KEY").await?;
+ let test_start_slot = current_slot.saturating_sub(750); // ~5 minutes ago
+ let test_end_slot = current_slot.saturating_sub(150); // ~1 minute ago
+
+ tx.send(SubscribeRequest {
+ // ... your subscription config
+ from_slot: Some(test_start_slot),
+ ..Default::default()
+ })
+ .await?;
+
+ while let Some(Ok(update)) = stream.next().await {
+ // Extract the slot from whichever update variant is present.
+ let slot = match &update.update_oneof {
+ Some(UpdateOneof::Transaction(t)) => t.slot,
+ Some(UpdateOneof::Account(a)) => a.slot,
+ Some(UpdateOneof::Slot(s)) => s.slot,
+ _ => continue,
+ };
+
+ // Stop processing once we reach the test end slot.
+ if slot >= test_end_slot {
+ break;
+ }
+
+ // your test handler here
+ }
+ ```
+
+
+
+## Handling duplicates
+
+When you use `from_slot` for gap recovery, you may receive updates you've already processed. Use a time-bounded cache or database unique constraints to deduplicate efficiently. See [Handle duplicate updates](/docs/reference/yellowstone-grpc-best-practices#handle-duplicate-updates) in the best practices guide for more.
+
+
+ For a complete production-ready client that combines automatic reconnection, gap recovery with `from_slot`, and separate ingress and processing tasks, see the [durable client example](/docs/reference/yellowstone-grpc-examples#durable-client).
+
+
+## Next steps
+
+
+
+ Detailed reference for `SubscribeRequest` and every field it supports.
+
+
+
+ Production patterns including gap recovery and reconnection.
+
+
+
+ Complete working examples, including a durable client with gap recovery.
+
+
+
+ Make your first Yellowstone gRPC connection.
+
+
diff --git a/content/docs.yml b/content/docs.yml
index c512e3b8a..bac5c3e3c 100644
--- a/content/docs.yml
+++ b/content/docs.yml
@@ -154,6 +154,8 @@ navigation:
path: api-reference/yellowstone/overview.mdx
- page: Quickstart
path: api-reference/yellowstone/quickstart.mdx
+ - page: Historical Replay
+ path: api-reference/yellowstone/historical-replay.mdx
slug: getting-started
- section: API Reference
path: api-reference/yellowstone/api-reference-overview.mdx