Usage limits
Two layers protect the API: a per-source-IP guard in front of every request, and a per-organization, per-endpoint quota. Both expose their state in standard headers: read them and your client paces itself.
5 min read
The two-layer model
| Layer | Scope | Limit | Code when exceeded |
|---|---|---|---|
| Per-IP guard | Per source IP | 35,000 requests per minute | RATE_DDOS_EXCEEDED |
| Per-endpoint quota | Per organization and per endpoint | Specific to each endpoint (ping allows 20 per minute) | RATE_TPS_EXCEEDED |
The per-IP guard runs first: it is a safety floor, not a commercial commitment. Hitting it usually means a misconfigured shared IP or a clear abuse pattern; the threshold is generous and legitimate traffic does not get close. The per-endpoint quota is the one you will see in normal operation, and each endpoint states its own in the reference.
The two layers count differently: the IP layer adds up every request coming from the same IP, no matter the endpoint; the quota layer adds up requests to a single endpoint. Rotating endpoints does not multiply the IP budget, and rotating IPs does not multiply the endpoint quota.
Sliding window, not fixed buckets
Both layers use a two-bucket weighted sliding window, the same technique Cloudflare and Stripe use. The intuition:
- The current 60-second bucket counts at full weight.
- The previous bucket counts with a weight that decays from 1.0 (start of the current bucket) to 0.0 (end of it).
- The effective total is
current + previous × weight.
With fixed buckets, a client could send the full limit at second 59 and the same again at second 0 of the next bucket: double the limit in two seconds. The sliding window catches that.
Response headers
The API follows RFC 9598 (RateLimit Header Fields for HTTP). Every quota-protected response carries:
RateLimit-Limit: 20
RateLimit-Remaining: 18
RateLimit-Reset: 31
RateLimit-Policy: 20;w=60;name="endpoint"RateLimit-Limit- Quota for the active window.
RateLimit-Remaining- Requests remaining in the active window.
RateLimit-Reset- Seconds until the active window resets.
RateLimit-Policy- Declarative quota in a parseable format.
20;w=60means 20 requests in a 60-second sliding window.
On a 429 you also get Retry-After in seconds: wait at least that long before retrying.
Recommended strategy
- 01Throttle preemptively
Read
RateLimit-Remainingon every response. When it drops below 20 % ofRateLimit-Limit, slow down so you reach the next reset with room to spare. - 02Respect Retry-After
When you get a 429, wait at least the seconds it reports before the next attempt. Retrying immediately just re-triggers the counter.
- 03Exponential backoff with jitter
If retries keep failing, add an exponential delay (for example
2^n × 100 ms) with ±50 % random jitter. The jitter keeps thousands of clients from syncing on the same second.
import time, random
def call_with_backoff(client, request, max_retries=5):
for attempt in range(max_retries):
r = client.send(request)
if r.status_code != 429:
return r
wait = int(r.headers.get("Retry-After", "1"))
# retroceso exponencial con techo en Retry-After * 4, mas variacion
delay = min(wait * (2 ** attempt), wait * 4)
delay += random.uniform(0, delay * 0.5)
time.sleep(delay)
return r # quien llama decide que hacer al agotar los intentosIf you write your own client, parsing the policy is straightforward:
// RateLimit-Policy: 20;w=60;name="endpoint"
// -> limite 20, ventana 60 s, nombre "endpoint"
const [limit, ...params] = policy.split(';')
const window = Number(params.find((p) => p.startsWith('w='))?.slice(2))What you see when it fires
Both layers return HTTP 429 with the standard envelope. The error.code tells you which one fired:
RateLimit-Limit: 20
RateLimit-Remaining: 0
RateLimit-Reset: 42
Retry-After: 42
{
"success": false,
"error": {
"code": "RATE_TPS_EXCEEDED",
"message": "You have exceeded the allowed request rate for this endpoint."
}
}Asking for a higher limit
If your traffic pattern legitimately exceeds an endpoint quota (bulk imports, campaigns, batch onboarding), talk to your account manager. We can raise the limit for your organization on a specific endpoint without touching the per-IP safety floor. Have this ready:
- Expected peak and total daily volume.
- Whether the peak is one-off (a launch) or sustained.
- How you back off when limited, so we know the increase does not just move the problem.