Rate limits
Each API key has its own request budget. Limits are returned in the key’s /me response and enforced per minute on a rolling window.
Default limits
Section titled “Default limits”| Endpoint group | Requests per minute |
|---|---|
Read endpoints (GET) | 300 |
Write endpoints (POST, PATCH, PUT, DELETE) | 30 |
Higher limits may be granted on request — contact your account manager.
Headers
Section titled “Headers”Every response carries the current state of your budget:
HTTP/1.1 200 OKX-RateLimit-Limit: 300X-RateLimit-Remaining: 287X-RateLimit-Reset: 1748340060| Header | Meaning |
|---|---|
X-RateLimit-Limit | Maximum requests in the current window. |
X-RateLimit-Remaining | Requests left before the window resets. |
X-RateLimit-Reset | Unix epoch (seconds) when the window resets. |
When the budget is exhausted the API returns 429 Too Many Requests with a Retry-After header:
HTTP/1.1 429 Too Many RequestsRetry-After: 23Content-Type: application/json
{ "error": { "code": "rate_limited", "message": "Too many requests" } }Retry-After is the number of seconds until the next request will succeed. Sleep at least that long before retrying.
Recommended retry pattern
Section titled “Recommended retry pattern”async function withRetry(fn, { maxAttempts = 5 } = {}) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { const res = await fn(); if (res.status !== 429) return res; const retryAfter = Number(res.headers.get('retry-after') ?? 1); const backoff = Math.min(retryAfter, 2 ** attempt); await new Promise((r) => setTimeout(r, backoff * 1000)); } throw new Error('rate-limit retry budget exhausted');}The same shape works in Python:
import time, httpx
def with_retry(call, max_attempts=5): for attempt in range(1, max_attempts + 1): res = call() if res.status_code != 429: return res retry_after = int(res.headers.get('retry-after', '1')) time.sleep(min(retry_after, 2 ** attempt)) raise RuntimeError('rate-limit retry budget exhausted')Monitoring your usage
Section titled “Monitoring your usage”Call GET /api/v1/me to inspect your key’s configured rateLimit:
{ "rateLimit": { "limit": 300, "remaining": 287, "reset": 1748340060 }}This is the same data as the response headers, useful for dashboards that don’t make a real request per check.