How Solana RPC is organized

Every Solana node exposes a JSON-RPC interface, and it comes in two flavours that solve different problems.

HTTP methods are request/response. You POST a method name and parameters, the node does the work, and it answers once. The connection has no memory of you afterwards. This is the right shape for one-off reads (what's in this account?), for writes (send this transaction), and for history (what happened at this signature?). It is also the shape that tempts you into polling - calling the same method on a timer to watch something change - which is exactly where latency creeps in.

WebSocket subscriptions are push channels. You open a long-lived connection, subscribe to a thing once, and the node sends you a message every time that thing changes. No loop, no interval, no re-asking. Subscriptions are the right shape for watching state evolve: an account you care about, the logs of a program, the status of a transaction you just sent.

HTTP methods answer a question once. WebSocket subscriptions keep answering it for you. The mistake is using the first to do the second's job - that's just polling with extra steps.

Both speak the same data model and both respect a commitment level (processed, confirmed, finalized) that decides how settled the data you get back is. We unpack that in Solana Commitment Levels Explained; for this reference, just remember that commitment is a knob on almost every method, and the looser it is the faster - and less settled - your answer.

The core read methods

These are the HTTP methods you'll call most. They fetch state and history without changing anything on chain.

MethodWhat it does
getAccountInfoReturns the data, owner, lamports, and metadata for a single account
getMultipleAccountsSame as above for a batch of accounts in one round-trip - prefer this over many getAccountInfo calls
getBalanceReturns just the lamport balance of an account
getProgramAccountsReturns every account owned by a program that matches your filters - powerful and heavy
getTokenAccountsByOwnerReturns the SPL token accounts held by a wallet, optionally filtered by mint
getLatestBlockhashReturns a recent blockhash and its last-valid height - required to build any transaction
getSlotReturns the current slot the node has reached at your commitment
getBlockReturns a full block - its transactions, rewards, and metadata - for a given slot
getTransactionReturns a confirmed transaction (and its decoded meta) by signature
getSignaturesForAddressReturns recent transaction signatures touching an address, newest first - the basis of "account history"
getSignatureStatusesReturns the confirmation status of one or more signatures - used to poll for a send landing

A few habits worth forming early. Batch with getMultipleAccounts instead of firing getAccountInfo in a loop. Use getSignaturesForAddress to page history, then getTransaction to hydrate the ones you care about. And treat getProgramAccounts as a tool you reach for deliberately, not reflexively - more on why below.

The write and send methods

These submit work to the cluster or estimate it before you commit.

MethodWhat it does
sendTransactionSubmits a signed, serialized transaction to the leader - returns a signature, not a confirmation
simulateTransactionRuns a transaction against the current bank without submitting it - catches errors and reports compute used and logs
getFeeForMessageReturns the fee a given message would cost at a recent blockhash

The key thing about sendTransaction is what it does not do: it does not wait for the transaction to land. It hands the bytes to the leader and gives you back a signature. Confirmation is a separate job - poll getSignatureStatuses or open a signatureSubscribe. Run simulateTransaction first and you'll catch most failures (bad accounts, insufficient compute, program errors) for free, before you spend a fee or a blockhash. Why so many sends fail anyway is its own topic, covered in Why Solana Transactions Fail.

The WebSocket subscriptions

Open a WebSocket connection and these let the node push updates to you instead of you asking.

SubscriptionWhat it pushes
accountSubscribeA message every time a specific account's data or lamports change
logsSubscribeTransaction logs matching a filter (all, or mentioning an address) as they occur
signatureSubscribeA single message when a given signature reaches your chosen commitment - then it's done
slotSubscribeA message each time the node processes a new slot - a cheap heartbeat
programSubscribeA message every time any account owned by a program changes, with optional filters

signatureSubscribe is the clean way to confirm a send: subscribe to the signature, get one push when it lands, move on. programSubscribe is the push-based answer to getProgramAccounts - watch a program's accounts change instead of re-scanning them on a timer. That said, the per-subscription model has a ceiling: thousands of accountSubscribe channels or a busy programSubscribe firehose will strain a single WebSocket, which is the wall that pushes serious pipelines toward gRPC.

The gotchas that bite

A reference is only useful if it tells you where the edges are. These are the three that catch people most often.

getProgramAccounts is heavy. It walks every account a program owns and returns the ones that match your filters. On a program with a large account set, that's a slow, memory-hungry scan the node performs on every call. Shared and public tiers routinely rate-limit it, cap the response size, or surcharge it - and rightly so, because one tight loop of getProgramAccounts can degrade a node for everyone on it. Use filters (dataSize, memcmp) to shrink the result, and if you're calling it repeatedly to track changes, you want a programSubscribe or a gRPC stream, not a poll.

Polling adds latency you didn't choose. Any method called on a timer - getAccountInfo, getSignaturesForAddress, getProgramAccounts - pays, on average, half your poll interval in pure staleness before you even see a change, plus the round-trip every call. A 400ms loop is ~200ms behind the network on a good day. For latency-sensitive work that's disqualifying; the full breakdown is in Why RPC Polling Can't Keep Up.

Commitment matters. Nearly every read takes a commitment, and the default isn't always what you want. Read at processed and you may see state that gets dropped; read at finalized and you wait longer for certainty. Confirm sends and reconcile accounting at the commitment your strategy actually requires - don't inherit a default and hope.

Outgrowing request/response?

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

View plans & pricing →

A couple of calls in practice

Reading one account, then sending a transaction and confirming it, looks roughly like this.

rpc-calls.ts
// read a single account at confirmed commitment
const info = await connection.getAccountInfo(pubkey, "confirmed");
 
// build, simulate, send, confirm
const { blockhash, lastValidBlockHeight } =
  await connection.getLatestBlockhash("confirmed");
 
tx.recentBlockhash = blockhash;
tx.sign(payer);
 
// catch errors before paying for them
const sim = await connection.simulateTransaction(tx);
if (sim.value.err) throw new Error("would fail: " + JSON.stringify(sim.value.err));
 
// send returns a signature, not a confirmation
const sig = await connection.sendRawTransaction(tx.serialize());
 
// confirm by polling status (or use signatureSubscribe on the WebSocket)
const status = await connection.getSignatureStatuses([sig]);

Notice the rhythm: getLatestBlockhash to build, simulateTransaction to check, sendTransaction to submit, getSignatureStatuses to confirm. Four methods, one transaction.

When to stop using RPC methods and stream instead

RPC methods are the right tool for one-off reads, for writes, and for history. They stop being the right tool the moment you're watching state change continuously and competing on time.

The first step off the methods is Yellowstone gRPC. Instead of polling getProgramAccounts or fanning out thousands of accountSubscribe channels, you open one stream, describe what you care about with server-side filters, and the validator pushes account and transaction updates as it processes them. One connection, filtering next to the data, no loop. We compare the two models directly in Yellowstone gRPC vs Standard RPC.

The step beyond that is decoded shreds. gRPC removes the poll interval but still surfaces data at the validator's commitment. Shreds sit one layer further upstream, at the propagation layer, where blocks travel before any node has assembled a confirmed one. Decode transactions straight from shreds and you get first-seen, pre-confirmation visibility - the earliest the network makes a transaction observable.

The takeaway

Most of Solana RPC is a dozen methods: a handful of reads, two or three writes, five subscriptions. Learn the shapes - HTTP for ask-once, WebSocket for keep-answering - watch out for getProgramAccounts and tight poll loops, and set commitment deliberately. Then, when request/response stops keeping up with a real-time strategy, move up the stack: push with gRPC, read upstream from shreds, and put it all next to the cluster.

Frequently asked questions

What are the most common Solana RPC methods?
For reads: getAccountInfo, getMultipleAccounts, getBalance, getProgramAccounts, getTokenAccountsByOwner, getLatestBlockhash, getSlot, getBlock, getTransaction, getSignaturesForAddress, and getSignatureStatuses. For writes: sendTransaction and simulateTransaction. On WebSocket: accountSubscribe, logsSubscribe, signatureSubscribe, slotSubscribe, and programSubscribe.
Why is getProgramAccounts expensive?
getProgramAccounts scans every account owned by a program and returns those matching your filters. The node has to walk a large account set on each call, so it is slow, memory-heavy, and frequently rate-limited or surcharged on shared tiers. For anything continuous, use a programSubscribe or a gRPC stream with server-side filters instead of polling it.
How do I send a transaction?
Build and sign the transaction, then submit the serialized bytes with sendTransaction. The node forwards it to the current leader and returns a signature, not a confirmation. To confirm, poll getSignatureStatuses or open a signatureSubscribe. Use simulateTransaction first to catch errors and estimate compute without paying fees.
What is the difference between an HTTP method and a WebSocket subscription?
An HTTP method is one request and one response: you ask, the node answers, the connection's job is done. A WebSocket subscription is a long-lived push channel: you subscribe once and the node sends updates whenever the thing you watched changes. HTTP suits one-off reads and writes; subscriptions suit watching state change over time.
When should I use gRPC instead of RPC methods?
Reach for Yellowstone gRPC when you are watching many accounts or transactions continuously and per-subscription WebSockets or polled getProgramAccounts calls stop scaling. gRPC gives you one stream with server-side filters and processed-commitment pushes. For first-seen, pre-confirmation visibility, go one layer further upstream to decoded shreds.