Solana gRPC Streaming API

AllenHark provides high-performance Solana gRPC streaming powered by the Yellowstone Geyser plugin. Stream real-time account updates, transactions, blocks, and slot notifications directly from the validator with sub-millisecond latency — significantly faster than WebSocket or polling-based alternatives.

Why gRPC Over WebSocket

FeatureAllenHark gRPCStandard WebSocket
ProtocolHTTP/2 multiplexed streamsSingle TCP connection
LatencySub-millisecond (Geyser plugin)50–500 ms (RPC polling)
FilteringServer-side (only matching data sent)Client-side (all data sent, filter locally)
ThroughputMillions of updates/secLimited by connection
BackpressureBuilt-in flow controlManual handling
Data SourceDirect from validator (Geyser)Processed via RPC layer

gRPC is the preferred protocol for HFT bots, MEV searchers, arbitrage systems, snipers, real-time analytics, and any application where latency is critical.

Getting Started

1. Get Your Endpoint

Join our Discord to request gRPC access. Your server IP will be whitelisted for direct connection — no API keys needed.

2. Install Dependencies

Node.js

1npm install @grpc/grpc-js @grpc/proto-loader

Rust

Add to Cargo.toml:

1[dependencies]
2yellowstone-grpc-client = "6.0.0"
3yellowstone-grpc-proto = "6.0.0"
4tokio = { version = "1.0", features = ["full"] }
5tonic = "0.12"
6futures = "0.3"

3. Get the Proto File

Download the Yellowstone Geyser proto definition:

1curl -o geyser.proto https://raw.githubusercontent.com/rpcpool/yellowstone-grpc/master/yellowstone-grpc-proto/proto/geyser.proto

4. Connect and Subscribe

Node.js

1const grpc = require('@grpc/grpc-js');
2const protoLoader = require('@grpc/proto-loader');
3
4const packageDef = protoLoader.loadSync('geyser.proto', {
5  keepCase: true, longs: String, enums: String, defaults: true, oneofs: true
6});
7const proto = grpc.loadPackageDefinition(packageDef).geyser;
8const client = new proto.Geyser('[IP_ADDRESS]:[PORT]', grpc.credentials.createInsecure());
9
10const stream = client.subscribe(new grpc.Metadata());
11
12stream.on('data', (update) => {
13  if (update.account) {
14    console.log('Account update:', update.account.account.pubkey);
15  } else if (update.transaction) {
16    console.log('Transaction:', update.transaction.transaction.signature);
17  } else if (update.slot) {
18    console.log('Slot:', update.slot.slot);
19  }
20});
21
22stream.on('error', (err) => {
23  console.error('Stream error:', err.message);
24  // Implement reconnection logic here
25});
26
27// Subscribe to all slot updates and Raydium program accounts
28stream.write({
29  accounts: {
30    raydium: {
31      account: [],
32      owner: ['675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'],
33      filters: []
34    }
35  },
36  slots: { slots: {} },
37  transactions: {},
38  blocks: {},
39  blocksMeta: {},
40  commitment: 1, // Processed
41  entry: {},
42  accountsDataSlice: [],
43  ping: null
44});

Rust

1use yellowstone_grpc_client::GeyserGrpcClient;
2use yellowstone_grpc_proto::prelude::*;
3use std::collections::HashMap;
4use futures::StreamExt;
5
6#[tokio::main]
7async fn main() -> anyhow::Result<()> {
8    let mut client = GeyserGrpcClient::build_from_uri("http://[IP_ADDRESS]:[PORT]")
9        .connect()
10        .await?;
11
12    // Subscribe to Raydium program accounts and all slots
13    let mut accounts = HashMap::new();
14    accounts.insert("raydium".to_string(), SubscribeRequestFilterAccounts {
15        account: vec![],
16        owner: vec!["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8".to_string()],
17        filters: vec![],
18        nonempty_txn_signature: None,
19    });
20
21    let mut slots = HashMap::new();
22    slots.insert("slots".to_string(), SubscribeRequestFilterSlots {
23        filter_by_commitment: None,
24        interleave_updates: None,
25    });
26
27    let request = SubscribeRequest {
28        accounts,
29        slots,
30        transactions: HashMap::new(),
31        blocks: HashMap::new(),
32        blocks_meta: HashMap::new(),
33        commitment: Some(CommitmentLevel::Processed as i32),
34        entry: HashMap::new(),
35        accounts_data_slice: vec![],
36        ping: None,
37        from_slot: None,
38        transactions_status: HashMap::new(),
39    };
40
41    let (_, mut stream) = client.subscribe_with_request(Some(request)).await?;
42
43    while let Some(msg) = stream.next().await {
44        match msg?.update_oneof {
45            Some(UpdateOneof::Account(account)) => {
46                println!("Account update: {:?}", account);
47            }
48            Some(UpdateOneof::Slot(slot)) => {
49                println!("Slot: {}", slot.slot);
50            }
51            _ => {}
52        }
53    }
54
55    Ok(())
56}

