Skip to main content

Rate Limits

Three limits apply. All are enforced at the edge, before your query is parsed.

ScopeLimitWindow
GraphQL requests per IP100per minute
GraphQL requests per API key1,000per day
API key request form per IP5per hour

The per-IP and per-key limits are independent — you can exhaust either one. The health check endpoint is exempt.

Per-key means per key, not per host

The per-key limit is tracked against the first 12 characters of your token, which is the key's prefix. Every process, server and region using the same key shares one daily allowance. Running ten instances does not give you ten times the quota.

If you need isolated quotas for separate workloads, request separate keys on separate email addresses — see API Keys.

The 429 response

Exceeding a limit returns HTTP 429 with a Retry-After header:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 37
{ "errors": [{ "message": "Rate limit exceeded. Retry after 37 seconds." }] }

Retry-After is the number of seconds until the current window rolls over. Honour it rather than retrying immediately — retrying inside the window consumes nothing but still fails.

Note that windows are fixed, not sliding: the per-minute counter resets at the boundary, so Retry-After can be anywhere from 1 to 60 seconds.

Staying under the limits

The most effective measures, roughly in order of impact:

  • Cache what does not change. translations, languages, books and bibleIndex change only when the corpus does. Fetch them at startup, not per request.
  • Ask for one query, not five. GraphQL lets you request several fields in a single document; a book list and a passage can share one request.
  • Cache verseOfTheDay for the rest of the day — the answer is stable.
  • Cache semanticSearch by query string. It is the most expensive query in the API.
  • Request only the fields you need. This does not affect the request count, but it does affect latency and complexity budget.

Backing off

On a 429, sleep for Retry-After seconds and retry once. If you are running a batch job, add jitter so parallel workers do not all wake at the same instant.

Both official SDKs raise a distinct error for this case — BibleQL::RateLimitError in Ruby and RateLimitError in Node — so you can catch it specifically rather than inspecting status codes. See SDKs.

Query complexity is a separate ceiling

Rate limits cap how often you can ask. Complexity caps how much one query can ask for:

LimitValue
Max depth15
Max complexity300
Max query string tokens5,000

Exceeding these returns a validation error, not a 429, and it does not count against your quota. See API Behavior.