0-100 (Clean / Low / Medium / High) and is driven entirely by the signals collected from a visitor. This page is about keeping the gateway healthy under load.
In normal use you rarely hit these limits. The snippet manages its own call cadence (one identify per visit, throttled by a session window), so a single visitor on a single page does not generate a burst of requests. Limits exist to absorb abusive traffic, not legitimate integration patterns.
The limits at a glance
The snippet posts collected signals torest.shieldlabs.ai. That ingest gateway applies protections in this order: per IP, then domain freeze / per domain (by plan), then a shared ingest cap. /health is not rate-limited.
None of these feed the score. A sticky IP ban can still surface the sentinel value
999 in a stored snapshot (see below). Soft 429s on the domain cap, shared cap, or domain freeze do not write 999.
The Management API on api.shieldlabs.ai applies the same per-IP limit (15/min, 10-minute ban) and concurrency cap (512 in-flight) independently. It does not use the per-domain or shared ingest table. A 429 or 503 there returns {"error":"too many requests"} or {"error":"server is busy"}.
The History API on account.shieldlabs.ai (/api/v1 and legacy /pub) is not the ingest gateway. It has its own soft cap: 15 requests per second per site (Private API Key / site). Crossing it returns 429 {"error":"too many requests"} with no sticky ban — retry in the next second. A 1:1 identify → History lookup stays inside this ceiling on Scale (15 ingest RPS).
Per-IP rate limit and the 10-minute ban
A single source IP may make up to 15 requests per minute to the REST ingest endpoint. Cross that and the gateway returns:429. The ban clears automatically; there is no manual unban step and nothing to configure.
Guard against the “999” ban marker
When an IP is banned, the browser receives the429, but your backend can still see a snapshot for that request: ShieldLabs writes a sentinel value of 999 to mark the banned request, and that snapshot can reach you on a webhook delivery or a History API row. The 999 is not capped to 100 on the ban path, so it arrives as-is. You may therefore receive a payload where data.risk_score (webhook) or Score (History API) is 999.
A 429 from the per-domain or shared ingest cap is a soft reject: no ban, and no 999 marker.
Per-domain ingest (soft 429)
Every identification for one registered host shares a single per-second budget, regardless of how many visitor IPs are hitting it. The budget follows the account plan:
Exceeding the budget returns the same
429 body as the per-IP limit. The domain is not banned; traffic is accepted again in the next second. Upgrade the plan if a busy site needs more headroom. How many domains you may hold is separate — see Domains.
Domain freeze (plan RPS)
If the domain stays at that cap for 10 seconds in a row, ShieldLabs pauses processing for that domain: further requests get the same429 JSON, they are not scored, and they are not billed. WebRTC for that domain is paused the same way (existing WebRTC 429 text). This is not a disabled domain and not the per-IP 10-minute ban (no 999). Processing starts again after several consecutive seconds below the cap.
The dashboard shows a Frozen status with an explanation. You may also get one email per domain per 24 hours with a short snapshot.
Shared ingest cap (soft 429)
REST ingest also applies a shared cap of 40 requests per second across domains. Crossing it returns429 without a ban and without a 999 marker. Retry shortly; do not treat it as an IP or plan problem.
Concurrency cap (503)
Independent of the rate limits, the gateway caps the number of simultaneous in-flight requests across all traffic. When that cap (512 connections) is saturated, new requests get:503 here means “try again shortly,” not “you did something wrong.” It is transient back-pressure. The snippet does not need special handling for this; if you call the Server API server-side and hit a 503, retry with a short backoff.
Request body size (512 KB)
Each request body to the REST gateway is capped at 512 KB. The signal payload the snippet sends is well under this in normal operation, so you will not approach the cap unless something is wrong upstream (for example, a payload being duplicated or wrapped before it reaches the gateway). Oversized bodies are rejected before scoring.Billing exhaustion is separate (402)
Running out of request balance is not a rate limit. It is a billing condition with its own status code:402 means the domain has no remaining request balance, so the call cannot be processed. This is unrelated to how fast you are sending traffic. Top up or upgrade the plan to clear it. The Billing page covers how requests are counted (one identify = one request), and the Errors page lists the full status-code reference.
How to stay within the limits
1
Let the snippet post from the browser
The snippet is designed to call
rest.shieldlabs.ai directly from each visitor’s browser, so requests are naturally spread across many client IPs. Do not relay snippet traffic through a single server, which would funnel everyone onto one IP and trip the 15/minute limit.2
Rely on the built-in session throttle
The snippet runs one identify per visit within a session window rather than on every interaction. You generally do not need to add your own debouncing. Use
forceCheckAnonymous / forceCheckAuthenticatedUser only at meaningful moments (right after login, before a sensitive action), not in a loop.3
Read results, do not poll the gateway
Get scores from the webhook (delivered automatically) or, when you need a guaranteed read, from the History API. Do not re-fire identify calls to “refresh” a score.
4
Back off on 429 and 503
For a per-IP
429, treat that IP as blocked until the 10-minute ban window passes. For a domain or shared 429, wait a second and retry. Treat 503 as transient and retry after a short, jittered delay.Quick reference
The Errors page documents every status code ShieldLabs can return and how to handle it.