Limits
Limits apply over rolling 60-second windows, both per key and per account. All keys share the account limit.
RPM windows are kept per workload group (chat, images, video, search), so image traffic never consumes chat’s slice. Per-key token and concurrency budgets are shared across every endpoint.
Not admission-limited:
GET /models, GET /pricing, GET /videos/{id} (poll as often as you like), GET /balance, GET /usage, GET /health. POST /estimate has its own limit of 30 per minute per IP.
Response headers
Included on every successful response from a billed route, and on admission429s. The 401 (missing/invalid key) and zero-balance 402 checks both run before the rate limiter, so those two error responses don’t carry them.
When you hit a limit
You get a429 whose body names the layer you hit:
limit is one of key_rpm, account_rpm, key_tpm, account_tpm, key_concurrency, or account_concurrency; scope is key or account. Concurrency denials suggest a 2-second retry_after; window denials report the time until the window rolls over. Batch and compare add units (the fan-out size that was counted).
The pre-auth flood cutoff answers earlier and more tersely — "Too many requests. Try again in N seconds." with retry_after but no request_id, limit, or scope — because it runs before your key is looked up.
Retry code
The SDK is the shortest safe implementation: it honorsRetry-After, uses exponential backoff, retries 429 and 5xx up to maxRetries (default 2), and only retries billed requests when an idempotency key makes replay safe.
Tips
- Bound concurrency — use a worker pool of at most 20 per key instead of unbounded
Promise.all - Video polling doesn’t count — poll as often as you want (every 10s recommended); video submissions are limited to 2 per minute per key
- Honor
Retry-After— it is more accurate than a fixed sleep - Reuse idempotency keys — retries of billed operations must not create duplicate work