Skip to main content
Rate limiting is two-tier. Every request first passes a per-wallet (or per-IP) request budget, and order-flow requests additionally pass a per-instrument order-rate limit. A request that exceeds either tier is rejected with a JSON-RPC error — nothing is queued. Read your live budgets at any time with public/getRateLimits.

Tier 1 — Request budget (per wallet)

Every method carries a rate-limit class (shown on each method’s page in the API Reference tab). The class selects which per-wallet budget the request decrements. Authenticated traffic is keyed by wallet; unauthenticated/public traffic is keyed by IP. Across the 100 JSON-RPC methods the classes are distributed:

non_matching

92 methods

matching

5 methods

endpoint

1 method

custom

2 methods
order, replace, cancel, cancel_by_instrument, cancel_by_nonce. All order-flow writes share one per-wallet matching budget, regardless of how many connections or session keys the wallet has open.
cancel_all — decrements its own per-(wallet, method) budget so a bulk cancel doesn’t consume the shared matching budget.
cancel_by_label and public/get_all_live_instruments opt out of the uniform dispatch and apply their own logic. cancel_by_label decrements the matching budget when scoped to a single instrument, otherwise a dedicated per-method budget; public/get_all_live_instruments is a public read with no per-request decrement, bounded only by the per-IP request and connection limits.
Budgets are wallet-scoped, not per-subaccount or per-session-key. All concurrent connections and session keys belonging to one wallet share a single budget. Public, pre-authentication traffic is limited per source IP, along with a cap on simultaneous WebSocket connections per IP.

Budget & window semantics

A request budget is a fixed window. The number of points available in each window is:
Every request costs one point; there is no per-method weight table. When the window elapses the budget resets. Because it is a fixed window (not a sliding one), a client can briefly emit up to two windows’ worth of requests across a window boundary.

Tier 2 — Per-instrument order-rate limit

Order-flow requests (order, replace, cancel, …) pass a second limiter: a per-instrument token bucket, keyed by (wallet, instrument).
  • The refill rate is a per-second TPS chosen by asset type — one value for perps and spot, another for options.
  • The bucket’s capacity (its burst allowance) is refill_rate × burst_multiplier.
  • Unlike the request budget’s fixed window, the bucket refills continuously, so it does not permit a window-boundary double-burst.
An order-placement request is limited twice: once by the matching request budget and once by the per-instrument bucket. A rejection can come from either tier, and both surface the same error code (see below), so a rejection alone does not tell you which tier tripped. The per-instrument limits are not reported by public/getRateLimits.

Concrete limits

All limits are deployment-specific and read from configuration at startup — code defaults, testnet, and mainnet differ. Treat the values below as a mainnet reference snapshot, not a contract, and always read your live budget with public/getRateLimits — the authoritative, live source for your budget.

Runtime introspection

public/getRateLimits returns your live request budgets. It is itself a non_matching method, so calling it costs one non-matching point. It takes no parameters.
object
required
Live state of the per-wallet matching budget.
object
required
Live state of the per-wallet non-matching budget.
map<string, object>
required
Per-method budgets, keyed by method name (e.g. cancel_all).
object
Live state of the per-IP concurrent-connection limiter. Present on WebSocket only.
Each budget object carries:
integer
required
Points (tokens) left in the current window.
integer
required
Milliseconds until the next request is allowed; 0 when a request may go through now.
integer
required
Points consumed in the current window.
boolean
required
Whether this is the first request in the current window.
getRateLimits exposes only the request-budget tier. The per-instrument token buckets are not reported, so perp/option instrument limits cannot be discovered at runtime.

Rejection error codes

When a request exceeds a limit it is rejected — never queued — with a JSON-RPC error. Codes -32000 through -32099 are reserved for rate-limit errors. See Error codes for the full catalogue.
Rejection
Back off using msBeforeNext from getRateLimits or the Retry after {ms}{" "} ms hint on a -32000 rejection. Because order flow is limited by both the matching request budget and the per-instrument bucket, spread order flow across instruments and keep a margin below your reported budget.