Yellowstone gRPC limits
Connection, stream, and filter limits for Corvus Yellowstone gRPC on Solana, and which gRPC status and reason each rejection carries.
Cross a limit here and you usually get a gRPC status plus a structured reason naming what you went over, so branch on the reason. One protection skips gRPC altogether: cancel streams fast enough and the connection closes with an HTTP/2 GOAWAY carrying ENHANCE_YOUR_CALM, no gRPC status and no reason field.
Most of the limits scale with your plan's RPS.
| Limit | Value |
|---|---|
| Concurrent connections | ceil(RPS × 5%), minimum 1, capped at 50 |
accounts: account keys, summed across every accounts filter | RPS |
accounts: owner keys, summed across every accounts filter | RPS ÷ 5 |
accounts: data slices | 10 |
transactions: account_include / account_exclude / account_required, each summed across every transactions filter | RPS each |
slots filters | 10 |
blocks_meta filters | 10 |
entry filters | 10 |
| Silence timeout | 20 seconds |
Worked through: on a 500 RPS plan that's 25 connections, 500 account keys or 100 owner keys, and 500 keys in each of account_include, account_exclude and account_required. At 1,000 RPS you get 50 connections, 1,000 accounts and 200 owners.
NFT pass holders are the exception: their caps are flat numbers rather than RPS-derived ones.
| Limit | Pro | Elite |
|---|---|---|
accounts account keys | 1,200 | 2,000 |
accounts owner keys | 200 | 200 |
transactions account_include / account_exclude / account_required, each | 1,200 | 2,000 |
slots filters | 50 | 100 |
Data slices, blocks_meta and entry filters stay at 10 either way.
Every filter figure above is a total, not a per-filter allowance. Keys are summed across every filter in the request, and that running total is charged to your account (or to your source IP, on an IP-allowlisted endpoint) across every connection you hold to that endpoint. When a filters rejection lands it's raised against the connection that sent the offending SubscribeRequest, and that request's contribution is rolled back, so your other connections keep running.
If you need a filter set larger than your plan allows, we can arrange it; ask us on Discord.
The silence timeout is armed by your writes, not the server's. If the server has sent nothing for 20 seconds while your client is still writing to the connection, the connection closes, so an application-level keepalive won't prevent it. The fix is a filter wide enough that the server always has something to send.
Limit classes
| Limit | What it controls | Failure |
|---|---|---|
| Connections | Open gRPC channels | RESOURCE_EXHAUSTED (8), reason connection_limit |
| Concurrent streams | Active RPC streams on a channel | RESOURCE_EXHAUSTED (8), reason stream_limit |
| Filters | Count and breadth of selectors | RESOURCE_EXHAUSTED (8), reason filters |
| Subscription updates | How often a connection rewrites its filter configuration | RESOURCE_EXHAUSTED (8), reason subscribe_rate_limit |
| Stream resets | How fast a connection cancels streams (RST_STREAM) | HTTP/2 GOAWAY with ENHANCE_YOUR_CALM; the connection is terminated. No gRPC status and no reason string are sent |
The weighted per-method costs in Solana RPC rate limits don't apply to a stream; streams are metered by connections, streams and filters. RPS, TPS and streaming allowances stay separate.
The PPS gate (aRPC only)
The packets-per-second gate only runs on the aRPC transport (http://arpc.<region>.corvus-labs.io:20202), so nothing on port 10101 will ever see it. It arrives as PERMISSION_DENIED (7) with the detail prefix PPS limit requirement not met, never as JSON-RPC code -32100 in an HTTP error body. If you also consume aRPC, PPS closes the connection has the requirement and the way out.
Matching detail strings
Detail strings can carry extra text after the stable prefix, including a trailing support sentence pointing at discord.gg/corvus-labs. Compare on status, reason and prefix.
Handle a rejection
| Symptom | Cause | Fix |
|---|---|---|
RESOURCE_EXHAUSTED, reason stream_limit | Too many concurrent streams on the channel | Close unused streams before opening another |
RESOURCE_EXHAUSTED, reason filters | Filter count or breadth over quota | Narrow the selector; splitting it across connections does not help |
RESOURCE_EXHAUSTED, reason subscribe_rate_limit | Filter configuration rewritten too often | Stop rewriting the request; back off, then send one complete configuration |
HTTP/2 GOAWAY, ENHANCE_YOUR_CALM | Too many stream cancellations in a short window | Stop opening and cancelling streams; the connection is terminated, so reopen once |
RESOURCE_EXHAUSTED, reason rate_limit | General allowance exhausted | Reduce load, then retry |
PERMISSION_DENIED, reason permission_denied | Resource does not include this product | Check access; reconnecting cannot fix it |
INVALID_ARGUMENT (3), message naming from_slot | The replay request breaks its contract: wrong commitment, a blocks filter, or a slot older than the retained window | See replay on reconnect |
FAILED_PRECONDITION (9), message beginning from_slot replay | Replay wasn't available on the node that took this connection | Reconnect and retry, or re-subscribe without from_slot and backfill through JSON-RPC |
INVALID_ARGUMENT (3) | Malformed request or filter | Fix the request; do not retry unchanged |
UNAUTHENTICATED (16) | Wrong x-token or unregistered egress IP | See Access & authentication |
Gateway & streaming errors is the full status and reason reference, and Pricing lists the preset capacities.
Yellowstone gRPC usage
Connect, authenticate, subscribe, and reconnect to Corvus Solana streams with the @triton-one/yellowstone-grpc TypeScript client.
Solana RPC rate limits
How Corvus meters the Solana JSON-RPC endpoint: weighted method costs, the getMultipleAccounts formula, scan concurrency, and TPS.