Rate limits
Per-second budgets, request weights, burst behaviour, daily caps and the X-RateLimit headers.
Limits are enforced per second on a sliding window, in two kinds of bucket: an anonymous per-IP bucket for unauthenticated market data, and a per-API-key bucket for everything signed. Your VIP tier sets the numbers; market makers get raised limits.
Budgets
Values below are tier 0 — the floor every new key starts at, and the fallback the gateway applies if the tier table is briefly unreadable.
| Bucket | Tier-0 limit | Burst (×1.5) |
|---|---|---|
| Read (GET) | 50 /s | 75 |
| Order placement | 10 /s | 15 |
| Cancel | 20 /s | 30 |
| Modify | 10 /s | 15 |
| Batch | 3 /s | 4 |
| Advanced orders | 5 /s | 7 |
| WebSocket messages | 50 /s | 75 |
| Ceiling | Tier 0 |
|---|---|
| WebSocket connections | 3 |
| WebSocket subscriptions | 50 |
| Requests per day | 50,000 |
| Orders per day | 1,000 |
The window is one second. The burst multiplier is applied to the base limit and the burst number is what is actually enforced — it is also what the X-RateLimit-* headers report, so headers and enforcement never disagree.
Weights
Requests cost more than one slot when they do more than one thing:
| Class | Weight |
|---|---|
| Read, order, cancel, modify | 1 |
| Advanced order (TWAP, VWAP, bracket, …) | 2 |
| Batch (multi-order place, batch cancel, position sweeps) | 3 |
So a tier-0 key gets ~15 order placements per second, but only one batch call per second-window (weight 3 against a burst budget of 4). Sizing a batch to 20 legs is the intended use — spraying 20 single orders costs the same budget and more latency.
Headers
Every response carries the state of the bucket it was charged to:
x-ratelimit-limit: 75 # enforced burst limit for this bucket
x-ratelimit-remaining: 74 # slots left in the current window
x-ratelimit-reset: 1790094907 # unix seconds when the window rolls
x-ratelimit-used: 1 # weight consumed so far in this windowDrive your pacing off remaining rather than counting locally — the same key shares buckets across every process using it.
Rejections
| Code | Num | HTTP | Meaning |
|---|---|---|---|
RATE_LIMIT_IP | 19001 | 429 | anonymous per-IP bucket exhausted |
RATE_LIMIT_KEY | 19002 | 429 | per-key general bucket exhausted |
RATE_LIMIT_ORDER | 19003 | 429 | order-placement bucket exhausted |
RATE_LIMIT_CANCEL | 19004 | 429 | cancel bucket exhausted |
RATE_LIMIT_WS | 19010 | 429 | WebSocket message rate, connection or subscription ceiling |
RATE_LIMIT_FAUCET | 19005 | 429 | testnet faucet cooldown |
A daily cap is a different failure: it returns 403, not 429, and no amount of backoff clears it before the UTC day rolls.
Practical pacing
- Back off on the bucket that rejected you, not globally — a 429 on orders says nothing about your read budget.
- Prefer one WebSocket subscription over polling a ticker; the 50 /s read budget disappears fast across a handful of markets, and the socket is fresher.
- Cancels are cheaper than placements by design (20 /s vs 10 /s at tier 0). Cancel-replace loops should not need a raised tier.
- If you are systematically hitting ceilings at tier 0, the fix is a tier change (VIP level or market-maker flag), not more retries.
GET /api/v1/system/inforeports the limits your key is actually running under.
Updated 9 days ago
