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.

ClassEndpointsPer keyPer account
market10 per second, bursts of 2020 per second
read5 per second, bursts of 1010 per second
quote1 per second; 30 per minute60 per minute
trade10 per minute10 per minute; 500 per day
giftcard5 per hour5 per hour
withdrawal10 per hour10 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-Policy lists the buckets: q requests per window of w seconds.
  • RateLimit gives, for each bucket, r requests left and t seconds until it's full again.
  • Retry-After on a 429 or 503 is the number of seconds to wait.

For the read class, right after a first request:

Response headers
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=1

Suspension

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. Wait Retry-After seconds, then retry with exponential backoff and jitter. The SDKs do this, and never retry key_suspended.
  • Cap your concurrency. Run at most 5 requests at a time, with a queue or a pool, instead of Promise.all over a long list.
  • Use the bulk price endpoint. GET/v1/prices returns 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/assets changes 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() and sell() 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 401 doesn't get better by retrying, and repeated ones block your IP address.