generated: '2026-09-04' method: searched source: https://docs.shieldlabs.ai/rate-limits docs: rate_limits: https://docs.shieldlabs.ai/rate-limits billing: https://docs.shieldlabs.ai/billing changelog: https://docs.shieldlabs.ai/changelog note: >- ShieldLabs publishes explicit numeric limits on a dedicated page, and that page was REWRITTEN in August 2026. Three numbers this catalog previously recorded are now wrong and are corrected here: the per-IP limit is 15 requests/minute (not 20), the ban that follows is 10 minutes (not one hour), and the History API is NO LONGER exempt — it now carries its own soft cap of 15 requests/second per site. The provider says so in its own changelog ("Per-IP REST ingest: 15 requests per minute, then a 10-minute ban (not 20/minute and not a 1-hour ban)"), so this is a published correction, not drift we inferred. Two limits are entirely new since the last pass: a per-domain ingest budget that scales with the plan (5 / 5 / 10 / 15 requests per second for Free / Starter / Growth / Scale) and a DOMAIN FREEZE — sitting at the plan cap for ten consecutive seconds pauses processing for that domain until it drops below the cap for several seconds. A freeze is billed at nothing and, unlike the IP ban, writes no 999. It still ships NO rate-limit response headers. There is no X-RateLimit-*, no RateLimit-*, and no Retry-After on any surface, so an agent gets no runtime budget signal and must infer state from the status code alone. Every 429 in this API is the same 429: an agent cannot tell a one-second soft reject on the shared cap from a ten-minute sticky IP ban without knowing which limit it crossed. The single most dangerous detail for an integrator is still the 999 sentinel, and the August rewrite narrowed it usefully: 999 is now documented as ONLY the per-IP ban marker. A soft 429 from the per-domain cap, the shared cap or a domain freeze does not write it. limit_count: 7 limits: - name: Per-IP rate limit scope: source IP window: 1 minute limit: 15 burst: null applies_to: - https://rest.shieldlabs.ai (snippet ingest gateway) - https://api.shieldlabs.ai (Management API, applied independently) status_on_exhaustion: 429 response_body: '{"error":"too many requests"}' penalty: The source IP is banned for 10 minutes; further requests continue to return 429. reset: automatic after the ban window; no manual unban, nothing to configure writes_999_sentinel: true headers: [] changed_since: >- Was recorded as 20 requests/minute with a 1-hour ban on 2026-08-19; the provider corrected both numbers in the August 2026 changelog. - name: Per-domain ingest scope: one registered domain, all visitor IPs together window: 1 second limit_by_plan: Free: 5 Starter: 5 Growth: 10 Scale: 15 applies_to: - https://rest.shieldlabs.ai status_on_exhaustion: 429 penalty: none — soft reject for that second; the domain is not banned writes_999_sentinel: false headers: [] new_since: '2026-08' - name: Domain freeze scope: registered domain window: 10 consecutive seconds at the plan ingest cap limit: null applies_to: - https://rest.shieldlabs.ai status_on_exhaustion: 429 penalty: >- Processing pauses for that domain — requests are not scored and are NOT billed. WebRTC collection for the domain pauses the same way. Clears after several consecutive seconds below the cap. writes_999_sentinel: false not_a: [disabled domain, per-IP ban] surfaced_as: Dashboard shows a "Frozen" status; at most one email per domain per 24 hours. headers: [] new_since: '2026-08' - name: Shared ingest cap scope: all domains, platform-wide window: 1 second limit: 40 applies_to: - https://rest.shieldlabs.ai status_on_exhaustion: 429 penalty: none — soft reject, no ban writes_999_sentinel: false headers: [] new_since: '2026-08' - name: History API soft cap scope: per site (per Private API Key) window: 1 second limit: 15 applies_to: - https://account.shieldlabs.ai/api/v1 - https://account.shieldlabs.ai/pub (legacy) status_on_exhaustion: 429 response_body: '{"error":"too many requests"}' penalty: none — no sticky ban; retry in the next second headers: [] new_since: '2026-08' note: >- Deliberately sized so a 1:1 identify-to-History-lookup pattern stays inside the ceiling on Scale, whose ingest cap is also 15 RPS. This surface was recorded as "not rate limited" in the previous pass, which is no longer true. - name: Concurrency cap scope: whole gateway, in-flight connections window: instantaneous limit: 512 applies_to: - https://rest.shieldlabs.ai - https://api.shieldlabs.ai status_on_exhaustion: 503 response_body: '{"error":"server is busy"}' guidance: transient back-pressure; retry with a short jittered backoff headers: [] - name: Request body size scope: per request limit: 512 KB applies_to: - https://rest.shieldlabs.ai status_on_exhaustion: request rejected before scoring headers: [] enforcement_order: - per-IP - domain freeze / per-domain - shared ingest cap exempt: - path: /health surface: https://rest.shieldlabs.ai note: Explicitly not rate-limited. response_headers: ratelimit_headers_published: false x_ratelimit: false ratelimit_rfc9331: false retry_after: false note: >- No rate-limit signalling headers are documented on any surface. Clients must branch on the HTTP status code, and the status code alone cannot distinguish a soft one-second reject from a ten-minute sticky ban. adjacent_status_codes: - status: 402 meaning: Domain request balance exhausted — a billing condition, not a rate limit. sentinel: value: 999 field: data.risk_score (webhook) / Score (Management History array) written_by: the per-IP 10-minute ban ONLY not_written_by: [per-domain cap, shared ingest cap, domain freeze] meaning: >- Ban marker written to the snapshot for a banned request. Not capped to 100. Guard for score > 100 at the top of any handler before reading the risk band — the provider ships that guard as a two-line snippet in its own docs.