Rate limits
The API counts your requests (not “weight”) per route in a sliding window. Every
response tells you exactly where you stand via the X-RateLimit-* headers — a well-behaved
client reads them and never actually hits a 429.
Route counters are tracked per account (not per individual key).
The limits
Base limits:
| Route | Requests | Window |
|---|---|---|
POST /orders |
10 | 5 s |
DELETE /orders/{id} |
50 | 5 s |
GET /orders |
5 | 5 s |
GET /orders/trades |
5 | 5 s |
GET /portfolio |
5 | 5 s |
GET /portfolio/positions |
5 | 5 s |
POST /portfolio/position/leverage |
2 | 5 s |
GET /portfolio/margin-mode |
5 | 5 s |
POST /portfolio/margin-mode |
2 | 5 s |
POST /portfolio/position/margin-type |
2 | 5 s |
GET /exchange/hedge-mode |
5 | 5 s |
POST /exchange/hedge-mode |
2 | 5 s |
GET /ping |
10 | 5 s |
| Default — every other route | 5 | 5 s |
Two extra layers to know about:
- A coarse pre-auth limiter of 100 requests / 5 s per IP applies to every request,
including the unauthenticated
/server/*endpoints. - The WebSocket has its own DoS backstops (200 messages / 10 s per socket, connection caps) that are not scaled by subscription — see WebSocket.
💡 Batch beats loops. The bot batch endpoints (
/bots/batch,/bots/batch/runs) operate on up to 20 bots in one request — one unit of rate limit instead of twenty.
Rate limits are separate from the API-key permission tier (read / trade / full) —
that tier controls what a key may do, not how often.
Reading the headers
Every response carries the current counters:
X-RateLimit-Limit: 10 ← allowance for this route in the current window
X-RateLimit-Remaining: 7 ← requests you still have
X-RateLimit-Reset: 1743360065 ← when the window resets — Unix SECONDS
⚠️
X-RateLimit-ResetandRetry-Afterare the only seconds-based values in the API; everything else is milliseconds. See Conventions.
When you do overflow, the response is:
HTTP/1.1 429 Too Many Requests
Retry-After: 3
{
"error": {
"code": "RATE_LIMITED",
"status": 429,
"message": "Rate limit exceeded. Retry after the period specified in Retry-After header."
},
"meta": { "requestId": "550e8400-e29b-41d4-a716-446655440005" }
}
Self-throttling done right
Three rules cover 99% of cases:
- Watch
X-RateLimit-Remaining. When it approaches 0, pause untilX-RateLimit-Reset. - On
429, honorRetry-After— wait that many seconds, then retry. - Every retry must be re-signed. A retried request needs a fresh timestamp and
signature — replaying the identical signature is rejected as a duplicate
(why). If the retried call is
mutating, also reuse the same
X-Idempotency-Key(why).
With the reference client — which re-signs
on every call and exposes retryAfter on errors — a correct retry wrapper is tiny:
// JavaScript
async function withRetry(fn, attempts = 3) {
for (let i = 1; ; i++) {
try { return await fn(); }
catch (e) {
if (e.status === 429 && i < attempts) {
await new Promise(r => setTimeout(r, (e.retryAfter ?? 5) * 1000));
continue; // fn() re-signs: fresh timestamp + signature
}
throw e;
}
}
}
const { data } = await withRetry(() => lexx.request("GET", "/orders"));
# Python
import time
def with_retry(call, attempts=3):
for i in range(1, attempts + 1):
try:
return call()
except LexxError as e:
if e.status == 429 and i < attempts:
time.sleep(e.retry_after or 5) # call() re-signs on the next attempt
continue
raise
data = with_retry(lambda: lexx.request("GET", "/orders"))
Design tips for trading bots
- Prefer WebSocket for state. Order, balance and position changes arrive on the WebSocket — poll REST only to bootstrap and reconcile, not as your main loop.
- Budget the hot path.
POST /ordersgives 10 per 5 s — a strategy that bursts orders should queue them client-side and drain within budget. - Slow down before the wall. Throttling at
Remaining ≤ 1is cheaper than eating a429and itsRetry-After.
Verified against API spec v2.0.0 · 2026-07-11