Skip to main content
The Risk Score is a single integer from 0 to 100 that ShieldLabs returns for each identify call. Higher means more anonymous, more likely masked or spoofed, or more likely abusive. It is hard-capped at 100: individual signals can sum past 100 internally, but the score you receive is always clamped to 100. The score is explainable. Every score ships with a Details array listing the exact signals that fired and the points each contributed, so you never act on a black box. ShieldLabs only scores. Your own code decides allow, challenge, review, or block.
The Risk Score is delivered by webhook and readable via the History API. The browser does not compute it. See Webhooks and Management API.
The score maps to four bands. These labels are the only ones ShieldLabs uses. The recommended action is a guide, not a rule: you set the action per band that fits your own risk tolerance.
BandRangeMeaningRecommended action (a guide, not a rule)
Clean0–9No meaningful signalsPass through, no friction
Low10–29One minor signalAllow, worth logging
Medium30–59Multiple overlapping signals, or one moderate signalStep-up challenge, second look, or review
High60–100Strong anonymity or abuse signalsBlock, review, or require verification
A legitimate user can score high. A corporate proxy, a VPN, or a privacy browser all raise the score without any wrongdoing. Decide on Score plus Details plus the action context (signup, login, payment, withdrawal), never on the number alone. Tune your thresholds gradually as you observe real traffic.

How signals combine

The score is additive: each signal in Details contributes its points, the points are summed, and the total is capped at 100. A few rules shape which signals are added.
  • Anonymity signals are exclusive in priority order: Tor, then Privacy Relay, then VPN. If one of these fires, the others are not added on top. Tor is evaluated first and short-circuits the rest.
  • JavaScript-disabled short-circuits. If the WebRTC API is absent (a noscript beacon or a headless environment), scoring returns immediately with that one signal.
  • Anti-detect browser signals do not stack. One anti-detect verdict is enough. A second anti-detect indicator does not add a second penalty.
  • Proxy, Datacenter, and Abuser do stack. These IP-reputation signals add up with each other.
  • Everything is summed, then capped at 100.
For the full list of signals and their per-signal weights, see Signals.

VPN is corroborated, not guessed

ShieldLabs does not call a connection a VPN from a single IP-blocklist hit. VPN is asserted when at least 2 of 3 independent checks agree: IP reputation, the TCP/network fingerprint, and a failed real-IP (STUN) check. When there is no TCP data to consult, the threshold drops to 1 of 2 of the remaining checks. This corroboration is why the VPN signal surfaces connections that pure IP-blocklists miss. VPN services rotate IPs daily and residential proxies use consumer ISP addresses that never appear on a static list, so a blocklist lookup alone is not enough.

Explainable by design

Every score carries the reasoning with it. The Details array contains one entry per signal, each { "Value": <int>, "Description": "<signal>" }. The Value is the points that signal contributed, and the Description names the signal in plain language. The sum of the Value fields equals the Score (until the cap clamps a very high total down to 100). A sample webhook body for a visit on a proxy with a timezone mismatch:
{
  "RequestID": "13f84f05-3b2a-4f1e-9c7d-2a4b6e8f0a11",
  "DeviceID": "5eb7fd5c-8c2e-4a91-b0f3-1d7c9e2a4b55",
  "VisitorID": "161dfbad-2f4a-4c81-9e0b-7a3c5d8f1e22",
  "IP": "203.0.113.42",
  "OS": "Windows",
  "Country": "Germany",
  "UserHID": "u_7f3c9a2b",
  "Score": 20,
  "Details": [
    { "Value": 10, "Description": "Is proxy" },
    { "Value": 10, "Description": "Browser timezone ≠ IP-timezone" }
  ],
  "LastRequestTime": "2026-06-16T18:00:21.685Z",
  "Phase": "initial"
}
Here the score is 20 because the two signals contribute 10 points each. Because you can see both reasons, your code can decide differently for a payment screen than for a blog read, even at the same score.
The update webhook (Phase: "update") recomputes the score after the WebRTC real-IP check and carries only the delta signals in Details, not the full list. Correlate by RequestID and make your handler idempotent. See Webhooks.

999 is not a score

You may see the value 999 referenced internally. It is not part of the 0 to 100 scale and it is not a band. 999 is an internal rate-limit ban sentinel: when a single IP exceeds the REST request limit, the gateway marks it banned, and that sentinel never reaches a normal scored visit. A rate-limit ban surfaces to you as a separate gateway behavior, HTTP 429, not as a score in the payload. It is a DDoS protection on the infrastructure, and it does not feed the Risk Score. See Rate limits and Errors.
The customer-facing scale is 0 to 100 only. The four bands above are the complete set. There is no “Banned” band and no score above 100.

Act on score plus context

ShieldLabs surfaces signals. Your application owns the verdict. There is no in-product rules engine and no threshold UI: you read the Score and Details from the webhook or History API and write the allow, challenge, review, or block logic in your own code. A practical pattern is to weigh the score against the sensitivity of the action and against specific signals in Details. A Is proxy or Is tor entry on a withdrawal usually warrants a hard challenge, while an Is abuser flag on the device or account is worth treating as high-risk even at a moderate score.

Signals and weights

Every signal that can fire, its weight, and what it means.

Acting on the Risk Score

Turn Score plus Details into allow, challenge, review, or block in your own code.