> For the complete documentation index, see [llms.txt](https://docs.reya.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.reya.xyz/developers/connectivity/rate-limits.md).

# Rate Limits

{% hint style="info" %}
**Production limits.**
{% endhint %}

Reya applies limits to account operations, resting orders, WebSocket messages and traffic from each IP address. These limits apply together: staying within an account's allowance does not exempt its requests from connection or IP limits.

## Account operation limits

Each `accountId` has independent allowances for four operation groups.

Standard is the default account tier. Contact the Reya team to arrange Market Maker enrollment and confirm your account's allowances. WebSocket Order Entry access is restricted to approved market-maker source IPs; provide your egress IPs during onboarding. Other clients use REST for order entry.

| Operation group   | Requests                     | Standard: operations/minute · burst | Market Maker: operations/minute · burst |
| ----------------- | ---------------------------- | ----------------------------------- | --------------------------------------- |
| Create and modify | `createOrder`, `modifyOrder` | 60 · 5                              | 25,000 · 100                            |
| Single cancel     | `cancelOrder`                | 120 · 10                            | 50,000 · 200                            |
| Bulk cancel       | `cancelAll`                  | 10 · 5                              | 100 · 50                                |
| Countdown control | `cancelAllAfter`             | 100 · 10                            | 100 · 10                                |

The rate is a sustained allowance that replenishes continuously. The burst is the number of operations you can send together when that allowance is fully available. It does not reset at a minute boundary, and unused allowance does not accumulate beyond the burst. For example, Standard create/modify allows five immediate operations after an idle period and replenishes at one operation per second.

Each request uses one operation in its group. `cancelAll` costs one bulk-cancel operation regardless of the number of orders it targets, including when there are no orders to cancel. A request that later fails validation, risk checks or an open-order cap, or is refused during a [matching pause](/developers/order-entry/order-lifecycle.md#matching-pauses), can still consume an operation; do not count only successful requests.

When an armed countdown expires, its automatic cancellation is not blocked by an exhausted bulk-cancel allowance. It consumes one bulk-cancel operation, which can delay a subsequent explicit `cancelAll`.

Disarming an active `cancelAllAfter` countdown consumes an operation but is allowed even when the countdown-control allowance is exhausted. Repeated disarms when no countdown is armed can be rate-limited.

A rate-limited refresh does not extend the countdown: continue tracking the last acknowledged `triggerAt`. See [Cancel-on-Disconnect](/developers/order-entry/cancel-on-disconnect.md).

A rate-limit rejection leaves the requested action unapplied. Use the returned `retryAfterMs` to schedule another attempt.

## Resting-order limits

Caps apply per account.

| Cap                               | Standard | Market Maker |
| --------------------------------- | -------- | ------------ |
| Resting orders across all markets | 200      | 1,500        |
| Resting orders in one market      | 50       | 300          |
| Combined resting-order notional   | $250,000 | $50,000,000  |

Notional is the sum of `remaining quantity × limit price` across the account's resting orders, counting bids and asks without netting them.

A new GTC/GTT limit order is checked using its full submitted quantity, even if it could immediately match. An IOC does not occupy resting-order capacity. A modify keeps the same order count but must fit the notional cap using its proposed quantity and price.

Armed SL/TP orders do not occupy these caps. Each account can have one stop-loss and one take-profit per market. A fired GTC/GTT child that rests contributes to the account's resting-order totals. Trigger creation and modification consume the create/modify allowance, but automatic firing does not require a new client request or consume that allowance.

## WebSocket message limits

Each connection has an inbound message allowance, independent of account-operation limits. It counts client application messages, including JSON pings, subscriptions and order requests, even if a message is invalid. Server-to-client market-data updates do not consume this allowance.

| Connection            | Sustained messages/second | Burst |
| --------------------- | ------------------------- | ----- |
| WebSocket Order Entry | 2,000                     | 4,000 |
| WebSocket Info        | 20                        | 1,200 |

If a connection exceeds its message allowance, it closes with code `4029` and a reason of the form `MSG_RATE_EXCEEDED retry_after_ms=<n>`. Wait at least the indicated milliseconds, reconnect with backoff and resume at a lower message rate. Pace subscription replay as well as order traffic. Protocol-level ping/pong control frames are separate from these application messages; see [Heartbeats](/developers/connectivity/heartbeats.md).

### Order Entry in-flight requests

Each WebSocket Order Entry connection can have at most **100 requests awaiting a response**. Every `createOrder`, `modifyOrder`, `cancelOrder`, `cancelAll` and `cancelAllAfter` request holds a slot from the moment it is received until its response is sent; a JSON `ping` does not.

A request that arrives while 100 are outstanding is rejected immediately with a top-level `error` envelope carrying `TOO_MANY_INFLIGHT` and the request's `id`. It is not sent to the matching engine and does not consume an account operation, and the connection stays open.

The cap applies to `cancelAll` as well, so a burst of individual cancels can delay a `cancelAll` sent behind it on the same connection. To clear many orders, send one `cancelAll` rather than cancelling them one by one, and keep fewer than 100 requests outstanding on each connection.

### Info subscription limits

These apply alongside the message limits above.

| Limit                    | Allowance             | Scope                             |
| ------------------------ | --------------------- | --------------------------------- |
| Subscription requests    | 10/second · burst 40  | Per connection                    |
| Subscription requests    | 40/second · burst 160 | Per IP, shared across connections |
| Active subscriptions     | 500                   | Per connection                    |
| Distinct wallet subjects | 10                    | Per IP, shared across connections |

A wallet counts once toward the distinct-wallet cap, even when subscribed to through multiple channels or connections. A subscription-rate rejection returns an error without closing the socket or sending the subscription's snapshot; retry with backoff.

## IP request and connection limits

The limits below are total allowances per public IP, shared across all of your clients and accounts using that IP. Account tiers do not increase these allowances. Order Entry and Info have separate simultaneous-connection caps but share one new-connection allowance across all three WebSocket hostnames.

| Traffic                              | Limit                 | Scope                                                                                                                 |
| ------------------------------------ | --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| REST order management                | 2,000 requests/minute | One shared bucket on `api.reya.xyz` for `createOrder`, `modifyOrder`, `cancelOrder`, `cancelAll` and `cancelAllAfter` |
| Other REST requests                  | 1,500 requests/minute | All other routes on `api.reya.xyz`, in a separate bucket                                                              |
| New WebSocket connections            | 300 attempts/minute   | One shared bucket across `ws-exec.reya.xyz`, `ws.reya.xyz` and `ws-gke.reya.xyz`                                      |
| Simultaneous Order Entry connections | 20 total              | Per IP                                                                                                                |
| Simultaneous Info connections        | 100 total             | Per IP                                                                                                                |

The REST and WebSocket-upgrade limits use 60-second counting windows. New-connection limits count HTTP upgrade attempts, including rejected handshakes, but do not count messages on an established socket.

Clients behind the same public IP share its limits, even when they trade different accounts. Reuse established connections, avoid unnecessary polling, and stagger reconnections across your clients. Closing an unused connection can free connection capacity; repeatedly attempting to reconnect does not.

For IPv6, Info shares its application IP allowances across addresses in the same /64. Order Entry and edge counters use the full source IP address.

## Error responses and recovery

Order-management rejections use **HTTP 400** on REST. The body's `error` field identifies the outcome; a retryable rejection is not necessarily HTTP 429. WebSocket Order Entry returns the same error object inside its normal `{ "ok": false, "error": ... }` response.

For example, an account-operation rate rejection can carry:

```json
{
  "error": "RATE_LIMITED_ERROR",
  "message": "Rate limit exceeded; retry after 250 ms",
  "retryAfterMs": 250
}
```

`retryAfterMs` is a positive duration in milliseconds when provided. Wait at least that long and coordinate retries across clients sharing the account. Other traffic can consume the replenished allowance, so the hint does not reserve the next operation. Order-management retry hints are carried in the body, not a `Retry-After` header.

| Error                                | Meaning                                                                       | Client action                                                                                      |
| ------------------------------------ | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `RATE_LIMITED_ERROR`                 | The account exhausted the allowance for this operation group                  | Respect `retryAfterMs` when present and reduce the sending rate; use backoff if no hint is present |
| `OPEN_ORDER_COUNT_EXCEEDED_ERROR`    | A new order would exceed the account-wide or per-market resting-order count   | Cancel orders or wait for them to fill; elapsed time alone does not release a slot                 |
| `OPEN_ORDER_NOTIONAL_EXCEEDED_ERROR` | A create or modify would exceed combined resting-order notional               | Reduce the proposed notional or release capacity through cancellations or fills                    |
| `CAPACITY_LIMITED_ERROR`             | New orders and modifications are temporarily restricted because of venue load | Back off with jitter; cancellation and countdown requests remain available under their own limits  |

The count, notional and capacity errors carry no `retryAfterMs`. Staying within your account limits does not guarantee acceptance during a capacity restriction, and a capacity rejection does not consume the create/modify allowance.

Retry only while the intended action is still appropriate. Sign a new request with a fresh nonce and a valid signature deadline; ensure any order expiry is still valid. Follow the [nonce rules](/developers/authentication/signatures-and-nonces.md#nonces). A transport timeout or disconnect is different from an explicit rejection: follow [Handling ambiguous outcomes](/developers/connectivity/handling-ambiguous-outcomes.md) and reconcile the original request before sending another one.

### IP-limit rejection and recovery

Exceeding an edge limit blocks **all requests in that rule's scope from the IP for 60 seconds**, returning **HTTP 429**, including during a WebSocket upgrade. In particular, exhausting the REST trading bucket also blocks REST cancellations and countdown refreshes. Separate account-level cancellation allowances do not bypass this block. Exhausting the shared WebSocket-upgrade allowance blocks new connections to all three WebSocket hostnames. These edge blocks do not close established sockets; their account and connection limits still apply.

The edge response has `error: "rate_limited"`, `scope: "ip"` and `retryAfterSeconds: 60`. It is separate from the order-management error contract and supplies **no `Retry-After` header**. Wait at least 60 seconds after an edge rejection, then retry with jitter and a lower request rate. The one-minute allowance can still be exhausted after the block expires, so increase backoff if rejected again. Browser clients can see a network error instead of a readable 429 because the block response has no CORS headers; if the outcome of an order request is unclear, [reconcile before retrying](/developers/connectivity/handling-ambiguous-outcomes.md).

For a simultaneous-connection refusal, reduce open connections from that IP and back off. A refusal can arrive as HTTP 429 during upgrade or as close code `4029` with `IP_CONN_LIMIT_EXCEEDED max_conns=<n>`. Unlike the timed edge block, this cap does not provide a time at which capacity is guaranteed to become free.
