# Solana JSON-RPC over HTTP (/solana-rpc/json-rpc)

> 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

```bash
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](https://solana.com/docs/rpc) is your method documentation. What changes here is the metering (see [Rate limits](/solana-rpc/limits)) and the gateway error contract below.

<Callout type="warn" title="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](https://discord.gg/corvus-labs) where it stands before you build around it.
</Callout>

## 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.

```ts
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](/solana-rpc/errors).

## Batches

A batch is one HTTP body containing an array of calls:

```json
[
  { "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](/solana-rpc/transactions).
