RateLimit-* headers, and a 429 response includes Retry-After so you know how long to wait before retrying.
Burst limit
120 requests / 60s across all endpoints. Smooths out spikes of traffic.
Generation quota
500 generations / rolling 30 days. Both
POST /diagrams/generate and POST /diagrams/{id}/edit count.The two limits
120 requests / 60s
Applies to every request made with a given key, regardless of endpoint. When you exceed it you receive a
429 with code rate_limit_exceeded. The window is a rolling 60 seconds.500 generations / rolling 30 days
Applies to
POST /diagrams/generate and POST /diagrams/{id}/edit (both use scope generate and run an AI generation). Each accepted job counts once against the quota. When the quota is exhausted you receive a 429 with code quota_exceeded. The window is a rolling 30 days.Both limits are tracked per key. A personal Pro key and a Team org key each have their own independent counters.
Response headers
Every API response carries the rate-limit headers, so you can read your remaining allowance without waiting for a429.
integer
The maximum number of requests allowed in the current window.
integer
The number of requests remaining in the current window.
integer
The number of seconds until the current window resets.
integer
Present only on
429 responses. The number of seconds to wait before retrying.Example: a normal response
Handling 429 responses
A429 Too Many Requests always has error.type of rate_limit_error. Inspect error.code to tell the two limits apart, then use the Retry-After header to decide how long to wait.
Example: a 429 burst response
Recommended strategy
1
Read the code
On a
429, branch on error.code. Treat rate_limit_exceeded as retryable and quota_exceeded as a hard stop for the current window.2
Honor Retry-After
Wait at least
Retry-After seconds (it is provided in seconds) before retrying. Never retry sooner.3
Add exponential backoff with jitter
For repeated
rate_limit_exceeded responses, increase the delay on each attempt (e.g. Retry-After, then 2x, 4x…) and add random jitter to avoid thundering-herd retries. Cap the number of attempts.4
Stop on quota_exceeded
Do not retry
quota_exceeded in a loop. Surface it to the operator and resume after the rolling window frees up capacity.Backoff example
FAQ
Do RateLimit-* headers appear on every response?
Do RateLimit-* headers appear on every response?
Yes.
RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset are returned on every API call. Retry-After is added only on 429 responses.Which calls count against the generation quota?
Which calls count against the generation quota?
Only
POST /diagrams/generate. Each accepted job (returned as 202 Accepted with a queued Generation) counts once. Polling GET /generations/{id} does not count against the quota, but every request still counts against the 120/60s burst limit.Are the limits per key or per account?
Are the limits per key or per account?
Per key. Each key — personal Pro or Team org — maintains its own burst and quota counters.
What's the difference between rate_limit_exceeded and quota_exceeded?
What's the difference between rate_limit_exceeded and quota_exceeded?
Both are
429 with error.type rate_limit_error. rate_limit_exceeded means you hit the short-term burst limit (120/60s) — retry after Retry-After seconds. quota_exceeded means you exhausted the 500-generations rolling 30-day quota — retrying immediately won’t help; wait for the window to free up or upgrade.