Documentation index

Solana gRPC Streaming API

AllenHark provides Solana gRPC streaming powered by the Yellowstone Geyser plugin. Stream real-time account updates, transactions, blocks, and slot notifications directly from the validator's Geyser plugin, pushed to you instead of polled.

Why gRPC Over WebSocket

FeatureAllenHark gRPCStandard WebSocket
ProtocolHTTP/2 multiplexed streamsSingle TCP connection
DeliveryPushed from the validator's Geyser pluginPushed by the RPC layer, or polled
FilteringServer-side (only matching data sent)Client-side (all data sent, filter locally)
StreamsOne Subscribe stream per connection you buy, on one IP or split across several; each carries every filter you setOne subscription per method call
BackpressureBuilt-in flow controlManual handling
Data SourceDirect from validator (Geyser)Processed via RPC layer

gRPC suits HFT bots, MEV searchers, arbitrage systems, snipers and real-time analytics: anything that needs account and transaction changes as the validator processes them.

Getting Started

1. Get Your Endpoint

Buy a gRPC plan at checkout (see pricing) and enter your server's IP address. That address is added to the allowlist — no API keys needed.

The endpoint is plaintext HTTP/2 on port 10001: Frankfurt 88.216.36.108:10001 or Amsterdam 84.32.104.38:10001. The examples below use Frankfurt.

2. Install Dependencies

Node.js

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

Rust

Add to Cargo.toml:

TOML
[dependencies]
yellowstone-grpc-client = "6.0.0"
yellowstone-grpc-proto = "6.0.0"
tokio = { version = "1.0", features = ["full"] }
tonic = "0.12"
futures = "0.3"

3. Get the Proto File

Download the Yellowstone Geyser proto definition:

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

4. Connect and Subscribe

Node.js

JavaScript
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const packageDef = protoLoader.loadSync('geyser.proto', {
  keepCase: true, longs: String, enums: String, defaults: true, oneofs: true
});
const proto = grpc.loadPackageDefinition(packageDef).geyser;
const client = new proto.Geyser('88.216.36.108:10001', grpc.credentials.createInsecure());

const stream = client.subscribe(new grpc.Metadata());

stream.on('data', (update) => {
  if (update.account) {
    console.log('Account update:', update.account.account.pubkey);
  } else if (update.transaction) {
    console.log('Transaction:', update.transaction.transaction.signature);
  } else if (update.slot) {
    console.log('Slot:', update.slot.slot);
  }
});

stream.on('error', (err) => {
  console.error('Stream error:', err.message);
  // Implement reconnection logic here
});

// Subscribe to all slot updates and Raydium program accounts
stream.write({
  accounts: {
    raydium: {
      account: [],
      owner: ['675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'],
      filters: []
    }
  },
  slots: { slots: {} },
  transactions: {},
  blocks: {},
  blocksMeta: {},
  commitment: 1, // Processed
  entry: {},
  accountsDataSlice: [],
  ping: null
});

Rust

Rust
use yellowstone_grpc_client::GeyserGrpcClient;
use yellowstone_grpc_proto::prelude::*;
use std::collections::HashMap;
use futures::StreamExt;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut client = GeyserGrpcClient::build_from_uri("http://88.216.36.108:10001")
        .connect()
        .await?;

    // Subscribe to Raydium program accounts and all slots
    let mut accounts = HashMap::new();
    accounts.insert("raydium".to_string(), SubscribeRequestFilterAccounts {
        account: vec![],
        owner: vec!["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8".to_string()],
        filters: vec![],
        nonempty_txn_signature: None,
    });

    let mut slots = HashMap::new();
    slots.insert("slots".to_string(), SubscribeRequestFilterSlots {
        filter_by_commitment: None,
        interleave_updates: None,
    });

    let request = SubscribeRequest {
        accounts,
        slots,
        transactions: HashMap::new(),
        blocks: HashMap::new(),
        blocks_meta: HashMap::new(),
        commitment: Some(CommitmentLevel::Processed as i32),
        entry: HashMap::new(),
        accounts_data_slice: vec![],
        ping: None,
        from_slot: None,
        transactions_status: HashMap::new(),
    };

    let (_, mut stream) = client.subscribe_with_request(Some(request)).await?;

    while let Some(msg) = stream.next().await {
        match msg?.update_oneof {
            Some(UpdateOneof::Account(account)) => {
                println!("Account update: {:?}", account);
            }
            Some(UpdateOneof::Slot(slot)) => {
                println!("Slot: {}", slot.slot);
            }
            _ => {}
        }
    }

    Ok(())
}

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)

JavaScript
stream.write({
  accounts: {
    pool: {
      account: ['POOL_ACCOUNT_PUBKEY'],
      owner: [],
      filters: []
    }
  },
  slots: {},
  transactions: {},
  blocks: {},
  blocksMeta: {},
  commitment: 1,
  entry: {}
});

Track All Transactions for a Program

JavaScript
stream.write({
  accounts: {},
  slots: {},
  transactions: {
    pumpfun: {
      vote: false,
      failed: false,
      account_include: ['6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P'],
      account_exclude: [],
      account_required: []
    }
  },
  blocks: {},
  blocksMeta: {},
  commitment: 1,
  entry: {}
});

Watch Multiple Wallets

JavaScript
stream.write({
  accounts: {
    wallets: {
      account: [
        'WALLET_PUBKEY_1',
        'WALLET_PUBKEY_2',
        'WALLET_PUBKEY_3'
      ],
      owner: [],
      filters: []
    }
  },
  slots: {},
  transactions: {},
  blocks: {},
  blocksMeta: {},
  commitment: 1,
  entry: {}
});

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 the earliest updates, 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

JavaScript
function connectWithRetry() {
  const stream = client.subscribe(new grpc.Metadata());

  stream.on('data', handleUpdate);

  stream.on('error', (err) => {
    console.error('Stream error:', err.message);
    setTimeout(connectWithRetry, 1000); // Reconnect after 1 second
  });

  stream.on('end', () => {
    console.log('Stream ended, reconnecting...');
    setTimeout(connectWithRetry, 1000);
  });

  // Send subscription request
  stream.write(subscriptionRequest);
}

connectWithRetry();

Support