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.
remaining_matching
object
required
Live state of the per-wallet matching budget.
remaining_non_matching
object
required
Live state of the per-wallet non-matching budget.
remaining_per_endpoint
map<string, object>
required
Per-method budgets, keyed by method name (e.g. cancel_all).
remaining_connections
object
Live state of the per-IP concurrent-connection limiter. Present on WebSocket only.
Each budget object carries:
remainingPoints
integer
required
Points (tokens) left in the current window.
msBeforeNext
integer
required
Milliseconds until the next request is allowed; 0 when a request may go through now.
consumedPoints
integer
required
Points consumed in the current window.
isFirstInDuration
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.