gRPC Streaming
Account and transaction streaming over Yellowstone Geyser gRPC, served from a Solana validator's Geyser plugin.
Features
- Streams, not polling: updates are pushed to you as the validator processes them
- Account Streaming: Real-time account updates
- Transaction Streaming: Pre-confirmation transaction data
- Block Streaming: Full block data as it's produced
- Filtered Streams: Subscribe to specific accounts/programs
Endpoint
You connect to our gRPC endpoints utilizing IP Whitelisting instead of API keys. Once your server IP is whitelisted, you can access the endpoint directly.
The endpoint is plaintext HTTP/2 (h2c) on port 10001, in two regions. The examples below use Frankfurt.
| Region | Endpoint |
|---|---|
| Frankfurt | 88.216.36.108:10001 |
| Amsterdam | 84.32.104.38:10001 |
http://88.216.36.108:10001A connection from an address that is not on your allowlist completes the handshake, then every call returns PERMISSION_DENIED (Unauthorized IP).
Proto Definition
You will need the standard Solana Geyser proto file. Save this as geyser.proto. The endpoint serves eight RPCs: Subscribe, SubscribeReplayInfo, Ping, GetLatestBlockhash, GetBlockHeight, GetSlot, IsBlockhashValid and GetVersion. Newer upstream protos also define SubscribeDeshred and SubscribeGossip, which this endpoint does not serve. Server reflection is off, so tools like grpcurl need -proto geyser.proto.
Download Standard Geyser Proto
Installation
Node.js
npm install @grpc/grpc-js @grpc/proto-loaderRust
Add to Cargo.toml:
[dependencies]
tonic = "0.10"
prost = "0.12"
tokio = { version = "1.0", features = ["full"] }
yellowstone-grpc-proto = "1.4.0"Usage
Node.js Example
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// Load the protobuf definition
const packageDefinition = protoLoader.loadSync('geyser.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const proto = grpc.loadPackageDefinition(packageDefinition).geyser;
// Create client (Note: insecure credentials since traffic is direct to IP)
const client = new proto.Geyser('88.216.36.108:10001', grpc.credentials.createInsecure());
// Metadata (Not needed if IP is whitelisted)
const metadata = new grpc.Metadata();
// Create stream
const stream = client.subscribe(metadata);
stream.on('data', (data) => {
console.log('Update received:', data);
});
stream.on('error', (error) => {
console.error('Stream error:', error);
});
// Send subscription request
const request = {
accounts: {
'client': {
account: [],
owner: [],
filters: []
}
},
slots: {},
transactions: {},
blocks: {},
blocksMeta: {},
commitment: 'processed',
entry: {}
};
stream.write(request);Rust Example
use yellowstone_grpc_proto::geyser::geyser_client::GeyserClient;
use yellowstone_grpc_proto::geyser::{SubscribeRequest, SubscribeRequestFilterAccounts};
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Note: Use http for whitelisted connections
let endpoint = Channel::from_static("http://88.216.36.108:10001");
let channel = endpoint.connect().await?;
let mut client = GeyserClient::new(channel);
let request = SubscribeRequest {
accounts: [("client".to_string(), SubscribeRequestFilterAccounts {
account: vec![],
owner: vec![],
filters: vec![],
})].into_iter().collect(),
slots: Default::default(),
transactions: Default::default(),
blocks: Default::default(),
blocks_meta: Default::default(),
commitment: Some(0), // Processed (Confirmed = 1, Finalized = 2)
entry: Default::default(),
};
let mut stream = client.subscribe(std::iter::once(request)).await?.into_inner();
while let Some(message) = stream.message().await? {
println!("Update received: {:?}", message);
}
Ok(())
}Performance Tips
- Use Filters: Reduce bandwidth with specific filters
- Batch Processing: Process updates in batches
- One stream, many filters: each connection you buy is one open
Subscribestream, on one IP or split across several. An IP that opens more than its share getsRESOURCE_EXHAUSTED, so put every filter in one request - Backpressure Handling: Implement flow control
Error Handling
stream.on('error', (error) => {
console.error('Stream error:', error);
// Implement reconnection logic
});
stream.on('end', () => {
console.log('Stream ended, reconnecting...');
// Reconnect
});