Every API is rate limited, the token endpoint included. This page gives the numbers, the headers that tell you where you stand, and what to do when you are refused.
The limits
| Policy | What it counts | Limit | Window |
|---|---|---|---|
burst |
Your calls to any product API | 60 | 1 minute |
daily |
Your calls to any product API | 5,000 | 1 UTC day |
broker_uploads |
Your uploads for one brokerage | 500 | 1 UTC day |
broker_daily |
Every integration's uploads for one brokerage, yours included | 2,000 | 1 UTC day |
token |
Exchanges at the token endpoint for your integration | 10 | 1 minute |
token_daily |
Exchanges at the token endpoint for your integration | 1,000 | 1 UTC day |
token_ip |
Exchanges at the token endpoint from one IP address | 30 | 1 minute |
unauthenticated |
Calls whose access token fails verification, from one IP address | 60 | 1 minute |
- The first three are yours, and are the defaults. One integration's limits can be raised without a
change to anything else: ask us. A raised limit shows in
RateLimit-Policyon the next response. - The token endpoint's limits are much tighter, because a correct integration exchanges about once an hour per brokerage. See Authentication.
broker_uploadsbounds what one brokerage can be sent in a day, so that one busy brokerage cannot use up the room for your others.- A day is a UTC day. The daily limits reset at 00:00 UTC, and the header that counts down counts down to that moment. It is not a rolling 24 hours.
- The per-minute limits are approximate. Each instance of our service counts them separately, so treat the number as the rate you are held to and not as a guarantee that you can reach it. The daily limits are exact.
The headers
Every response carries two headers, defined by the latest IETF draft, draft-ietf-httpapi-ratelimit-headers-11.
RateLimit-Policy lists every limit that applies to the request, by name, with its quota q and its window
w in seconds:
RateLimit-Policy: "burst";q=60;w=60, "daily";q=5000;w=86400
RateLimit says, for each of those limits, how many requests remain (r) and how many seconds remain until
the window resets (t):
RateLimit: "burst";r=41;t=23, "daily";r=4870;t=51300
- Both are HTTP structured fields (RFC 9651). Read them with a structured-field parser, not by splitting on commas. Parsers exist for the common languages.
- The names are the policy names in the table above, and are part of the contract.
- The fields list the limits we consulted before deciding the request. A request refused by one limit does not report the limits it never reached.
- These two headers are advisory. The draft is not yet an RFC, and if its final syntax differs from
what we send, we will announce the change. The
429status andRetry-After(RFC 9110) are what your retry logic must rely on, and they will not change.
When you are refused
A refused request is answered 429 Too Many Requests.
Retry-Afteris the number of seconds to wait. It is never earlier than the end of the window that refused you. Do not send the request again sooner.- The
reason(or, from the token endpoint, theerror) israte_limited_<policy>, naming the limit that refused you, such asrate_limited_burst. The status andRetry-Afterare what to branch on. - On repeated refusals, back off exponentially and add jitter, so that a fleet of workers does not retry
in step. Start from
Retry-Afterand grow from there. - A request refused by
dailyorbroker_uploadsis not counted against them, and nothing is stored, so waiting costs you nothing but time. - If you meet a limit you expect to need, look at
RateLimiton your ordinary responses first. It tells you how much is left before you hit it, and slowing down then is better than being refused.
{
"status": 429,
"reason": "rate_limited_burst",
"message": "The burst limit of 60 requests per 60 seconds has been reached. Retry after 37 seconds.",
"requestId": "2f4a5f8e-9d3c-4b6e-8f4a-0c5b1e7d2a90"
}