Default limits
Rate limit headers
Every response includes rate limit headers:Rate limit response
When rate limited, you’ll receive:429 Too Many Requests
Best practices
1. Implement exponential backoff
2. Use batch endpoints
Instead of individual requests:3. Cache results
Per-endpoint limits
Some endpoints have specific limits:Request size and time limits
Beyond request-count quotas, the API enforces hard size and wall-clock bounds on expensive operations. These limits are fixed and apply to every plan tier./verify/stats upload limits
Statistical verification accepts a CSV upload, and both the transfer and the parsed dataset are capped:
The byte cap is enforced while the body is received, before any parsing starts, so chunked uploads without a
Content-Length header are bounded too. The cell cap is enforced during parsing: QWED reads the CSV in chunks and aborts with 413 the moment the accumulated row × column count crosses the budget, so a compact but very wide file cannot allocate an oversized DataFrame. Empty or column-less CSVs return 400.
On 503, retry after a short delay. On 413, reduce the file size or split the dataset — the limit cannot be raised per request.
LLM provider call timeout
Every outbound LLM provider call (translation and codegen for natural-language verification) is pinned to a 30-second HTTP timeout with SDK retries disabled. A stalled provider surfaces as a bounded verification failure instead of holding the request open. Implement retries in your client if you need them; see best practices.Consensus deadline
/verify/consensus runs all engines under a single 30-second aggregate deadline. Engines that miss the deadline are returned as explicit BLOCKED results rather than hanging the request or returning HTTP 500. See Consensus engine — execution deadlines.
Math expression compute-cost bounds
Math expressions whose exact evaluation would be unboundedly expensive (huge integer literals, oversized exponents, power towers, large factorials) are rejected before evaluation. See Math engine — compute-cost bounds.Math time limit and worker pool
From v9.0.0,/verify/math and the maths items of /verify/batch run in a fixed pool of worker processes. Unlike the limits above, the server operator sets these with the maths limits settings.
None of these results is admitted. On
math.capacity, retry after a short delay.
In a batch, a maths item’s time limit is cut to what is left of the job’s budget. If the budget runs out while the item waits for a worker, the item is BLOCKED (math.capacity); if it runs out during the run, the item is UNVERIFIABLE (math.timeout). Starting a new worker isn’t counted against the item’s own limit, which is set before the start, so that item can finish after the budget ran out and return its result. The start still uses the job’s wall-clock budget, so later items can get batch.math_budget. Each batch item also has its own limit, QWED_BATCH_ITEM_TIMEOUT (default 60 seconds). The maths time limit applies to the wait and to the run separately, so with QWED_MATH_TIMEOUT_SECONDS above about 30 seconds a slow item can reach the item limit first: it is then failed, with an error and no result.
Per-IP limits on authentication endpoints
Anonymous/auth/* routes such as POST /auth/signup and POST /auth/signin carry no API key, so the per-key limiter cannot apply. As of v7.2, these routes are rate limited per client IP instead. This blocks password-guessing and prevents unauthenticated requests from saturating the service with expensive password hashing.
When the limit is exceeded, the API returns
429 Too Many Requests with a Retry-After header (always at least 1 second):
Client IP resolution behind proxies
By default the direct peer address is the rate-limit key and theX-Forwarded-For header is ignored, so a client cannot mint fresh quotas by rotating spoofed header values. If QWED runs behind a load balancer or reverse proxy, set QWED_AUTH_TRUSTED_PROXIES to the proxy’s address range:
X-Forwarded-For hop as the client IP. Trusted proxies append the real client address after any client-supplied entries, so clients still cannot choose their own bucket. Port suffixes (1.2.3.4:8080, [2001:db8::1]:8080) are stripped before bucketing.
Per-IP buckets are process-local. Running multiple workers or replicas multiplies the effective per-IP budget by the process count. Run a single replica for exact limits, or move rate limiting to a shared store such as Redis before scaling out.
Thread-safe in-memory limiter
The default in-memory rate limiter is thread-safe. All check-and-record operations (per-key and global) are protected by a lock, making it safe to use with multi-threaded ASGI servers such as Uvicorn with multiple workers. Theget_reset_time method also operates under the lock to prevent stale reads.
Fail-closed enforcement
When using Redis-backed rate limiting, the rate limiter operates with a fail-closed policy. If the Redis backend encounters an error at runtime, requests are denied rather than allowed through. This ensures that a temporary Redis outage does not silently bypass rate limits. If Redis is unavailable at startup, an in-memory fallback is used until the service is restarted with a healthy Redis connection.Enterprise options
For higher limits, contact us for:- Custom rate limits
- Dedicated infrastructure
- SLA guarantees