Limits
Three different kinds of limit, and they fail differently. Knowing which one you hit tells you what to do about it.
| Kind | Bounded by | Waiting helps? |
|---|---|---|
| Throughput — how fast | Your tier's rate class | Yes |
| Per transaction — how big | Admission policy | No: change the transaction |
| Volume — how much per month | Your plan's cap | No: buy more |
Rate classes
Your tier carries a rate class, and the class is part of what you bought — it is stored on chain, not in a configuration file we can quietly change.
| Class | Used by | Relay Units/s, sustained / burst | Gas/s per shard · burst | Max gas limit per transaction | Relay Units/h |
|---|---|---|---|---|---|
| 1 | Starter | 1 / 15 | 20 M · 200 M | 100 M | 300 |
| 2 | Builder | 5 / 25 | 50 M · 600 M | 600 M | 600 |
| 3 | Growth, Agent Pro | 20 / 60 | 100 M · 1,000 M | 600 M | 1,500 |
| 4 | Scale | 50 / 150 | 150 M · 1,500 M | 600 M | 3,000 |
| 5 | Enterprise, Agent Fleet | 100 / 300 | 200 M · 2,000 M | 600 M | 6,000 |
| 6 | Agent Metered | 2 / 15 | 20 M · 200 M | 100 M | 300 |
These are launch values; they are published in the pricing document with a policy version and read
from the chain as getRateClasses(). The backend's own configuration must be at or below the
on-chain table — it can tighten during an incident without an owner transaction, and can never
loosen beyond what the chain says.
| Dimension | Error |
|---|---|
| Relay Units per second | RATE_LIMITED |
| Gas per second, per shard | GAS_BUDGET_EXCEEDED |
| Relay Units per hour | HOURLY_BURN_EXCEEDED |
| Above the class gas limit | GAS_LIMIT_TOO_HIGH — permanent, not a rate limit |
All limits are per account, never per sender. A sponsor key may impose tighter limits of its own on top.
Note that the burst capacity is never below the largest single transaction the class allows — 15 units for classes 1 and 6, 25 for classes 2 to 5. A bucket that could not admit a transaction the class permits would be a permanent, invisible refusal.
There is also a platform-wide admission budget. When it binds, you get RATE_LIMITED with
details.scope = "platform" and there will be an incident: that one is ours, not yours.
Per-transaction ceilings
| Limit | Rule | Error |
|---|---|---|
| Gas price | Between the network minimum and twice it | GAS_PRICE_OUT_OF_RANGE, with details.bound = min or max |
| Gas limit, lower | At least the cost of moving the transaction | GAS_LIMIT_TOO_LOW |
| Gas limit, upper | The class ceiling above | GAS_LIMIT_TOO_HIGH |
| Data | 4,096 bytes on a self-serve account | DATA_TOO_LARGE |
| Over-provisioned gas | With simulation on, headroom above the simulated cost is bounded | GAS_OVERPROVISIONED |
| Deploys and upgrades | Allow-listed per account | DEPLOY_NOT_ALLOWED |
| Sender's balance | Must cover any value the transaction moves | INSUFFICIENT_SENDER_BALANCE |
| Version and options | Accepted forms only; the guarded bit is fine | TX_VERSION_UNSUPPORTED, TX_OPTIONS_UNSUPPORTED |
| Unknown transaction members | Refused | UNSUPPORTED_TX_FIELD |
| Variant sets | Refused outright | VARIANTS_NOT_SUPPORTED |
The balance check deserves a word: a relayed transaction that fails because the sender cannot
pay its own value still costs the relayer the whole fee. Checking it before co-signing is not
paternalism; it is the difference between your mistake costing you one Relay Unit and costing us a
fee for nothing.
In flight
| Limit | Value | Error |
|---|---|---|
| Non-terminal intents per sender | 64 | TOO_MANY_IN_FLIGHT |
| Assignments | 10/s per IP; 2/s with a burst of 10 per sender | RATE_LIMITED |
| Free-flow transactions in flight per sender | 1 | FREE_FLOW_BUSY |
| Free flag relays | 10 per 24 h, 30 per 30 days, per account | FREE_FLOW_BARRED |
The free-flow limits cover the calls CoRelayer pays for on your behalf — buying a plan, setting flags, removing senders. They are generous enough to manage an account and tight enough that the free list is not a free relay service.
Request limits outside the relay path
| Route class | Key | Limit |
|---|---|---|
| Public cached reads — pricing, relayers, status, incidents, network, account mirrors, intent status | IP | 20 req/s, burst 60 |
GET /v1/relay/{id}/events | IP | 20 concurrent streams |
POST /v1/quote, POST /v1/validate | IP / account | 2 req/s per IP, 5 req/s per authenticated account |
The prepare routes | IP | 2 req/s, burst 10 |
| x402 purchase routes | IP / payer | 1 req/s per IP, 6 per minute per payer |
| Private reads | account, by class | 5 / 10 / 20 / 40 / 80 / 10 req/s, burst ×4 |
GET /v1/stream | account, by class | 2 / 3 / 5 / 10 / 20 / 2 concurrent streams; 5 per IP |
| Writes on keys, webhooks, notifications | account | 1 req/s, burst 5 |
quote and validate are limited tightly because validate can trigger a chain simulation — it
is the most expensive public call on the API and the most useful, so it is protected rather than
removed.
Sizes
| Request body | 131,072 bytes → PAYLOAD_TOO_LARGE |
| Transaction data | 4,096 bytes self-serve; more only by allow-list |
| Idempotency key | 16 to 128 visible ASCII characters |
Purchase reference (ref) | 32 bytes |
| API keys per account | 10 |
relay_batch over MCP | 16 transactions |
| Page size | 1 to 200, default 50 |
Reading your own limits
curl -sS https://api.co-relayer.com/v1/pricing
curl -sS https://api.co-relayer.com/v1/account/$ME/quota
Rate-limited responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a 429
carries Retry-After and details.retryAfterMs.
With one exception, which is the most important line on this page:
QUOTA_EXHAUSTED is a 429 with no Retry-After, because waiting
does not help. Buying does.
When a limit is the wrong limit
If you are hitting a rate class repeatedly while your monthly cap is barely touched, the cap is not your constraint — the class is. Moving up a tier buys throughput as well as volume, and for bursty workloads that is usually the real reason to do it. (Tiers · Tiers for agents)