Solana JSON-RPC over HTTP
Send Solana JSON-RPC 2.0 requests to the Corvus endpoint on port 8899, batch calls, and match gateway error codes correctly.
You send JSON-RPC 2.0 over HTTP POST to http://<region>.corvus-labs.io:8899, with content-type: application/json.
Send a request
curl --fail-with-body "$CORVUS_RPC_URL" \
--header 'content-type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "getLatestBlockhash",
"params": [{ "commitment": "processed" }]
}'We serve every Solana JSON-RPC method. There's no Corvus subset and no allowlist to check against, so the Solana JSON-RPC reference is your method documentation. What changes here is the metering (see Rate limits) and the gateway error contract below.
Ledger history reaches back 3 days
Historical methods (getBlock, getTransaction, getSignaturesForAddress and the rest) reach back a rolling 3 days. Archival depth to genesis is planned; ask on Discord where it stands before you build around it.
Error responses
| HTTP | Code | Message prefix |
|---|---|---|
| 400 | -32600 | Invalid request body |
| 401 | -32002 | Unauthorized |
| 403 | -32003 or -32004 | You have no access to this service |
| 429 | -32001 | RPS limit exceeded |
| 429 | -32005 | Transaction rate limit exceeded |
| 429 | -32005 | Scan request timed out waiting for concurrency slot |
| 502 | -32000 | Unable to process Batch Request |
| 503 | -32099 | Server is not available at the moment |
Messages sometimes carry a support sentence after the prefix (Please contact support at discord.gg/corvus-labs), so full-string comparison will eventually break. Match on status, code and prefix together.
-32005 is the one to watch. It covers two conditions with opposite fixes, transaction throughput and scan concurrency, and only the prefix separates them.
function classify(status: number, error: { code: number; message: string }) {
if (status === 429 && error.code === -32005) {
return error.message.startsWith('Scan request timed out')
? 'scan-concurrency'
: 'tps';
}
if (status === 429 && error.code === -32001) return 'rps';
return 'other';
}The -32100 code
-32100 / PPS limit requirement not met isn't a JSON-RPC error, and nothing on port 8899 will ever send it to you. The PPS limiter lives on the aRPC streaming transport, where it arrives as gRPC PERMISSION_DENIED (7) and the connection is then closed. See Gateway & streaming errors.
Batches
A batch is one HTTP body containing an array of calls:
[
{ "jsonrpc": "2.0", "id": 1, "method": "getSlot" },
{ "jsonrpc": "2.0", "id": 2, "method": "getLatestBlockhash" }
]Each item is metered on its own, at its own weight. Ten getTransaction calls in one batch cost 10 × 10 = 100 RPS units.
A batch the gateway can't proxy fails whole: HTTP 502 with -32000 Unable to process Batch Request for the batch, not per item.
Logging and retries
401 and 403 are configuration problems, not transient ones. When you log the JSON-RPC id, status and code, leave the endpoint URL out; on a dedicated endpoint the path is your credential. sendTransaction has its own retry rules, covered in Transactions.