Developer portal
Rate limits & quotas
Layered limits protect shared infrastructure while keeping scan polling and auditor verify flows unblocked.
Last updated: 2026-07-01
Overview
Qtangl applies limits at several layers. Authenticated write routes share a per-key requests-per-minute budget. Authenticated read routes (including PQC scan polling) do not count toward that budget. Public verify, transparency, and Readiness Index routes use a separate per-IP limit. Signup and lead-capture forms have abuse caps. Discovery and remediation have additional tenant-scoped limits. Monthly scan caps are tier entitlements and return 402 Payment Required, not 429.
For authentication schemes and RBAC, see Authentication & RBAC. For key hygiene and transport security, see Security & compliance.
| Category | Limit | Scope | Status | Notes |
|---|---|---|---|---|
Authenticated writesQTANGL_RATE_LIMIT_PER_MINUTE | 300 / minute | Per API key or dashboard session | 429 | Minimum floor 300; Redis when REDIS_URL is set |
| Authenticated reads | No global counter | Per API key | 429 | Polling GET /pqc/scan/{scanId} does not burn write budget |
Public verify & transparencyQTANGL_VERIFY_RATE_LIMIT_PER_MINUTE | 60 / minute | Per client IP | 429 | Retry-After header on 429; in-memory per instance |
Assess signupQTANGL_ASSESS_SIGNUP_LIMIT_PER_HOUR | 5 / hour | Per email domain | 429 | — |
Lead captureQTANGL_LEAD_CAPTURE_LIMIT_PER_HOUR | 10 / hour | Per email domain | 429 | — |
| Discovery scan enqueue | 50 / hour | Per tenant | 429 | Requires Redis |
| Discovery findings ingest | 10,000 / minute | Per tenant + agent | 429 | Requires Redis |
| Monthly scan quota | Tier-dependent | Per tenant | 402 | Payment Required — not a rate limit |
| Crypto flip budget | 50 / day | Per tenant | Policy | Policy rejection; 24h KMS prod cooldown |
Authenticated API — 300 req/min per key
The backend enforces 300 requests per minute per API key by default on authenticated write routes. Operators can override with QTANGL_RATE_LIMIT_PER_MINUTE; values below 300 are ignored. When REDIS_URL is set, counters are distributed across instances; otherwise each process keeps an in-memory sliding window.
Every response includes informational headers X-RateLimit-Limit and X-RateLimit-Window: 60. Rate-limit counter state uses a rolling 60-second window — see Data retention.
Routes under /admin/* use a separate admin key and are not counted. Unauthenticated health routes are unlimited.
Write routes (count toward limit)
| Method | Path | Counts toward 300/min | Notes |
|---|---|---|---|
| POST | /pqc/scan | Yes | Supports Idempotency-Key |
| POST | /pqc/upload-bundle | Yes | — |
| POST | /optimize | Yes | — |
| POST | /tenant/discovery/host-scan | Yes | — |
| POST | /tenant/discovery/binary-scan | Yes | — |
| POST | /tenant/* | Yes | Write mutations (require_auth_write) |
Read routes (exempt from global counter)
This exemption is intentional: polling GET /pqc/scan/{scanId} during a scan workflow does not consume your write budget.
| Method | Path | Counts toward 300/min | Notes |
|---|---|---|---|
| GET | /pqc/scan/{scanId} | No | Safe for polling |
| GET | /pqc/report/{scanId} | No | — |
| GET | /pqc/inventory | No | — |
| GET | /pqc/scenarios | No | — |
| GET | /tenant/scans | No | — |
| GET | /tenant/me | No | — |
Public endpoints — 60 req/min per IP
Verify, transparency log, Readiness Index, and dogfood routes are public — no API key required. They share a default limit of 60 requests per minute per client IP, keyed from X-Forwarded-For or the connection host. Override with QTANGL_VERIFY_RATE_LIMIT_PER_MINUTE and QTANGL_VERIFY_RATE_WINDOW_SEC. Responses include a Retry-After header on 429.
| Method | Path | Notes |
|---|---|---|
| GET | /pqc/verify/{scanId} | — |
| POST | /pqc/verify | — |
| GET | /pqc/dogfood/latest | — |
| GET | /pqc/dogfood/summary | — |
| GET | /pqc/dogfood/history | — |
| GET | /pqc/dogfood/auditor-bundle | — |
| GET | /pqc/index | — |
| GET | /pqc/index/drift | — |
| GET | /pqc/transparency/consistency | — |
| GET | /pqc/transparency/witnesses | — |
| POST | /pqc/transparency/witness | — |
| GET | /pqc/transparency/root | — |
| GET | /pqc/transparency/keys | — |
| GET | /pqc/transparency/{contentHash} | — |
Signup and lead capture
Self-serve signup and marketing forms are rate-limited per email domain to reduce abuse.
| Category | Limit | Scope | Status | Notes |
|---|---|---|---|---|
POST /public/assess-signupQTANGL_ASSESS_SIGNUP_LIMIT_PER_HOUR | 5 / hour | Per email domain | 429 | Set <= 0 to disable |
POST /public/lead-captureQTANGL_LEAD_CAPTURE_LIMIT_PER_HOUR | 10 / hour | Per email domain | 429 | Set <= 0 to disable |
Discovery limits
Host discovery scan enqueue and agent findings ingest have separate tenant-scoped limits. These require Redis; when Redis is unavailable, discovery rate limits are not enforced.
| Category | Limit | Scope | Status | Notes |
|---|---|---|---|---|
| Scan enqueue | 50 / hour | Per tenant | 429 | POST /tenant/discovery/host-scan, POST /tenant/discovery/binary-scan |
| Findings ingest | 10,000 / minute | Per tenant + agent | 429 | POST /discovery/agent/findings |
Tier quotas (402, not 429)
Monthly scan counts, schedule caps, and API key limits are subscription entitlements. Exceeding a monthly scan quota returns 402 Payment Required with a scan_quota_exceeded code — not a rate limit. Production live scans also require legal acceptance and trial or paid status.
| Tier | Scans / month | Schedules | API keys |
|---|---|---|---|
| free | 5 | 0 | 1 |
| monitor | 100 | 10 | 5 |
| convert | 500 | 25 | 10 |
| enterprise | 5000 | 100 | unlimited |
Upgrade via the billing portal or pricing.
Remediation flip budget
Crypto flip automation enforces a policy budget of 50 flips per day per tenant, with a 24-hour cooldown between KMS production flips. Exceeding the budget returns a policy rejection in the response metadata — not HTTP 429.
429 and 402 responses
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 300
X-RateLimit-Window: 60
{
"detail": "Rate limit reached. The pilot API allows 300 requests per minute per key."
}| Code | Meaning | Typical cause | Suggested fix |
|---|---|---|---|
| 429 | Too many requests | Per-key rate limit exceeded (default 300/min) or public endpoint IP limit (60/min). | Backoff with jitter; poll read endpoints; cache results; contact support for production limits. |
| 402 | Payment required | Monthly scan quota or feature not included in current tier entitlements. | Upgrade via billing portal or contact sales for enterprise tier. |
Client patterns
- Poll
GET /pqc/scan/{scanId}freely — read routes are exempt from the 300/min write counter. - Send
Idempotency-KeyonPOST /pqc/scanto avoid duplicate scans when retrying after 429 or network errors. - Exponential backoff with jitter on 429; respect
Retry-Afteron public routes. - Official SDKs (TypeScript and Python) treat 429 as retryable with exponential backoff.
- Cache optimize results when inputs have not changed.
Operator environment variables
| Variable | Default | Scope |
|---|---|---|
| QTANGL_RATE_LIMIT_PER_MINUTE | 300 | Per API key / session token, 60s window |
| QTANGL_VERIFY_RATE_LIMIT_PER_MINUTE | 60 | Per IP on public verify, transparency, and index routes |
| QTANGL_VERIFY_RATE_WINDOW_SEC | 60 | Window length for public IP limit |
| QTANGL_ASSESS_SIGNUP_LIMIT_PER_HOUR | 5 | Per email domain on assess signup |
| QTANGL_LEAD_CAPTURE_LIMIT_PER_HOUR | 10 | Per email domain on lead capture |
| REDIS_URL | unset | Enables distributed per-key and discovery limits |
Found an issue? Report documentation feedback