Rate limits
Status:
stableCategory:referenceAdded:2026-04-20Last reviewed:2026-06-04
Summary
Rate limits are yours to set. From the developer portal you cap how fast your integration may call the API — across your whole account, a single key, or an individual route — so a runaway loop or a noisy deploy can never burn through more than you intended. Set a number, leave a field blank for no cap, and the API enforces it for you.
When a request would exceed one of your caps it returns
429 RateLimited, and the response tells you exactly which cap you hit
and when it clears.
How limits work
You configure limits in the portal at three levels, and each level takes a value for any of six time windows — per minute, hour, day, week, month, or year:
| Level | Applies to |
|---|---|
| Account-wide | Every key on your account, combined. |
| Per key | One key on its own. |
| Per route | One endpoint, for a chosen key. A route cap sits on top of that key's caps. |
A request is checked against every cap that applies to it, and the tightest one wins. If your key allows 10 requests/minute but you've set a single route to 1/minute, the second call to that route in a minute is limited even though the key still has room. Any window you leave blank simply isn't enforced.
Because you set these values, you always know your limits — there's no hidden number to discover. Adjust them anytime in the portal; changes take effect within about a minute.
When you hit a limit
The request returns 429 Too Many Requests with headers describing the
cap you hit:
| Header | Meaning |
|---|---|
Retry-After | Seconds to wait before retrying. |
X-RateLimit-Scope | Which limit you hit: version (a single key) or consumer (your whole account). |
X-RateLimit-Window | The window that was exceeded: minute, hour, day, week, month, or year. |
X-RateLimit-Limit | The cap for that scope and window. |
X-RateLimit-Remaining | Requests left in the window — 0 on a 429. |
X-RateLimit-Reset | Unix epoch (seconds) when the window resets. |
A real example — a key capped at 10 requests/minute, on the 11th call within the minute:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 41
X-RateLimit-Scope: version
X-RateLimit-Window: minute
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1780545600
The same shape appears for any cap. A per-route hourly cap reads
X-RateLimit-Window: hour with that route's limit; an account-wide cap
reads X-RateLimit-Scope: consumer. The headers only appear on a 429.
The 429 body
The body follows the standard error envelope (see errors.md):
{
"timestamp": "2026-06-04T03:16:44.034Z",
"error": "RateLimited",
"message": "Too many requests",
"request_id": "ce9ea793-1759-4cad-ab1b-fbc11475df3a",
"retry_after": 16
}
retry_after in the body and the Retry-After header always carry the
same value. Read either.
When a limit resets
Windows are aligned to the calendar and reset on their own schedule —
the minute window at the top of each minute, the day at 00:00 UTC, the
month on the first, and so on. They reset independently: exhausting
your hourly cap doesn't touch the daily one, and a minute clearing
doesn't help if the hour you're in is still over.
That independence is easy to see in practice. Hammer a key past its
per-minute cap and you get 429s until the next minute — then the very
next request succeeds:
requests 1–10 -> 200
request 11 -> 429 X-RateLimit-Window: minute Retry-After: 13
... wait for the minute to roll over ...
next request -> 200
You never have to clear anything or contact us — once the window the cap belongs to rolls over, you're through again.
Recommended client behavior
- Honor
Retry-After. Don't retry before the indicated delay. Retrying early just reconfirms the limit and pushes your recovery out by another window. - Back off on repeated
429s. If a retry afterRetry-Afteris still limited, double your wait and add jitter:wait = Retry-After × 2^attempts + random(0, jitter). - Cap the backoff. Don't grow the wait past a minute — at that point, surface the limit to your caller instead of looping.
- Pace bursts. If you have a batch to send, spread it out to stay under the caps you set rather than firing everything at once. If you regularly bump a cap, raise it in the portal.
- Don't retry
400,401,404,413,415,422. These aren't rate-limit errors; retrying without changing the request wastes calls against your window.
Example retry loop (Python)
import os
import time
import random
import requests
def call_engine(path: str, body: dict, max_attempts: int = 5) -> dict:
attempt = 0
while True:
attempt += 1
resp = requests.post(
f"https://engine.tripod-health.com{path}",
headers={
"apikey": os.environ["ENGINE_API_KEY"],
"Content-Type": "application/json",
},
json=body,
timeout=30,
)
if resp.status_code != 429:
resp.raise_for_status()
return resp.json()
if attempt >= max_attempts:
resp.raise_for_status() # surface the 429
wait = int(resp.headers.get("Retry-After", "1"))
wait = wait * (2 ** (attempt - 1)) + random.uniform(0, 0.5)
time.sleep(min(wait, 60))
What counts against a limit
Every authenticated request counts — including 400, 404, and 5xx
responses. A 400 ValidationError still consumes the window, so fix
validation errors client-side before retrying.
What does not count:
- Requests rejected with
401before a key is accepted. - The unauthenticated
GET /healthliveness probe.
Related
- authentication.md — keys and how to send them.
- errors.md — full error taxonomy, including
RateLimited. - credits.md — credit metering and budgets; the contrasting "only successful calls are billed" rule.
- conventions.md — response-shape rules.
Change history
| Date | Change |
|---|---|
| 2026-04-20 | Initial publication. |
| 2026-06-04 | Documented configurable account / key / route limits, the six time windows, and the X-RateLimit-* headers. |