API Reference

Unary Methods (Request/Response)

Standard request-response methods available alongside streaming:

MethodDescription
GetLatestBlockhashReturns the latest blockhash for transaction signing
GetBlockHeightReturns the current block height
GetSlotReturns the current slot
GetBlockReturns full block data for a given slot
GetTransactionReturns transaction details by signature
GetVersionReturns the Geyser plugin version
GetGenesisHashReturns the genesis hash
GetFeeForMessageReturns the fee for a given message
IsBlockhashValidChecks if a blockhash is still valid
SubscribeOpens a bidirectional streaming subscription

Subscription Filters

The Subscribe method accepts filters for different data types. Each filter type streams specific on-chain data in real-time:

SubscriptionDescriptionCommon Use Case
AccountsReal-time account state changesToken balances, program state, wallet monitoring
TransactionsIndividual transactions as they're processedDEX trade monitoring, copy trading, MEV
TransactionStatusTransaction confirmation status updatesConfirmation tracking without full tx data
BlocksComplete block data including all transactionsBlock explorers, full indexing
BlockMetaBlock metadata without transaction detailsSlot monitoring, block timing
SlotsSlot progression notificationsLeader tracking, timing synchronization
EntriesLedger entry dataAdvanced analytics, validator monitoring
ProgramsProgram deployment and upgrade eventsProtocol monitoring, security alerts

Common Patterns

Monitor a DEX Pool (e.g., Raydium AMM)

1stream.write({
2  accounts: {
3    pool: {
4      account: ['POOL_ACCOUNT_PUBKEY'],
5      owner: [],
6      filters: []
7    }
8  },
9  slots: {},
10  transactions: {},
11  blocks: {},
12  blocksMeta: {},
13  commitment: 1,
14  entry: {}
15});

Track All Transactions for a Program

1stream.write({
2  accounts: {},
3  slots: {},
4  transactions: {
5    pumpfun: {
6      vote: false,
7      failed: false,
8      account_include: ['6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P'],
9      account_exclude: [],
10      account_required: []
11    }
12  },
13  blocks: {},
14  blocksMeta: {},
15  commitment: 1,
16  entry: {}
17});

Watch Multiple Wallets

1stream.write({
2  accounts: {
3    wallets: {
4      account: [
5        'WALLET_PUBKEY_1',
6        'WALLET_PUBKEY_2',
7        'WALLET_PUBKEY_3'
8      ],
9      owner: [],
10      filters: []
11    }
12  },
13  slots: {},
14  transactions: {},
15  blocks: {},
16  blocksMeta: {},
17  commitment: 1,
18  entry: {}
19});

Performance Tips

  1. Use server-side filters — only subscribe to accounts and programs you need. Every filter reduces bandwidth and processing on your end.
  2. Process updates asynchronously — use worker threads or async channels to avoid blocking the stream reader.
  3. Implement reconnection logic — gRPC streams can disconnect. Always handle errors and reconnect automatically.
  4. Use processed commitment — for lowest latency, subscribe at processed level. Use confirmed only if you need higher certainty.
  5. Batch state updates — if you receive many account updates per second, batch them before writing to storage.

Error Handling and Reconnection

1function connectWithRetry() {
2  const stream = client.subscribe(new grpc.Metadata());
3
4  stream.on('data', handleUpdate);
5
6  stream.on('error', (err) => {
7    console.error('Stream error:', err.message);
8    setTimeout(connectWithRetry, 1000); // Reconnect after 1 second
9  });
10
11  stream.on('end', () => {
12    console.log('Stream ended, reconnecting...');
13    setTimeout(connectWithRetry, 1000);
14  });
15
16  // Send subscription request
17  stream.write(subscriptionRequest);
18}
19
20connectWithRetry();

Support