API Reference — Rate limits
The API is public and needs no key. Two things are metered so that one script cannot starve everyone else: how many requests an IP sends, and — more tightly — how many signed quotes it asks for.
Limits
| Scope | Limit | Key |
|---|---|---|
Every /api/* route | 600 requests / minute | client IP |
POST /api/quote | 10 quotes / minute | client IP |
POST /api/quote | 30 quotes / minute | sender address (canonical form) |
Limits are token buckets: the allowance refills continuously (600/min ≈ one request every 100 ms, with the full 600 available as a burst from idle). CORS preflight (OPTIONS) and the long-lived GET /api/orders/stream connection are not counted.
There is also a coarser per-IP cap at the edge (nginx), meant for floods: about 30 requests/s on /api/* with a burst of 20, and 2 requests/s on POST /api/quote with a burst of 5. It answers 429 with a non-JSON body and no Retry-After — pause for a second or two and retry.
What a rejection looks like
Every metered response carries the allowance of the last bucket checked: the per-IP bucket on most routes; on POST /api/quote with a sender in the body, the per-sender bucket (30) — the 10/min per-IP bucket only shows up in its own 429.
X-RateLimit-Scope: anonymous
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 29When you are over the limit you get 429 Too Many Requests:
HTTP/1.1 429 Too Many Requests
Retry-After: 6
Content-Type: application/json
{ "error": "RATE_LIMITED", "retryAfterSec": 6 }Retry-After (seconds, whole) is the earliest moment the next request will pass. Sleep for it, then retry — do not tight-loop; every rejected attempt is itself a request.
Quote once, pay once
A quote is valid for 180 s — you do not need to re-quote while a quote is still live, and you should not request a quote before you are ready to send the transaction. Batches (multisend) are one quote regardless of the recipient count, so 10 quotes/min is 10 swaps, not 10 recipients.
Running at volume?
If the anonymous allowance is too small for what you are building, reach out on Telegram or Discord.
Not rate limits
Two other 5xx answers look similar but mean something else:
| Status | Body | Meaning |
|---|---|---|
503 | { "error": "WARMING_UP", "retryAfterSec": 5, "waitingFor": [...] } | The backend just restarted and is loading prices / balances (/api/quotes, /api/prices, POST /api/quote). Retry after Retry-After. |
503 | { "error": "Quoting is disabled", "code": "SIGNING_DISABLED" } | Quoting is paused by the operator — see POST /api/quote. |
Summary of error shapes
| Status | Body | Meaning |
|---|---|---|
429 | { "error": "RATE_LIMITED", "retryAfterSec": n } | per-minute allowance exhausted (IP or sender) |
429 | (empty) | edge flood protection — back off harder |
503 | { "error": "WARMING_UP", "retryAfterSec": n, "waitingFor": [...] } | backend warming up — retry |