Skip to content
Fundamentals11 min read

What Is JSON-RPC? How Solana's Node API Works

JSON-RPC is a four-field request and a two-field response, and Solana's entire node API is built on it. This explainer covers the protocol, Solana's HTTP and WebSocket methods, commitment levels, encodings, common errors, and where gRPC and shreds take over.

Sourav Mishra · OrbitFlare
What Is JSON-RPC? How Solana's Node API Works

What is JSON-RPC?

JSON-RPC is a remote procedure call protocol which is encoded in JSON. The latest version 2.0 of the protocol (see jsonrpc.org) defines the request object, the response object, the error object and the batch semantics and nothing else. It doesn't define a transport, authentication or a type system. Such a minimalistic approach makes JSON-RPC a widely used protocol: it's implemented by Ethereum, Solana, Bitcoin Core and other systems.

JSON-RPC request format:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getBalance",
  "params": ["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]
}

JSON-RPC response format:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": { "context": { "slot": 1 }, "value": 0 }
}

The id binds the response to a particular request, which allows a client to send multiple requests using one connection and process the responses accordingly. In case of an error, the response contains an error object with a numeric code and a message instead of a result.

Three useful features:

  • Stateless. Each request contains everything needed to process it. There's no session kept on the server side, which makes balancing between multiple RPC nodes easy.
  • Transport-agnostic. HTTP is the default transport for request-response calls and WebSockets are used to allow a server to push notifications.
  • Batchable. The client can send an array of request objects and receive an array of response objects in return, which reduces roundtrips.

How Solana uses JSON-RPC

A Solana node exposes a JSON-RPC 2.0 interface, documented at solana.com/docs/rpc. The Solana node API is divided into two groups depending on the transport used.

HTTP methods perform request and response calls. You may query a balance, an account, a block, a transaction or submit a signed transaction and receive a response. Examples of such methods: getAccountInfo, getBalance, getBlock, getTransaction, getLatestBlockhash, sendTransaction, simulateTransaction, getProgramAccounts.

WebSocket methods subscribe to notifications. You make a subscription once and receive a notification from a node every time it encounters a relevant event. All notifications are wrapped in the JSON-RPC protocol. Examples of such methods: accountSubscribe, logsSubscribe, programSubscribe, signatureSubscribe, slotSubscribe.

The methods themselves adhere to the JSON-RPC protocol specification. Solana introduces conventions in the params: the configuration object as the last parameter, which may contain commitment, encoding and some method-specific filters.

An RPC provider (such as OrbitFlare) runs nodes that expose this API and adds what raw nodes don't have: authentication, rate limiting, regional endpoints, load balancing and transaction forwarding prioritization. The RPC nodes page describes this in detail.

Commitment levels: the parameter that changes the answer

A Solana block passes through several levels of commitment and most read methods take a commitment parameter which allows you to select the level. With the advent of Alpenglow's Votor consensus algorithm replacing TowerBFT on mainnet in October 2026, the number of commitment levels was reduced to two in practice:

Which state your call actually reads

The same getBalance call may return different values depending on the commitment (a processed read and a finalized one), and each value is correct for the commitment level it corresponds to. When the results look strange, verify the commitment before doing anything else.

Also, for transaction submission, commitment determines what sendTransaction's preflightCommitment will simulate against and what signatureSubscribe will wait for.

The methods you will actually use

Having a good understanding of about a dozen methods will cover most cases.

Reading state

  • getAccountInfo: the data and metadata for a single account. The workhorse.
  • getMultipleAccounts: several accounts in one call. Use it instead of calling getAccountInfo in a loop.
  • getProgramAccounts: all accounts owned by a program, with optional filters. It's a very powerful but also very expensive method that is frequently priced or capped separately.
  • getBalance: lamports of an account.
  • getTokenAccountBalance and getTokenAccountsByOwner: balances of SPL tokens.

Blocks and transactions

  • getLatestBlockhash: the recent blockhash a transaction must reference to be valid. Call it immediately before signing.
  • getBlock and getTransaction: block and transaction details with a possibility to set an encoding.
  • getSignaturesForAddress: a paginated history of transactions affecting a particular address.

Submitting

  • simulateTransaction: simulate a transaction execution against the current state without sending it to the cluster. Use it to detect errors and estimate compute usage.
  • sendTransaction: submit a signed transaction. A node forwards it to the current leader. A signature is returned, but not the confirmation. The confirmation process is done separately.

Subscribing

  • accountSubscribe: receive notifications on account changes.
  • logsSubscribe: receive notifications for transactions whose logs mention a particular address or program.
  • signatureSubscribe: a single notification for a transaction reaching the desired commitment level.

Encoding and data size

Account data and transactions may be big and JSON encoding may be verbose. Most methods have an encoding parameter. base64 encoding is efficient for raw account data; jsonParsed encoding provides decoded fields for known programs (for example, for the token program) at the cost of size; base58 is a legacy encoding, slow and limited to account data of 128 bytes or less. Use base64 if you're going to parse the data yourself and use jsonParsed for decoding by the node.

Two practices related to size reduce your bandwidth usage and prevent rate limiting:

  • Use dataSlice in getAccountInfo and getProgramAccounts to receive only the necessary bytes.
  • Use getMultipleAccounts and batch requests instead of multiple individual calls.

Errors and what they mean

