WS Connection Limits
WebSocket connections for Options and Perpetuals are served by the same gateway and share the same per-account allowances. A limit is keyed on your account only: the product, the contract filter and the client IP play no part.WebSocket Connection Rate Limit
New connections are metered per account. Exceeding the allowance rejects the handshake with429 Too Many Requests, a retry-after-ms header giving the wait in milliseconds, a
Retry-After header giving it in whole seconds, rounded up, and a body carrying
TOO_MANY_REQUESTS (0003):
Concurrent WebSocket Connection Limits
The number of connections you may hold open at the same time is capped per account. Every connection counts once, whether it is scoped to a single contract, to an expiration, or opened withmarket=ALL. When the cap is reached, further handshakes are rejected like a
rate-limited one, but with retry-after-ms: 0: the cap clears when you close a connection, not
with time.
Message and Subscription Limits
Each connection is also limited in how many messages it may send. Every inbound frame is charged, whatever the request type. A frame over the limit is not processed and is answered with a frame that carries noresultType, where retryAfterSec is the wait in seconds; the connection
stays open:
subscriptions query parameter over the limit rejects the
handshake with 429 Too Many Requests and a body shaped like a rate-limited handshake’s,
carrying the SUBSCRIPTION_LIMIT error (0012) and the message Too Many Requests: subscription limit is <max>. A subscribe request over the limit is answered with statusCode: 429,
body: "Subscription limit reached (max N)" and the same error, and nothing is subscribed.
Order Creation and Operation Rate Limits
Account-Level Rate Limit
Requests are metered per account across all contracts and both transports, regardless of API keys. Each class of operation has its own budget, so exhausting one does not throttle the others. Thekey column names the budget as it is reported back to you when a request is refused.
Every request costs one unit from its class’s budget. Order creation, modification and single
cancellation are charged by the matching engine when the request reaches it, so a request refused
earlier, for a malformed body or an expired token for instance, costs nothing.
Two classes exist so that a caller who has spent the ordinary budgets can still close out:
- Cancel all costs one unit of its own budget per call, however many orders it cancels, and
the cancels it expands into cost nothing. On a
cancelAllOrdersrequest the engine charges the sweep before expanding it, so a refused sweep cancels nothing. - Reduce-only orders draw on their own allowance instead of the creation or modification budget. A modification cannot set the flag: it inherits the resting order’s, so modifying an ordinary order draws on the standard modification budget.
These are the defaults; a different allowance may be configured for your account.
Market data is not metered: Get Expirations,
Get Contracts and the public WebSocket streams are
cached, shared data.
Maximum Open Orders Per Contract
The number of orders you may have open at once on one contract is capped:
These are the defaults; a different cap may be configured for your account.
Important details:
- Checked on order creation and modification, never on cancellation
- Every order resting on the contract counts, as does every order you have submitted for it that the engine has not yet applied; an order queued for cancellation counts until it leaves the book
- Applies to limit and market orders combined
- Exceeding this limit rejects the order with
CREATE_ORDER_MAX_OPEN_ORDERS(10103), orMODIFY_ORDER_MAX_OPEN_ORDERS(10303) for a modification; see Error Codes - Use Get Open Orders to monitor your current count.
Follow
nextPageTokento count every page; each page consumes oneoptions-read-ordersrequest.
In-Flight and Timing Limits
Independently of the per-account budgets, the matching engine bounds how much work it holds at once. These limits are fixed rather than configured per account:
A request refused for one of these is reported on the
Order Management Stream as
TOO_MANY_INFLIGHT_ORDERS (10007), ENGINE_OVERLOADED (10008) or
RECEIVE_WINDOW_EXCEEDED (10005) (see Error Codes) and has no effect: a refused cancel leaves the
order resting, and a refused sweep cancels nothing. The receive window is measured inside the exchange, from the moment your request was queued, so it
does not depend on your clock. Room in the in-flight limits returns as the engine settles earlier
requests, so wait for their outcomes before resubmitting.
Handling Rate Limit Violations
Where a refusal is reported depends on where the budget is charged. Over HTTP, Cancel All Orders and the order and account read endpoints answer429 Too Many Requests when their budget is spent, with a Retry-After header
in whole seconds and a body carrying TOO_MANY_REQUESTS (0003) and naming the budget:
429: they
answer 202 as soon as the request is queued, and the matching engine decides afterwards. A
request the engine refuses for rate limiting is reported on the
Order Management Stream like any other
engine rejection, so hold a connection to that stream when you submit orders over HTTP.
Over WebSocket, a refused request is answered on the
Order Management Stream with
statusCode: 400 and the RATE_LIMITED error (10006); see
Error Codes for the shape. The WebSocket rejection carries no retry hint. Back off from that class of operation and resume
at a steady rate; room returns gradually as the sliding windows move on, not all at once. Other
classes are unaffected, so a refused create does not stop you cancelling.
A rejection is delivered on a best-effort basis: under heavy load the engine may drop the message
rather than delay order processing. Treat a request with no outcome as unknown and check
Get Open Orders before resubmitting; see
FAQs for safe-retry guidance.
Repeated violations or failure to respect rate limits may result in a ban.
Bans
There is no automatic soft ban on Options: a refused request is not followed by a disconnection or a timed lockout, and a rate-limited connection stays open. Repeated or abusive violations may lead to a ban applied to your account, under which every request in the banned class is refused as above until it is lifted. The cancel-all and reduce-only budgets are separate, so a ban on order creation does not prevent you from closing out.Monitoring Rate Limit Usage
The Options APIs do not report your current usage: there is no quota field on WebSocket acknowledgments and no quota header on HTTP responses. The only signals are the429 body’s
remaining and retry_after_seconds on HTTP, once a request has already been refused, and the
RATE_LIMITED rejection on the Order Management Stream.
Meter each operation class on your side and stay below your allowance. If you receive a refusal,
back off immediately and wait for Retry-After before sending further requests in that class.
Network Rate Limits
API Rate Limit
All HTTP endpoints, whether they are metered per account or not, sit behind a network-level limit shared by every user of one IP address:
A client over this limit is blocked at the edge with
403 Forbidden until its request count
falls back under the window. This limit is separate from the per-account budgets above and is
applied before authentication, so refused requests do not reach the API.
We enforce fair usage of the HTTP endpoints. If we detect abnormal spamming of the API, we
might ban the user.