# Solana RPC rate limits (/solana-rpc/limits)

> How Corvus meters the Solana JSON-RPC endpoint: weighted method costs, the getMultipleAccounts formula, scan concurrency, and TPS.



We meter the JSON-RPC endpoint on three separate axes, and they don't trade against each other.

| Axis             | What it covers                                         | Rejection                                                                |
| ---------------- | ------------------------------------------------------ | ------------------------------------------------------------------------ |
| RPS units        | All reads, charged at a weighted cost per method       | `429` · `-32001` · `RPS limit exceeded`                                  |
| TPS              | `sendTransaction` only                                 | `429` · `-32005` · `Transaction rate limit exceeded`                     |
| Scan concurrency | How many of six specific methods are in flight at once | `429` · `-32005` · `Scan request timed out waiting for concurrency slot` |

You pick your RPS and TPS when you rent capacity in the [dashboard](https://dashboard.corvus-labs.io). Everything below applies to the standard regional JSON-RPC endpoints. Connections, gRPC streams and filter quotas are counted separately; [Yellowstone gRPC limits](/solana-rpc/grpc/limits) and [aRPC limits](/arpc/limits) cover the streaming side.

## Method weights

| Method                                                                                               |                RPS units |
| ---------------------------------------------------------------------------------------------------- | -----------------------: |
| Most methods                                                                                         |                        1 |
| `getAccountInfo`, `getTokenAccountBalance`                                                           |                        2 |
| `getTokenAccountsByOwner`, `getTokenAccountsByDelegate`, `getTokenLargestAccounts`, `getTransaction` |                       10 |
| `getProgramAccounts`                                                                                 |                       30 |
| `sendTransaction`                                                                                    | 0; consumes TPS, not RPS |

Every item in a JSON-RPC batch is weighed on its own.

## `getMultipleAccounts`

`getMultipleAccounts` is charged by how many accounts you ask for, and there are two rates:

| Who                               | Cost                    | 100 accounts | 500 accounts (the cap) |
| --------------------------------- | ----------------------- | -----------: | ---------------------: |
| NFT pass holders, on any plan     | `ceil(accounts / 20)`   |            5 |                     25 |
| Everyone else, below 800 plan RPS | **2 units per account** |          200 |                  1,000 |
| Everyone else, from 800 plan RPS  | `ceil(accounts / 20)`   |            5 |                     25 |

You're charged for every account you ask for, whether or not it exists. One call can request at most **500 accounts**, and that 500 is counted across a whole batch, so several `getMultipleAccounts` calls in one body share it.

The rate difference changes how you should read the method:

* At the per-account rate there is no batching discount: 100 accounts cost 200 units through `getMultipleAccounts` and 100 × 2 = 200 through `getAccountInfo`. A 500-account call costs 1,000 units, more than a Starter or Standard plan's entire per-second allowance, so split wide reads or size up.
* At `ceil(accounts / 20)` the discount is real: the same 100 accounts cost 5 units against `getAccountInfo`'s 200, and the 500-account cap tops out at 25.

## Batch limits

| Limit                                                          | Value          |
| -------------------------------------------------------------- | -------------- |
| Request body                                                   | **16 MiB**     |
| Response body                                                  | **16 MiB**     |
| Body read timeout                                              | **30 seconds** |
| `getMultipleAccounts` accounts, counted across the whole batch | **500**        |

Exceed the body limit, or upload slowly enough to run past the read timeout, and the whole batch fails rather than the offending item.

## Scan concurrency

Six methods count as **scans**. They share a concurrency pool that sits on top of their RPS cost rather than replacing it:

|                                  |               |
| -------------------------------- | ------------- |
| Concurrent scans, your account   | **3**         |
| Concurrent scans, all accounts   | **20**        |
| Wait for a slot before rejection | **5 seconds** |

| Scan method                  | RPS units |
| ---------------------------- | --------: |
| `getProgramAccounts`         |        30 |
| `getTokenAccountsByOwner`    |        10 |
| `getTokenAccountsByDelegate` |        10 |
| `getTokenLargestAccounts`    |        10 |
| `getLargestAccounts`         |         1 |
| `getSupply`                  |         1 |

<Callout type="warn" title="getLargestAccounts and getSupply look cheap but are scans">
  Both cost a single RPS unit, so they sail through RPS budgeting and traffic shaping without registering. They still take a slot in the same pool as `getProgramAccounts`. That's how a health check polling `getSupply` in parallel ends up starving your real scan work.
</Callout>

### `Scan request timed out waiting for concurrency slot`

```text
HTTP 429
code    -32005
message Scan request timed out waiting for concurrency slot
```

From the outside this looks like 429s on scan methods while your RPS usage sits nowhere near the allowance, often on the cheap methods first. Queue the six scan methods separately from ordinary reads; three in flight is the number to design against.

## Rate limit responses

The two `-32005` conditions want opposite fixes, so branch on the message prefix with `startsWith`. Messages sometimes carry a trailing support sentence, and equality will miss it.

If you shape traffic client-side, charge the real weights. A token bucket that counts one unit per request lets through 30× too much `getProgramAccounts` and 2× too much `getAccountInfo`.

The full error contract, including the gRPC statuses the streaming interfaces use, is in [Gateway & streaming errors](/solana-rpc/errors).
