API documentation

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-Reset and Retry-After are 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:

  1. Watch X-RateLimit-Remaining. When it approaches 0, pause until X-RateLimit-Reset.
  2. On 429, honor Retry-After — wait that many seconds, then retry.
  3. 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 /orders gives 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 ≤ 1 is cheaper than eating a 429 and its Retry-After.

Verified against API spec v2.0.0 · 2026-07-11

Cookie Preferences

Manage your cookie preferences below. Necessary cookies are always active as they are essential for the website to function properly.

Strictly Necessary

Essential for the website to function. These cookies cannot be disabled.

Analytics

Help us understand how visitors interact with our website by collecting and reporting information anonymously.

Marketing

Used to track visitors across websites to display relevant advertisements.

Preferences

Allow the website to remember choices you make, such as language or region.