Summary
The Solana HTTP API is the JSON-RPC interface your app uses to read accounts, submit transactions, and query the cluster over standard HTTPS. You send a POST request with a JSON body to an RPC endpoint, and the node returns a JSON result or an error object. This article covers the request shape, the methods you will use most, and how to debug the failures that show up in real Solana apps. It also explains when a shared public endpoint is enough and when a dedicated Solana node is the better fit for production traffic.
The Solana HTTP API is the JSON-RPC interface your application uses to talk to a Solana node over HTTPS. Every wallet balance lookup, account read, transaction submission, and block query goes through a POST request to an RPC endpoint. If you are building on Solana, this is the layer you will debug most often.
This page focuses on the practical side: the request shape, the methods you will actually call, how commitment levels change results, and how to read the errors that come back. It also helps you decide whether a shared endpoint is enough or whether your workload needs a dedicated Solana node.
Which endpoint should you start with?
Start with the official OnFinality public endpoint for Solana mainnet:
https://solana.api.onfinality.io/public
That endpoint is fine for local development, scripts, and low-volume reads. Move to a dedicated or private endpoint when you hit any of these signals:
- You are submitting transactions at a steady rate and seeing intermittent 429 responses.
- You need consistent access to historical account state or large
getProgramAccountsscans. - You run indexers, bots, or backends that poll the same accounts every few seconds.
- You need WebSocket subscriptions for account or slot changes alongside HTTP calls.
If your workload is read-heavy and bursty, a shared RPC plan usually covers it. If your workload is continuous and latency-sensitive, a dedicated Solana node gives you isolated capacity. You can compare options on the RPC pricing page and see the full list of supported RPC networks.
The request shape
Every Solana HTTP API call is a JSON-RPC 2.0 POST. The body has four fields: jsonrpc, id, method, and params.
curl https://solana.api.onfinality.io/public \
-X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": [
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
]
}'
The response returns a result object on success, or an error object with a code and message on failure. The id you send is echoed back, which matters when you batch requests.
A few things that trip people up:
paramsis always an array, even for a single argument.- Some methods take an options object as the second element, for example
{"commitment": "confirmed"}. - The
Content-Typeheader must beapplication/json.
Methods you will call most
You do not need to memorize the full method list. Most Solana apps use a small set repeatedly.
| Method | What it does | Typical caller |
|---|---|---|
getBalance | Returns the lamport balance of an account | Wallets, dashboards |
getAccountInfo | Returns the data and owner of an account | Programs, indexers |
getLatestBlockhash | Returns a recent blockhash for transaction building | Any transaction sender |
sendTransaction | Submits a signed transaction | Wallets, bots, backends |
getSignatureStatuses | Checks confirmation status of signatures | Transaction senders |
getTransaction | Returns a confirmed transaction by signature | Explorers, support tools |
getProgramAccounts | Returns accounts owned by a program | Indexers, analytics |
getSlot | Returns the current slot | Health checks, monitors |
getProgramAccounts deserves a warning. It can be expensive on large programs and is often the first call to time out or get rate limited on shared endpoints. If you rely on it, plan for a dedicated node or an indexed data source.
Commitment levels change what you get back
Solana does not have a single "confirmed" state. You choose a commitment level per request, and it changes both the result and the latency.
| Commitment | Meaning | Trade-off |
|---|---|---|
processed | Node has seen the slot | Fastest, can be rolled back |
confirmed | Supermajority of stake has voted | Balanced default for most apps |
finalized | Rooted, cannot be rolled back | Slowest, safest for settlement |
For a wallet showing a balance, confirmed is usually right. For anything that moves funds or triggers irreversible business logic, wait for finalized. If you mix commitment levels across calls, you can get inconsistent reads, for example a balance that appears before the transaction that changed it.
Reading errors instead of guessing
When a Solana HTTP API call fails, the error object tells you where to look. The table below maps common symptoms to the likely cause and the next step.
| Symptom | Likely cause | Next step |
|---|---|---|
| HTTP 429 | Rate limit on a shared endpoint | Back off, batch reads, or move to a dedicated node |
-32602 invalid params | Wrong param shape or missing options object | Check the method signature and array order |
-32002 transaction simulation failed | Transaction would fail on-chain | Run simulateTransaction and read the logs |
Blockhash not found | Blockhash expired before submission | Fetch a fresh blockhash right before sending |
Empty result on getTransaction | Not yet confirmed or wrong commitment | Retry with confirmed or finalized |
Timeout on getProgramAccounts | Result set too large | Add filters or use a dedicated node |
A useful habit is to log the full error object, not just the message. The data field often contains logs from the failed transaction, which usually points straight at the program error.
Building and sending a transaction
The HTTP API does not sign transactions for you. You build and sign locally, then submit the signed bytes. A minimal JavaScript flow looks like this:
import { Connection, PublicKey, Transaction, SystemProgram } from "@solana/web3.js";
const connection = new Connection("https://solana.api.onfinality.io/public", "confirmed");
const from = new PublicKey("<YOUR_WALLET_PUBLIC_KEY>");
const to = new PublicKey("<RECIPIENT_PUBLIC_KEY>");
const { blockhash } = await connection.getLatestBlockhash("confirmed");
const tx = new Transaction().add(
SystemProgram.transfer({ fromPubkey: from, toPubkey: to, lamports: 1_000_000 })
);
tx.recentBlockhash = blockhash;
tx.feePayer = from;
// Sign with your wallet adapter or keypair, then:
const signature = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(signature, "confirmed");
Two details matter here. First, always fetch a fresh blockhash immediately before sending, because blockhashes expire. Second, confirm the signature rather than assuming submission equals success.
HTTP versus WebSocket
HTTP is request-response. WebSocket is a persistent connection that pushes updates. Use HTTP for reads and transaction submission. Use WebSocket when you need to react to changes without polling.
const subId = connection.onAccountChange(
new PublicKey("<ACCOUNT_PUBLIC_KEY>"),
(accountInfo) => {
console.log("Account changed:", accountInfo.lamports);
},
"confirmed"
);
OnFinality exposes WebSocket transport for Solana alongside HTTP, so you can keep both on the same provider. If your app polls the same account every few seconds, switching to a subscription usually reduces load and improves reaction time.
When to move off a public endpoint
A public endpoint is a starting point, not a production plan. The decision usually comes down to three questions:
- Is your traffic continuous or occasional? Continuous traffic needs isolated capacity.
- Do you depend on expensive calls like
getProgramAccountsor historical reads? Those need headroom. - Do you need predictable behavior under load? Shared endpoints are best-effort by nature.
If you answer yes to more than one, look at a dedicated node. Dedicated infrastructure gives your app its own capacity, which removes the noisy-neighbor problem and makes rate-limit behavior predictable. OnFinality provides both RPC API access and dedicated node options, so you can start shared and move up without changing your integration.
For devnet and testing, use the Solana Devnet RPC page to set up a separate endpoint so test traffic never competes with production.
Operational checks before you ship
Before you point real users at your Solana HTTP API setup, confirm these basics:
- Your endpoint is configurable through an environment variable, not hardcoded.
- You have a fallback endpoint or provider for failover.
- You log request IDs and error codes for support.
- You monitor
getSlotorgetHealthon a schedule to detect stalls early. - You batch independent reads instead of firing them one at a time.
- You handle 429 responses with exponential backoff.
A simple health probe looks like this:
curl -s https://solana.api.onfinality.io/public \
-X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"getHealth"}'
If getHealth returns "ok", the node is responding. Pair that with a slot check to confirm the node is actually advancing.
Key Takeaways
- The Solana HTTP API is JSON-RPC over HTTPS: POST a JSON body, get a JSON result or error.
paramsis always an array, and options like commitment go in a second element.- Commitment level changes both latency and safety; use
finalizedfor irreversible actions. - Expensive calls like
getProgramAccountsare the first to fail on shared endpoints. - Use HTTP for reads and submission, WebSocket for subscriptions.
- Move to a dedicated node when traffic is continuous or latency-sensitive.
Frequently Asked Questions
Is the Solana HTTP API the same as JSON-RPC?
Yes. When people say Solana HTTP API, they mean the JSON-RPC interface served over HTTPS. The transport is HTTP, the payload format is JSON-RPC 2.0.
What is the default commitment level?
If you do not pass a commitment option, most methods default to finalized. Many apps explicitly set confirmed for faster reads.
Why do I get 429 responses?
A 429 means you hit a rate limit, which is common on shared public endpoints. Reduce request volume, batch reads, or move to a dedicated node.
Can I use the same endpoint for mainnet and devnet?
No. Mainnet and devnet are separate clusters with separate endpoints. Keep them in separate configuration so test traffic never touches production.
Do I need WebSocket if I already use HTTP?
Only if you need push updates. If you poll the same account repeatedly, a WebSocket subscription is usually more efficient.
How do I debug a failed transaction?
Run simulateTransaction and read the logs in the error data field. The program error code usually identifies the cause.
Next steps
If you are still evaluating, start with the public endpoint above and measure your request patterns for a week. If you see rate limits, timeouts on large queries, or a need for WebSocket subscriptions, review RPC pricing and the Solana network page to pick a plan that matches your workload. For teams running continuous traffic, a dedicated node removes the shared-capacity ceiling and keeps your Solana HTTP API calls predictable.