StrataDocs

Rate limits and budgets

Every Strata API key is governed by two independent controls: a per-hour request rate limit and an optional token budget per billing cycle. Each authenticated response surfaces the current state of both so your client can pace requests and detect exhaustion before it happens.

The two controls

Rate limits and budgets are evaluated separately on every request. A key can be rate-limited even with budget remaining, and budget-exhausted even with rate headroom available. Both are scoped to a single key, not the whole organization.

  • Rate limit. A per-key, per-hour cap. The default is 1,000 requests per hour; an admin can set it anywhere from 1 to 1,000,000 per key in Admin → API Keys. Counts include every /v1/* call made with that key, including GET /v1/health.
  • Budget. An optional per-key cap measured in tokens (prompt + completion). The default for a new key is 5,000,000 tokens, and an admin can raise, lower, or remove it. When a budget is set, the endpoints that invoke a model — /v1/chat and /v1/query — count their total_tokens against the remaining balance. Endpoints that never call a model do not consume budget: /v1/health, /v1/conversations, /v1/files, and the direct document generators (/v1/excel, /v1/pdf, /v1/pptx, /v1/docx), which run the engines with no AI involved and are zero token cost.
Key

The budget is denominated in tokens, not currency. X-Strata-Budget-Limit and X-Strata-Budget-Used are token counts.

Note

Both controls are per key. Issue a separate key per integration so one runaway client cannot starve the others, and so revoking it takes down only that integration.

Response headers

Every authenticated response includes the headers below. Read them on success responses, not just errors — that is how you pace before you hit the wall.

HeaderMeaning
X-Strata-Api-Key-IdOpaque ID of the key used. Log it alongside your request ID.
X-RateLimit-LimitRequest cap for the current hour.
X-RateLimit-RemainingRequests left in the current hour.
X-RateLimit-ResetUnix epoch seconds when the limit window resets.
X-Strata-Rate-LimitMirror of X-RateLimit-Limit (Strata-namespaced).
X-Strata-Rate-UsedRequests consumed so far in the window.
X-Strata-Budget-LimitToken budget for the cycle, or omitted if the key has no budget.
X-Strata-Budget-UsedTokens consumed so far this cycle.
Retry-AfterSeconds to wait. Sent on the rate-limit and IP-throttle 429s (not the budget 429, which backoff can't clear).
Tip

If you're calling from a browser-based integration (for example the Strata for Outlook add-in), these headers are exposed via CORS, so your fetch can read them off the response.

How the rate window works

On a single instance, the rate limit is a sliding 60-minute window — each request ages out exactly 60 minutes after it was made. On a multi-instance deployment, the limit is enforced as a shared fixed-hour bucket so the cap holds org-wide instead of multiplying by the instance count. In both cases a key admits up to its X-RateLimit-Limit requests per hour. Treat X-RateLimit-Reset as the authoritative "try again at" time rather than assuming a precise sliding boundary.

A separate, coarse per-IP throttle sits in front of key resolution to blunt brute-force and leaked-key abuse. Normal clients never hit it; if you do, you get a 429 with type ending in /ip_rate_limit_exceeded and a Retry-After.

Handling 429 Too Many Requests

Strata returns 429 in three cases — the per-key rate limit, the per-key budget, and the pre-auth IP throttle. Each returns an RFC 7807 application/problem+json body whose type identifies which limit fired. The rate-limit and IP-throttle 429s also carry a Retry-After header; the budget 429 does not, because waiting won't clear it until the cycle resets.

A rate-limit 429:

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 1843
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1780000247

{
  "type": "https://api.strata.kronisys.com/errors/rate_limit_exceeded",
  "title": "API key rate limit exceeded (1000/1000 per hour). Retry after 1843s.",
  "status": 429,
  "retry_after": 1843,
  "used": 1000,
  "limit": 1000
}

Wait at least Retry-After seconds, then retry with exponential backoff and jitter. A safe pattern:

import time, random

def call_with_backoff(req_fn, max_attempts=5):
    for attempt in range(max_attempts):
        r = req_fn()
        if r.status_code != 429:
            return r
        wait = int(r.headers.get("Retry-After", 1))
        # Exponential backoff with full jitter, floored at Retry-After.
        backoff = max(wait, (2 ** attempt)) + random.uniform(0, 1)
        time.sleep(backoff)
    return r
Tip

Do not retry faster than Retry-After. On a single-instance window a premature retry consumes another slot and pushes the reset further out.

Handling a budget 429

When a request would exceed the key's token budget, Strata also returns 429, with a type of /budget_exhausted. To prevent concurrent bursts from all slipping past the same pre-burst total, an estimated in-flight reservation is added on top of recorded usage — so a key can be turned away slightly before the recorded used reaches the limit.

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json

{
  "type": "https://api.strata.kronisys.com/errors/budget_exhausted",
  "title": "Monthly token budget exhausted (5,012,000 / 5,000,000, includes 16,000 in-flight). The cycle resets on day 1 of each month.",
  "status": 429,
  "used": 4996000,
  "in_flight": 16000,
  "limit": 5000000
}

Backoff alone will not clear this — the key is capped until the cycle resets. Surface the error to a human, fall back to a different key, or wait for the reset. The budget gate is enforced at the key level in the auth layer, so once a key is over budget every /v1/* call it makes is turned away — including the non-model endpoints (/v1/health, /v1/files, /v1/conversations). To monitor budget state without spending tokens, read X-Strata-Budget-Used / X-Strata-Budget-Limit on any successful response, or call GET /v1/health before the cap is reached. (Only /v1/chat and /v1/query add to the recorded total in the first place — the non-model endpoints never increase usage themselves.)

Warning

If budget enforcement can't reach the database, Strata fails closed with a 503 (type /budget_check_failed) rather than letting uncapped spend through. Retry after a few seconds.

Budget cycle and reset

The budget counts tokens since the start of the current cycle:

  • By default the cycle is the calendar month, resetting at 00:00 UTC on the 1st.
  • If your organization is on a usage pool with a custom reset day, the budget cycle follows that day instead. The /budget_exhausted message names the reset day, and GET /v1/health returns the exact cycle_started_at.

Call GET /v1/health at any time to read the live budget without spending tokens:

{
  "status": "ok",
  "key": {
    "rate_limit_per_hour": 1000,
    "monthly_token_budget": 5000000
  },
  "budget": {
    "used": 1284500,
    "limit": 5000000,
    "remaining": 3715500,
    "cycle_started_at": "2026-06-01T00:00:00.000Z"
  },
  "timestamp": "2026-06-24T14:00:00.000Z"
}
Warning

The default cycle resets at UTC midnight. A key on a 5,000,000-token budget will not get extra room on your local 1st if your servers run in a non-UTC zone — the reset happens at UTC midnight (or your pool's reset day).

Sizing limits

A few guidelines from production deployments:

  • Default the rate limit to 2-3x your steady-state peak. Leave headroom for retries and bursts.
  • One key per integration. A shared key makes rate limits and budgets unreadable, and revoking it takes down everything at once.
  • Set a token budget on every key, even a large one. A 5,000,000-token budget is still a circuit breaker if a job spirals. Keys with no budget can run away.
  • Watch X-Strata-Budget-Used proactively. Alert at 80% so on-call has time to react before the cap.
  • Use Idempotency-Key so a retry doesn't double-bill. A non-streaming POST /v1/chat that carries an Idempotency-Key header replays the original result on a repeat instead of re-running the turn and creating a duplicate conversation; a request still in flight under the same key returns 409. Keys are remembered for 24 hours, scoped per API key. Streaming requests are not deduplicated (reconnect rather than re-POST), and the other endpoints don't dedupe — so without an Idempotency-Key, every retry of a model call is a fresh request that consumes a request slot and tokens. Pace with Retry-After and reuse the same key on retries.

Related