Yellowstone gRPC Tutorial: Connect, Subscribe & Stream Solana Data (2026)

Yellowstone gRPC — also called Dragon's Mouth — is the standard protocol for real-time Solana data streaming. Built and maintained by Triton One, it hooks directly into the validator via the Geyser plugin framework and pushes account updates, transactions, and slot notifications to connected clients via gRPC (HTTP/2).
This tutorial walks you through connecting, subscribing, and streaming data using the standard open-source libraries. By the end, you'll have a working client in both Node.js and Rust.
What You'll Need
- A Yellowstone gRPC endpoint (from Subglow, QuickNode, Chainstack, or your own node)
- Node.js 18+ or Rust 1.70+
- An API key from your provider
How Yellowstone gRPC Works
When a Solana validator processes a new slot, the Geyser plugin intercepts account updates and confirmed transactions directly from validator memory. Yellowstone serializes this data into Protocol Buffers (protobuf) and streams it to connected clients over a persistent gRPC connection.
This is fundamentally different from RPC polling (you ask for data) or WebSocket (notification-based). With gRPC, data is pushed to you the instant it's confirmed — zero polling overhead, zero missed slots.
Architecture: From Validator to Your Bot
- Validator processes a slot — transactions confirm, accounts update
- Geyser plugin hooks fire — on_transaction, on_account_update callbacks capture data from validator memory
- Yellowstone serializes to protobuf — binary encoding for maximum throughput
- gRPC streams to your client — HTTP/2 multiplexed connection with built-in backpressure
Node.js: Connect with @triton-one/yellowstone-grpc
The official TypeScript/Node.js client is maintained by Triton One. Install it:
npm install @triton-one/yellowstone-grpc
Connect and subscribe to slot updates:
const Client = require("@triton-one/yellowstone-grpc").default;
const client = new Client("https://grpc.subglow.io", undefined, {
"grpc.max_receive_message_length": 64 * 1024 * 1024,
});
const stream = await client.subscribe();
const request = {
slots: { slot_sub: {} },
commitment: 1, // CONFIRMED
};
stream.write(request);
stream.on("data", (data) => {
if (data.slot) {
console.log("Slot:", data.slot.slot, "Status:", data.slot.status);
}
});
Subscribe to Transactions by Program
The real power is in filtering. Subscribe to only the transactions you care about — for example, all Pump.fun transactions:
const request = {
transactions: {
pump_filter: {
accountInclude: ["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"],
vote: false,
failed: false,
},
},
commitment: 1,
};
stream.write(request);
stream.on("data", (data) => {
if (data.transaction) {
const sig = Buffer.from(data.transaction.transaction.signature).toString("base64");
console.log("Pump.fun tx:", sig, "Slot:", data.transaction.slot);
}
});
You can add multiple named filters — for example, pump_filter and raydium_filter — on the same connection. All matching transactions stream through a single subscription.
Rust: Connect with yellowstone-grpc-client
Add to your Cargo.toml:
[dependencies]
yellowstone-grpc-client = "2"
yellowstone-grpc-proto = "2"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
Connect and subscribe:
use yellowstone_grpc_client::GeyserGrpcClient;
use yellowstone_grpc_proto::prelude::*;
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = GeyserGrpcClient::build_from_shared("https://grpc.subglow.io")?
.x_token(Some("YOUR_API_KEY".into()))
.connect()
.await?;
let (mut tx, mut stream) = client.subscribe().await?;
// Subscribe to confirmed slots
tx.send(SubscribeRequest {
slots: [("slot_sub".into(), SubscribeRequestFilterSlots { ..Default::default() })].into(),
commitment: Some(CommitmentLevel::Confirmed as i32),
..Default::default()
}).await?;
while let Some(msg) = stream.next().await {
match msg?.update_oneof {
Some(UpdateOneof::Slot(slot)) => println!("Slot: {} Status: {:?}", slot.slot, slot.status),
_ => {}
}
}
Ok(())
}
Handle Reconnection
gRPC streams can disconnect. Always wrap your subscription in a reconnection loop:
async function connectWithRetry() {
let delay = 1000;
while (true) {
try {
const client = new Client("https://grpc.subglow.io", undefined, {
"grpc.max_receive_message_length": 64 * 1024 * 1024,
});
const stream = await client.subscribe();
stream.write(subscriptionRequest);
for await (const data of stream) {
delay = 1000; // Reset backoff on success
handleEvent(data);
}
} catch (err) {
console.error("Stream error, reconnecting in", delay, "ms");
await new Promise(r => setTimeout(r, delay));
delay = Math.min(delay * 2, 30000);
}
}
}
From Raw Yellowstone to Pre-Parsed JSON
Raw Yellowstone gives you protobuf with Borsh-encoded instruction data. You must: decode protobuf, identify the program, parse the instruction discriminator, decode Borsh data, and map accounts to human-readable names.
With Subglow, the same Yellowstone endpoint delivers pre-parsed JSON with human-readable fields. Use the same @triton-one/yellowstone-grpc client — just point it at grpc.subglow.io.
Next Steps
- Yellowstone gRPC Filters Explained — deep dive on account, transaction, and slot filters
- Monitor Pump.fun with Yellowstone gRPC — program-specific tutorial
- Stream Raydium Swaps with Yellowstone gRPC — DEX monitoring guide
- TypeScript Tutorial — full type-safe streaming walkthrough
- Python Tutorial — grpcio + protobuf setup
Ready to try it?
Get your API key and start receiving filtered data in under 5 minutes. Free tier available.
Get started →