Rate limits & quotas
The Ottili AI API applies two related protections: rate limits* (a short-window throttle that protects shared
infrastructure) and quotas* (the plan-scoped allowance your company can consume per period). Both are returned on
every response so your client can self-regulate instead of guessing.
Rate-limit headers
Every response — successful or throttled — includes standard rate-limit headers:
RateLimit-Limit: 600
RateLimit-Remaining: 597
RateLimit-Reset: 1752364800| Header | Description |
|---|---|
RateLimit-Limit | Max requests allowed in the current window for your key/company. |
RateLimit-Remaining | Requests left in the current window. |
RateLimit-Reset | Unix timestamp (seconds) when the current window resets. |
The exact numeric ceiling depends on your plan and API key scopes. Always read the live values from these headers rather than hardcoding them — they may change as plans evolve.
Quotas and free vs paid usage
Your company's quota* is the total allowance your plan grants for a period (for example, requests per day and the
AI credit pool). How the two relate:
- Free / included allowance* — every plan ships an included monthly quota (request quota + included AI credits). It
resets at the start of each billing period and unused included amounts do not roll over.
- Paid plans* — raise both the request quota and the included credit pool, and unlock higher rate-limit ceilings.
- Credits* — AI requests are metered against the shared company credit wallet. See
[Credits & usage](/docs/ottili-ai-api-credits). When the credit balance or monthly budget is exhausted, generation
stops with a clear insufficient_balance (HTTP 402) or rate_limit_exceeded
(HTTP 429) error instead of over-spending.
The live numbers for your company are always shown in Settings → Billing* in the dashboard and in the response
headers above. Do not poll the generation endpoint to discover your limits — watch the headers and react to limit
errors.
When you are limited
When a rate limit is exceeded the API responds with HTTP 429 Too Many Requests, a Retry-After header telling you
how many seconds to wait, and the RateLimit-* headers reflecting the reset:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 1752364860{
"detail": {
"code": "rate_limit_exceeded",
"message": "Too many AI API requests. Please retry after the indicated delay."
},
"code": "HTTP_429",
"request_id": "b1c2d3e4-0000-1111-2222-333344445555"
}The request_id is present on every response and is the handle to share with support (see
[Errors → Support](/docs/ottili-ai-api-errors)).
Backing off (retries)
Honour Retry-After exactly, then add a small jitter so many clients do not retry in lockstep. Exponential backoff
without Retry-After is a safe fallback:
import time, requests
def complete_with_retry(url, headers, body, max_retries=4):
for attempt in range(max_retries):
r = requests.post(url, headers=headers, json=body)
if r.status_code != 429:
return r
wait = int(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
raise RuntimeError("Rate limited after retries")Authenticated, scoped requests receive higher ceilings than anonymous traffic. Cache static metadata and model lists
(GET /v1/models) to stay well under the limit, and send an Idempotency-Key on retried POST calls so a
retry never double-charges (see [Errors → Idempotency](/docs/ottili-ai-api-errors)).
Was this article helpful?
