The shape of the problem

Most Solana applications read chain state by polling JSON-RPC - calling getAccountInfo, getSignaturesForAddress, or getProgramAccounts on a timer. It's simple, it's stateless, and for wallets, dashboards, and explorers it's exactly right.

It falls apart the moment you're competing on time. Polling has a structural flaw: you only learn about a change on your next request. Between two polls, the world moved and you didn't know. The faster you poll to shrink that gap, the more you hammer the node - and the more you pay in rate limits, CPU, and bandwidth for answers that are mostly "nothing changed."

A poll loop spends most of its budget asking a question whose answer is usually "no." The one time the answer is "yes," you've already waited half an interval to hear it.

Where the milliseconds go

Stack up a single polled read and the latency is the sum of four things, none of which your strategy chose:

SourceWhat it costs you
Poll intervalOn average, half the interval of pure staleness - the gap since the last request
Request round-tripClient → node → client network time, every single call
Commitment delayWaiting for confirmed/finalized instead of seeing data as it lands
Queueing & rate limitsShared nodes throttle you; your call waits behind everyone else's

Drop a 400ms poll loop against a confirmed-commitment endpoint and you can be half a second or more behind the network before your strategy even evaluates. In a market where opportunities live for a few hundred milliseconds, that's not a tax - it's a disqualification.

"Just poll faster" doesn't scale

The obvious reaction is to tighten the loop - poll every 50ms, subscribe to more accounts, run more connections. Each of those hits a wall:

  • Rate limits. Public and shared RPC tiers cap requests per second. Tighten the loop and you get throttled, which adds latency exactly when you wanted less.
  • Fan-out. Watching thousands of accounts means thousands of subscriptions or a firehose of getProgramAccounts scans. WebSocket subscriptions (accountSubscribe, logsSubscribe) help - they push instead of you pulling - but the per-subscription model doesn't scale gracefully, and you're still reading at the node's commitment level.
  • Work in the wrong place. Client-side filtering means the node ships you everything and you discard 99% of it. The filtering should happen next to the data, not after a network hop.

You can engineer around each symptom, but you're optimizing the wrong model. Pulling will always be a step behind pushing.

Push instead of pull

Geyser-based Yellowstone gRPC flips the model. Instead of asking, you open one long-lived stream, describe what you care about with server-side filters, and the validator pushes account, transaction, and slot updates the moment it processes them. One connection, server-side filtering, data as it happens - it scales to thousands of accounts where fanned-out subscriptions choke. We go deeper on this in Yellowstone gRPC vs Standard RPC.

push-not-poll.ts
// one stream, server-side filters - no loop, no interval
const stream = await client.subscribe({
  accounts: { amm: { owner: [RAYDIUM_AMM] } },
  transactions: { swaps: { accountInclude: [RAYDIUM_AMM] } },
  commitment: "processed",
});
 
for await (const update of stream) {
  strategy.onUpdate(update); // arrives as the validator sees it
}

And one step further upstream: shreds

gRPC removes the poll interval, but it still surfaces data at a commitment level inside the validator. The earliest possible view is one layer up - at the propagation layer, where blocks travel as shreds before any node has assembled a confirmed block. Decoding transactions straight from shreds gives you first-seen, pre-confirmation visibility. That's the subject of What Are Solana Shreds?.

Location is the other half

Switching from pull to push removes the interval. It does nothing about distance. If your stream originates a region away from the leader, every update still crosses that gap before it reaches you

  • latency you're paying on top of whatever model you chose.

This is why rpc edge racks its RPC, gRPC, and shred infrastructure beside Solana stake clusters and the Jito Block Engine, and aggregates shreds from multiple upstreams so you always get whichever copy arrives first. Faster software a region away still loses to equal software next to the cluster. Proximity is the cheapest latency you'll ever buy.

Stop polling. Start streaming.

rpc edge runs push-based gRPC and decoded shreds co-located with the cluster - the earliest view, fewest hops.

View plans & pricing →

The takeaway

Polling isn't broken - it's just the wrong shape for time-sensitive reads. You can't out-poll a push model, and you can't out-run distance. If your strategy reacts to Solana in real time, move off the poll loop: push with gRPC, read upstream from shreds, and put it all next to the cluster. That's the difference between watching the market and being early to it.

Frequently asked questions

Is polling Solana RPC ever the right choice?
Yes - for one-off reads, sending transactions, fetching history, and any request where setting up a stream is overkill. Polling is the wrong tool only for continuous, latency-sensitive state where a push stream or shred decode wins.
How much latency does a poll interval add?
On average, half your poll interval, plus the request round-trip and the node's response time. A 400ms poll loop adds ~200ms of expected staleness on top of network and commitment delay - an eternity for arbitrage or sniping.
Do WebSocket subscriptions fix the polling problem?
Partly. accountSubscribe and logsSubscribe push updates instead of you polling, which helps, but they don't scale gracefully to thousands of accounts and still surface data at the node's commitment level. Geyser-based gRPC is the push model built for scale.
What is the fastest way to read Solana state?
Decode transactions directly from shreds at the propagation layer - that is upstream of even gRPC's processed commitment. Layer RPC for confirmed truth, gRPC for live account state, and shreds for first-seen signal.