DEV Community

Cover image for What Is JSON-RPC? How Solana's Node API Works
OrbitFlare RPC
OrbitFlare RPC

Posted on Originally published at orbitflare.com

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"]
}
Enter fullscreen mode Exit fullscreen mode

JSON-RPC response format:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": { "context": { "slot": 1 }, "value": 0 }
}
Enter fullscreen mode Exit fullscreen mode

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.


Originally published at orbitflare.com.

Top comments (0)