Rate limits
Limits are policy. They travel with the key, are enforced by the SDK against a central counter, and are reported in standard headers.
A limit is a rule on a key or a project default, and it travels on the same policy feed as everything else about the key. The SDK enforces it against a central counter store, one round trip per request, with every counter read before any is charged so a caller over their daily quota does not keep burning their per-second one.
The shape of a rule
Up to eight rules per key or per project, with unique ids:
{
"rate_limits": [
{
"id": "per-key-second",
"limit": 10,
"window_ms": 1000,
"algorithm": "sliding",
"scope": "key"
},
{
"id": "per-tenant-day",
"limit": 10000,
"window_ms": 86400000,
"algorithm": "fixed",
"scope": "tenant"
}
]
}| Field | Values | Meaning |
|---|---|---|
limit | 1 to 1,000,000,000 | Requests allowed per window. |
window_ms | 1,000 to 2,678,400,000 | One second to 31 days. Below a second is not a number a counter one network hop away can honour. |
algorithm | sliding, fixed | sliding for per-second and per-minute, no boundary burst. fixed for a daily quota. |
scope | key, tenant | tenant shares one counter across every key the tenant holds, and survives a rotation. |
Set them on Projects → New project as the default, or on a key's Edit form to override. A key with its own list replaces the project's; it does not merge.
Test and live traffic never share a counter, even under scope: 'tenant': a customer's free test traffic cannot spend their live quota.
What the caller sees
RateLimit-Policy: "per-key-second";q=10;w=1, "per-tenant-day";q=10000;w=86400
RateLimit: "per-key-second";r=7;t=1, "per-tenant-day";r=9000;t=3600
RateLimit-Limit: 10
RateLimit-Remaining: 7
RateLimit-Reset: 1Both the IETF draft spelling and the legacy triple ship, because every client library in production parses the triple. legacyRateLimitHeaders: false turns the triple off. A refused request is a 429 with Retry-After and Keyring-Rate-Limited-Policy naming the rule that refused it.
When the counters are unreachable
rateLimitOnUnavailable is open by default: the request is served, degraded is set, and no RateLimit header is emitted at all, because a fabricated remaining is a number your caller will act on. closed answers 503 with Retry-After, for a route where admitting unmetered traffic is the expensive failure: an LLM call, an outbound SMS. The wait is bounded by rateLimitTimeoutMs, 500 milliseconds.
This default is the opposite of idempotency's, and the asymmetry is deliberate.