JSON-RPC errors have a code and a message. Some errors are common for Solana:

  • Blockhash not found or transaction expired: the blockhash you used is too old. Get a fresh blockhash and sign again.
  • Transaction simulation failed: the program returned an error. The logs in the error data will usually show the instruction that failed and why.
  • Rate limited (HTTP 429 from a provider): you exceeded your plan's request or compute budget. Batch requests, use subscriptions or upgrade.
  • Node is behind: the node is lagging behind the cluster. Providers automatically route around it; a self-hosted node requires fixing.

Remember: sendTransaction returning a signature means "accepted for forwarding", not a confirmation. Always confirm a transaction separately using either signatureSubscribe or a getSignatureStatuses poll and be prepared to re-sign with a new blockhash if the transaction expires.

Where JSON-RPC stops being the right tool

JSON-RPC is great for querying and submissions. It's a bad choice for three workloads. Knowing the boundaries saves you from fighting the protocol:

State streaming with high frequency. If you require getting every change to thousands of accounts as soon as it happens, WebSocket subscriptions grow to thousands of subscriptions and JSON encoding leads to a serialization bottleneck. Geyser gRPC (Yellowstone) streams account and transaction updates from a plugin running inside the node, filtered on the server side, in a binary encoding. See the gRPC product page for both Yellowstone and Jetstream services.

Detection of events with the lowest possible latency. A WebSocket notification arrives after the node processes the block. A shred-level feed lets you see the block as it's constructed by the leader. The difference is crucial for sniping, liquidations and copy trading. See what is Shredstream.

Priority transaction delivery. sendTransaction forwards a transaction through an unstaked connection which gets dropped first under congestion. Staked lanes and Jito bundles are different routes, described in how to land transactions on Solana.

A typical production stack uses all three: JSON-RPC for reading and submissions, gRPC for state streaming and a shred-level feed for events where the lowest latency matters.

A minimal client walkthrough

Any Solana client library hides the same process. Understanding it without the wrapper makes debugging easier.

  1. Fetching a blockhash. getLatestBlockhash with finalized commitment (since Alpenglow, it's the same state that confirmed used to provide you, and confirmed is being phased out).
  2. Building the transaction. Instructions, accounts, the fee payer and the blockhash.
  3. Simulation. simulateTransaction to detect program errors and read compute usage.
  4. Setting the priority fee. A compute budget instruction with the compute unit price suitable for the accounts you're using.
  5. Signing and sending. sendTransaction with the skipPreflight flag set explicitly (false for safety, true if you've already simulated the transaction).
  6. Confirmation. signatureSubscribe with the required commitment and a timeout to trigger a re-sign on expiration.

Each step involves a JSON-RPC call. Whenever something goes wrong, repeat the exact JSON-RPC call with a plain HTTP client and look at the response; the error object will almost always say what the problem is.

Conclusion

JSON-RPC is a four-field request and a two-field response and this is the reason Solana chose it to build its node API. Learn the dozen methods that cover most needs, use the commitment deliberately, batch and slice your requests to avoid the rate limit and treat sendTransaction as the beginning of the confirmation process. Whenever you run into the limitations of JSON-RPC, which occurs with high-frequency streaming and lowest-latency event detection, move to gRPC and shred-level feeds.

For an endpoint to make calls against, the RPC plans include a free tier with WebSockets and the gRPC service is available when streaming outgrows subscriptions.

Frequently asked questions

Is JSON-RPC the same as REST?
No. REST models resources as URLs with HTTP verbs. JSON-RPC sends a method name and parameters to one endpoint, usually by POST, and the method name decides what happens. JSON-RPC is closer to a function call over the network.
Does Solana use JSON-RPC 1.0 or 2.0?
2.0. Every request carries `"jsonrpc": "2.0"`, and the response and error objects follow the 2.0 specification.
Can I batch Solana RPC calls?
Yes. Send an array of request objects and you receive an array of responses. Providers may limit batch size, and batching does not bypass compute or rate limits; it reduces round trips.
Why does my balance differ between two calls?
Almost always commitment. A `processed` read sees the latest block the node has handled; a `finalized` read sees only blocks that cannot be rolled back. Both are correct for their level. Since Alpenglow, `confirmed` and `finalized` return the same state.
What is the difference between an RPC node and a validator?
A validator participates in consensus and produces blocks. An RPC node runs the same software with voting disabled and serves the JSON-RPC API. Providers run RPC nodes; some also run validators.
When should I switch from WebSockets to gRPC?
When subscription count, message volume, or latency becomes the bottleneck. A few subscriptions for a dashboard are fine over WebSockets; tracking thousands of accounts or racing on event detection needs gRPC or shreds.

About the author

Sourav Mishra

Solutions Engineer · OrbitFlare

Sourav Mishra help build low-latency Solana infrastructure at OrbitFlare — shred streaming, gRPC pipelines, and eBPF-powered networking, mostly in Rust. He writes the kind of deep dives he wishes existed when he started: what a shred actually is, why Solana's VM shares a lineage with the packet filter in your kernel, and how to get transaction data out of the network before everyone else does.

More articles by Sourav Mishra
Thanks for reading.Back to all articles

Code. Build. Stream. Without Limits.

Build something that moves.

Choose your infrastructure. We’ll help you find the right setup.

A useful starting point

Edit the brief, then copy it and open your assistant. Paste it into a new chat to get started.

Official sources included. You choose when to send.