All articles
Solana·June 29, 2026·6 min read

Jito ShredStream: Sub-Slot Market Data for Solana Bots

Jito ShredStream Solana feeds de-shredded transactions to your bot milliseconds before RPC confirmation. How to subscribe, and the forks and dedupe gotchas.

Solana's default RPC feed hands you a transaction after it's already been processed and rooted — by then the block is built, the price has moved, and your sniper is fighting for the next slot instead of the current one. Jito ShredStream closes that gap. It streams the raw shreds a validator receives over Turbine, de-shreds them into full transactions, and delivers them to your process tens of milliseconds before the same data shows up on a logsSubscribe or accountSubscribe websocket. If you're building latency-sensitive Solana bots, that window is where the edge lives.

This isn't the same thing as running a Geyser plugin or subscribing to a generic shred feed, and conflating them will cost you. Let me walk through what ShredStream actually delivers, how to wire into it, and the sharp edges that bite people the first week.

What a shred is, and why sub-slot matters

When a leader produces a block, it doesn't broadcast finished transactions. It fragments the block's entries into shreds — small erasure-coded packets — and fans them out across the network through Turbine, Solana's block-propagation tree. Every validator reconstructs the block by collecting enough shreds to reassemble the entries, then replays them.

The key timing fact: shreds hit the network while the leader is still producing the slot. A validator has partial visibility into a block's contents before that block is complete, rooted, or visible to any RPC method. ShredStream taps this stream. Jito runs a set of high-uptime nodes that receive shreds early, and the ShredStream proxy forwards them to you.

Concretely, you're looking at a lead of roughly 50–150 ms over a co-located RPC websocket, and much more over a public endpoint. On a chain with 400 ms slots, that's a meaningful fraction of a slot. For a Solana sniper bot trying to land in the same block as a new pool's first liquidity add, that head start is the difference between a fill and a chase.

ShredStream vs. Geyser vs. a raw shred subscription

People lump these together. They're distinct:

  • Geyser (Yellowstone gRPC) streams account writes, transactions, and slot status after your own validator has processed them. It's rich and structured, but it's post-processing — you're downstream of replay.
  • A raw shred feed (e.g. subscribing to your validator's shred socket directly) gives you packets, but you own the reassembly, the erasure decoding, and the deduplication. It's a lot of undifferentiated plumbing.
  • Jito ShredStream sits in between: Jito's proxy receives shreds early and de-shreds them for you, handing over reconstructed entries/transactions over a gRPC stream. You skip the Turbine-tree reconstruction and the FEC decoding.

The tradeoff is that ShredStream is a firehose of unconfirmed, possibly-forked data. You get transactions that might never make it into a rooted block. That's fine — that's the point — but your consumer has to treat everything as speculative.

Getting connected

You run the jito-shredstream-proxy locally. It authenticates to Jito's block-engine with a keypair, receives the shred stream, reconstructs it, and re-emits UDP packets (or a gRPC stream, depending on version) to a destination address you control. A minimal Docker invocation:

docker run -d --name shredstream \
  --network host \
  jitolabs/jito-shredstream-proxy:latest \
  shredstream \
  --block-engine-url https://mainnet.block-engine.jito.wtf \
  --auth-keypair /keys/auth.json \
  --desired-regions amsterdam,ny \
  --dest-ip-ports 127.0.0.1:8001

--desired-regions matters more than it looks. ShredStream latency is dominated by physical distance from Jito's receiving nodes, so pick the region your bot is co-located in and only that region. Requesting three regions you're nowhere near just adds redundant packets you'll dedupe away.

On the consumer side, you're parsing Solana Entry structures out of the reconstructed shreds. The packets are wire-format transactions — you deserialize them the same way you'd handle any VersionedTransaction, then filter for the program IDs you care about (Raydium, a pump.fun bonding curve, an Orca whirlpool). A rough consumer loop:

loop {
    let packet = socket.recv()?;                 // UDP from the proxy
    let entry: Entry = bincode::deserialize(&packet)?;
    for tx in entry.transactions {
        if touches_target_program(&tx, &RAYDIUM_V4) {
            // speculative: this tx is in-flight, not rooted
            react(tx);
        }
    }
}

The critical discipline is in that comment. You are reacting to a transaction that has not landed.

The gotchas nobody warns you about

Duplicate and out-of-order shreds. Turbine delivers redundantly and unordered. You'll see the same entry twice, and you'll see slot N+1 data before slot N is complete. Keep a rolling dedupe set keyed on slot + shred index, and don't assume monotonic ordering.

Forks and dropped blocks. Some of what you see gets orphaned. If you fire a trade off a transaction that ends up on a losing fork, you acted on data that never happened. For anything holding real risk — a MEV/arb bot sizing a backrun, or a copytrading bot mirroring a whale — you still confirm against a rooted RPC before committing capital. ShredStream tells you what's likely coming; it doesn't tell you what happened.

QUIC and SWQoS still gate your response. Seeing an opportunity 80 ms early is worthless if your own transaction can't get to the leader. ShredStream is the sensing side; the actuation side is a separate fight. If your outbound transactions are getting dropped under load, that's a connection-throttling problem — I've written up the mechanics in why Solana drops your bot's transactions under QUIC throttling, and the priority-lane angle in how stake-weighted QoS decides whose transactions land. Read both before you blame ShredStream for a missed fill.

Bandwidth and CPU. The de-shredded stream is heavy — expect sustained tens of Mbps and real deserialization cost. Run the proxy and consumer on the same box, pin the parser to dedicated cores, and don't share the NIC with your RPC node under load.

Where it's genuinely worth it

ShredStream earns its complexity when your strategy is decided in the first packets of a block: sniping the initial liquidity event, market-making where quote staleness is measured in milliseconds, or feeding an early signal into a wallet tracker that flags a target's move before it confirms. If your bot is happy reacting a full slot later, a plain Geyser feed is simpler and you should use that instead.

One more design note: keep your ShredStream ingestion, your reconstruction, and your strategy logic as separate stages with a bounded queue between them. When the firehose spikes, you want to shed load at the ingestion boundary, not stall your decision loop. This is the kind of thing we bake into the low-latency data and infrastructure layer under every bot we ship, and it's also why cutting per-transaction bytes matters — pairing ShredStream with address lookup tables to shrink your own transactions keeps the actuation side as tight as the sensing side.

If you want a shred-fed data pipeline built and tuned to your strategy, our team designs Solana trading infrastructure around exactly this kind of sub-slot signal.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#Solana#Jito#ShredStream#Low Latency#Trading Bots