ShredStream is the fastest way to read Solana: instead of waiting for a block to be assembled, propagated, and served over RPC, you ingest the raw shreds — the packets validators exchange while the block is still being produced. Done right, your system sees transactions milliseconds before anything downstream of an RPC blockSubscribe does.
This guide takes you from zero to a working shred feed. If you want the conceptual background first, read What Are Solana Shreds? — this post is purely practical.
Step 1: Pick your delivery mode
AllenHark ShredStream ships in two flavors, and the right one depends on how much latency you're actually paying for elsewhere:
| Shared gRPC | Direct UDP | |
|---|---|---|
| How it works | We verify and parse shreds, stream structured entries to you over gRPC | Raw UDP packets forwarded from our validator nodes straight to your server's IP |
| Latency | Real-time + microseconds of parsing | Fastest possible — zero-copy forwarding |
| Integration effort | Low — a gRPC subscription | Higher — you parse shreds yourself |
| Best for | Monitoring, standard bots, testing the edge | HFT, snipers, systems where every microsecond is P&L |
| Pricing | $10/day · $59/week · $199/month | $199/month · dedicated cluster 5 SOL/month |
Two rules of thumb. First, start with shared gRPC — the daily pass exists precisely so you can test the edge cheaply before committing. Second, direct UDP only pays off if your server is network-close to the source: if you're streaming across an ocean, the transit time swamps the parsing time you saved. That's what Frankfurt co-location is for.
Step 2: Get an API key
- Log in to the AllenHark Console
- Open ShredStream and choose a pass (daily/weekly/monthly)
- Copy your API key — it's sent as an
x-api-keyheader on every connection
Step 3: Connect (shared gRPC)
The shared feed speaks the standard shredstream-proxy protocol on 84.32.223.83:9090.
Rust
use tonic::{Request, transport::Channel, metadata::MetadataValue};
use shredstream_proxy_client::ShredstreamProxyClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let channel = Channel::from_static("http://84.32.223.83:9090")
.connect()
.await?;
let mut client = ShredstreamProxyClient::new(channel);
let mut request = Request::new(SubscribeEntriesRequest {});
let api_key = MetadataValue::from_str("YOUR_API_KEY")?;
request.metadata_mut().insert("x-api-key", api_key);
let mut stream = client.subscribe_entries(request).await?.into_inner();
while let Some(entry) = stream.message().await? {
println!("slot={} entries={} bytes", entry.slot, entry.data.len());
// entry.data contains serialized Entry structs: deserialize with
// bincode into Vec<solana_entry::entry::Entry> to get transactions.
}
Ok(())
}
TypeScript
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
// shredstream.proto is published at https://allenhark.com/shredstream.proto
const def = protoLoader.loadSync("shredstream.proto");
const proto = grpc.loadPackageDefinition(def) as any;
const client = new proto.shredstream.ShredstreamProxy(
"84.32.223.83:9090",
grpc.credentials.createInsecure()
);
const metadata = new grpc.Metadata();
metadata.set("x-api-key", process.env.ALLENHARK_API_KEY!);
const stream = client.SubscribeEntries({}, metadata);
stream.on("data", (entry: { slot: number; data: Buffer }) => {
console.log(`slot=${entry.slot} ${entry.data.length} bytes`);
});
stream.on("error", (err: Error) => {
console.error("stream error, reconnecting:", err.message);
// production systems: reconnect with backoff here
});
The proto file is served from allenhark.com/shredstream.proto.
Step 4: Parse entries into transactions
Each message's data field is a bincode-serialized vector of Solana Entry structs. In Rust:
let entries: Vec<solana_entry::entry::Entry> =
bincode::deserialize(&entry.data)?;
for e in entries {
for tx in e.transactions {
// VersionedTransaction — filter by program ID, account keys, etc.
}
}
Filter as early as possible. A busy slot carries thousands of transactions; if you only care about one program (a DEX, pump.fun, a specific pool), drop everything else before it touches your strategy code. For a worked example of program-level filtering for launch sniping, see Best Shreds Setup for Pump.fun.
Step 5 (optional): Go direct UDP
When you've validated the edge and want the parsing time back:
- Provision a server with a static IP — ideally in the same facility as the source (Frankfurt bare metal is the canonical choice)
- Give us the IP; we whitelist it and forward raw UDP shreds to your port
- Ingest with the jito-shredstream-proxy tooling or your own deshredder
You take over verification and reassembly — that's the deal: UDP gives you speed, you supply the reliability.
Common pitfalls
- Streaming across the Atlantic and wondering where the edge went. Shreds beat RPC by milliseconds; a 90ms transit eats that many times over. Co-locate or pick the nearest region.
- No reconnect logic. gRPC streams drop — network blips, deploys. Reconnect with exponential backoff and expect to miss a slot or two; design the strategy to tolerate it.
- Parsing everything. Deserialize, filter by program ID, then process. The other order melts CPUs on busy slots.
- Treating shreds as confirmed data. You're seeing block production, ahead of consensus. A slot can be skipped. Act on shreds, confirm on commitment levels.
Where ShredStream fits in the stack
Shreds are the read side. If the goal is end-to-end speed you also need the write side: Slipstream for 0-slot transaction landing and Jito bundles when execution must be atomic. The full comparison of read paths — shreds vs gRPC vs RPC vs WebSockets — covers when each is the right tool.
Questions, or want the dedicated cluster? Join the Discord or head to the console and start with a $10 day pass.