Get started
Rate limits
Limits per key and per account keep the API fast for everyone. Every response tells you where you stand, and a well-behaved client never notices them.
How limits work
Each endpoint belongs to a class. Each class has token buckets for the key and for the whole account: a request takes one token from each, and tokens refill at a steady rate up to the bucket's burst size. The account buckets mean that five keys on one account don't get five times the limit.
| Class | Endpoints | Per key | Per account |
|---|---|---|---|
market | 10 per second, bursts of 20 | 20 per second | |
read | 5 per second, bursts of 10 | 10 per second | |
quote | 1 per second; 30 per minute | 60 per minute | |
trade | 10 per minute | 10 per minute; 500 per day | |
giftcard | 5 per hour | 5 per hour | |
withdrawal | 10 per hour | 10 per hour; 20 per day |
GET /v1/status isn't limited per key. GET /v1/limits returns these numbers too, under rate_limits.
Concurrency
At most 5 requests per key and 10 per account may be in flight at once. A request over the cap gets 429 too_many_concurrent_requests with Retry-After: 1, and the rate-limit token it took is given back.
Only one trade runs at a time for an account: a second POST /v1/trades while one is still running also gets 429 too_many_concurrent_requests.
Headers
Every signed response says which buckets apply and how much is left, in the format of the IETF RateLimit header fields draft:
RateLimit-Policylists the buckets:qrequests per window ofwseconds.RateLimitgives, for each bucket,rrequests left andtseconds until it's full again.Retry-Afteron a429or503is the number of seconds to wait.
For the read class, right after a first request:
RateLimit-Policy: "key-read-1s";q=10;w=2, "account-read-1s";q=10;w=1
RateLimit: "key-read-1s";r=9;t=1, "account-read-1s";r=9;t=1Suspension
A key that keeps hitting its limits is suspended for 15 minutes. That happens when, within 10 minutes, the key made at least 100 requests, more than 50 % of them were rate limited, and they were limited in at least 3 different minutes. A single burst that the buckets absorb never suspends a key; a loop that ignores 429 does.
While suspended, every request gets 429 key_suspended, and the account owner gets an email. The third suspension within 24 hours keeps the key suspended until the owner turns it back on under Settings → API.
Separately, 20 failed authentications from one IP address within 5 minutes block that address for 15 minutes.
Business limits
Rate limits protect the service. Business limits are about money, and they live in the account:
- the smallest and largest trade, in USDC;
- the daily volume and number of trades made through the API, per account (UTC day);
- gift cards per day and per 30 days, their value per 30 days, and the largest single card.
GET /v1/limits shows the current values and how much you've used. Going over one is a 422 (amount_below_minimum, amount_above_maximum or limit_exceeded), never a 429.
Staying within the limits
- Back off on
429. WaitRetry-Afterseconds, then retry with exponential backoff and jitter. The SDKs do this, and never retrykey_suspended. - Cap your concurrency. Run at most 5 requests at a time, with a queue or a pool, instead of
Promise.allover a long list. - Use the bulk price endpoint. GET
/v1/pricesreturns every asset in one call and is cached for a second. Polling it faster than once a second, or asking for each asset separately, gains nothing. - Cache assets. GET
/v1/assetschanges rarely and is cached for 30 seconds. - Poll pending trades gently. After a
202, check GET/v1/trades/{id}every second or two, backing off, rather than in a tight loop. The SDKs'buy()andsell()helpers do this for up to 30 seconds. - Page with cursors and filters instead of downloading your whole history again.
- Fix the clock, not the retry loop. A
401doesn't get better by retrying, and repeated ones block your IP address.