Rate limits and errors
120 requests a minute per key, 600 per org, and the error codes the data API returns.
Last updated on
Limits are fixed windows in @convex-dev/rate-limiter, one shard, so the caps stay exact. Change the numbers in convex/lib/dataApiLimits.ts if you operate the deployment. The values below are what the code ships.
| Limit | Value |
|---|---|
DATA_API_RATE_LIMIT_MAX_REQUESTS | 120 requests per key |
DATA_API_ORG_RATE_LIMIT_MAX_REQUESTS | 600 requests per org |
DATA_API_RATE_LIMIT_WINDOW_MS | 60000 (60 seconds) |
DATA_API_KEYS_PER_ORG_MAX | 25 active keys |
DATA_API_LAST_USED_MIN_INTERVAL_MS | 60000 |
The org cap stops 25 keys from multiplying the per-key cap. A request that is already over either limit returns 429 from the rate-limit read. It does not consume another token and does not change lastUsedAt.
400 and 401 are returned before the rate limit is checked and before lastUsedAt changes. 403 and 404 are decided after the request is charged. 429 includes Retry-After as a whole number of seconds. 400, 401, 403, and 404 do not.
Error codes
Every error body uses { "error": "<code>" }. message is optional and for humans. Branch on error.
400 bad_request
The query is wrong. Returned before the rate limit. No Retry-After.
{ "error": "bad_request", "message": "period must be 7d or 28d" }Other messages from the parser:
Provide one content id or a batchContent id batch is at most 20Content id must be a non-empty stringproject_id is not a project idproject_id is required for a key that can read every site
401 unauthorized
The bearer token is missing, unknown, or revoked. A dbw_ write key and a legacy dbr_ key are unauthorized here. Returned before the rate limit. No Retry-After.
{ "error": "unauthorized" }403 forbidden
The key is valid and the site is in the same org, outside this key's scope. The request has already been charged. No Retry-After.
{ "error": "forbidden" }404 not_found
project_id is a real id and the project is missing or belongs to another org. Both cases use this status. The request has already been charged. No Retry-After.
{ "error": "not_found" }429 rate_limited
The key is over 120 requests, or the org is over 600, in the current 60 second window. Retry-After is a whole number of seconds. The body does not include a message.
HTTP/1.1 429
Retry-After: <seconds>
Content-Type: application/json
{ "error": "rate_limited" }500 unavailable
DATA_API_KEY_PEPPER is not set on this Convex deployment. This is not a client rate limit.
{ "error": "unavailable" }