Skip to content

Rate limits

Two independent ceilings: public per-IP (no key) and authenticated per-plan (monthly quota). Authenticated requests skip the IP ceiling entirely.

EndpointLimit
GET /v1/check10 req / IP / hour
GET /v1/jurisdictions10 req / IP / hour
GET /v1/operators/search10 req / IP / hour (+ 3-row cap on limit)

The window is clock-aligned: the counter resets at the top of each UTC hour. A caller that burns 10 requests at 14:58 waits two minutes, not sixty.

PlanMonthly quotaBurst
Starter10,000 calls5 req/sec
Pro100,000 calls20 req/sec
BusinessFair useNo fixed cap
EnterpriseCustomNegotiated

Per-plan enforcement lands with Phase 2 billing. Plan-aware monthly quotas and burst ceilings activate once Stripe goes live. Until then a lightweight pre-launch daily cap of 10,000 requests/day per API key acts as a safety net — see below.

While Stripe integration is pending, authenticated keys on any plan tier except business / enterprise carry an additional soft cap of 10,000 requests/day per key, counted per UTC day and reset at 00:00:00Z. Rationale: a key shared with a reviewer or journalist shouldn’t be able to silently exhaust our Cloudflare bandwidth budget before billing lands. The 10k/day ceiling is deliberately picked to exceed the Pro plan average (~3,333 req/day over a 30-day month at 100k/mo), so a realistic Pro customer never hits it.

Hit the cap → 429 rate_limited:

{
"error": "Pre-launch rate limit exceeded",
"code": "rate_limited",
"details": {
"reason": "prelaunch_daily_cap",
"current_usage": 10000,
"limit": 10000,
"reset_at": "2026-04-21T00:00:00.000Z",
"suggestion": "Pre-launch keys are capped at 10000 requests/day. Upgrade to a paid plan at https://igregulator.io/pricing for production use."
}
}

Response carries X-Prelaunch-Daily-Limit, -Used, -Reset headers so clients can monitor consumption. Cap lifts automatically when the plan enforcement layer takes over; no client change needed.

Every public-endpoint response includes:

HeaderMeaning
X-RateLimit-LimitCeiling for this caller on this endpoint in the current window (e.g. 10).
X-RateLimit-RemainingRequests left. Never negative.
X-RateLimit-ResetUnix epoch seconds when the window rolls over.
X-RateLimit-PolicyHuman-friendly policy string: tier=public;limit=10;window=hour. Hand-readable, easy to awk.
RateLimit-PolicyIETF draft format (draft-ietf-httpapi-ratelimit-headers-09): "default";q=10;w=3600. Modern HTTP clients (Cloudflare SDK, Kong, etc.) auto-parse this.
X-Upgrade-URLhttps://igregulator.io/pricing — surfaced so a UI can link “upgrade to keep going” on 429.
// X-RateLimit-Policy — custom: tier=public;limit=10;window=hour
const policyCustom = Object.fromEntries(
res.headers.get('X-RateLimit-Policy').split(';').map((kv) => kv.split('=')),
);
// { tier: 'public', limit: '10', window: 'hour' }
// RateLimit-Policy — IETF: "default";q=10;w=3600
const policyIetf = res.headers.get('RateLimit-Policy');
const q = policyIetf.match(/q=(\d+)/)?.[1]; // quota
const w = policyIetf.match(/w=(\d+)/)?.[1]; // window in seconds
HTTP/2 429
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1776604800
X-Upgrade-URL: https://igregulator.io/pricing
{
"error": "Public rate limit reached (10/hour/IP).",
"code": "rate_limited",
"details": {
"limit": 10,
"window_seconds": 3600,
"upgrade_url": "https://igregulator.io/pricing"
}
}

Recommended client behaviour:

  1. Check X-RateLimit-Remaining before every call.
  2. On 429, sleep until X-RateLimit-Reset, then retry once.
  3. If the same caller keeps hitting 429, that’s a signal to authenticate or upgrade — not to back-off-and-retry indefinitely.
  • Don’t scrape the public endpoint. Paginate the authenticated /v1/jurisdictions/:code/operators list once a day and cache — the data only refreshes at 03:00 UTC anyway.
  • Front-ends that surface check results to end-users — apply the 10/hour IP limit on your server and call the API with a single authenticated key; don’t let every browser session hit us directly or the shared IP will burn out your quota.
  • Bulk re-verification (weekly AML sweep of 2,000 operators) — use the authenticated operator/licence endpoints, not /v1/check.