Skip to main content
ShieldLabs bills per request, per domain. Every checkAnonymous, checkAuthenticatedUser, forceCheckAnonymous, or forceCheckAuthenticatedUser call runs a full identification and draws one request from that domain’s prepaid balance. There is no client-side result you can re-read for free: the snippet keeps only a short visit session (about 10 minutes, governing the SessionID), and the identification itself re-runs and bills on every call. So the only cost lever is where and how often you call, never reusing a stored id to dodge a billed call. This page has two parts: tune your integration before it ships, and localize a usage spike if one shows up.

Optimize proactively

The goal is one billed identification at each point you actually act on a result, with the connection already warm so that call is fast.

Warm the connection early

ShieldLabs talks to two hosts: the module loads from cdn.shieldlabs.ai, and the identification posts to rest.shieldlabs.ai (plus a local-network check). Hint both early so the TLS handshake is done before you ever call. Put these in <head>:
<link rel="preconnect" href="https://cdn.shieldlabs.ai" crossorigin />
<link rel="preconnect" href="https://rest.shieldlabs.ai" crossorigin />
<link rel="dns-prefetch" href="https://cdn.shieldlabs.ai" />
<link rel="dns-prefetch" href="https://rest.shieldlabs.ai" />
These hints cost nothing against your request balance. They only shave latency off the call you are about to make. If your site sends a strict Content-Security-Policy, the same two hosts already need allowlisting under Content Security Policy.

Pre-warm the module, trigger the call at the decision point

import() and the identification are two separate steps, and only the identification bills. Pre-warm the module on load so it is parsed and ready, then call checkAnonymous or forceCheck* only at the moment you act on the result, the login submit or the checkout click. That way you never spend a request on a page where you read nothing.
// On page load: warm the module only. This does NOT identify and does NOT bill.
const shieldlabs = import(
  'https://cdn.shieldlabs.ai/snippet.js?publicKey=YOUR_PUBLIC_KEY'
);

// At the decision point (e.g. the login submit): now run the billed identification.
loginForm.addEventListener('submit', async () => {
  const mod = await shieldlabs;
  mod.forceCheckAuthenticatedUser(hashedUserId, (ip, requestID) => {
    // forward requestID to your backend, read the score from the webhook or History API
  });
});
Do not call checkAnonymous or checkAuthenticatedUser unconditionally at the top of every page. That bills an identification on every page load, including pages where you never look at the score. Pre-warming the module is free; calling the function is what spends a request.

Call only where you act on the result

Map each touchpoint to one call and trigger it at the action, not on render. Each row below is one request.
TouchpointCallTriggerExpected volume
Logged-out browsing you want to gradecheckAnonymousOnce per visit, on the entry page you care about1 request per visit
Sign-up or loginforceCheckAuthenticatedUser(hashedUserId)On submit, after you know the user1 request per sign-in
Checkout or paymentforceCheckAnonymous or forceCheckAuthenticatedUserOn the place-order / pay click1 request per attempt
Other sensitive action (withdrawal, password change, device approval)forceCheck*On the action, not on page view1 request per action
The forceCheck* variants reset the visit session so the call starts a fresh SessionID, which is what you want right after login or before a high-stakes action. They do the same full identification as the plain calls and bill the same one request. Reach for them at meaningful moments, not in a loop. The four exports are detailed on the snippet page.

Diagnose a usage spike

If the request count climbs faster than your traffic explains, it is almost always one of three things: an implementation that calls too often, a key used from a domain you do not own, or a genuine traffic burst. Work through them in that order.

Implementation causes

Most unexpected usage is a call firing more often than intended. Check for:
  • Firing on every page load. An unconditional checkAnonymous in a shared layout or on every route runs an identification on every navigation. Move the call to the specific touchpoint where you act on the result.
  • Duplicate or looping calls. A component-lifecycle effect without a stable dependency re-runs the identify on every re-render. Pin the dependency list and guard against double invocation so each mount calls once. The snippet framework examples show the mount-once pattern.
  • Calling where the result is never used. Any page that identifies but never reads the score is pure spend. If you do not branch on it, do not call it there.

Key-misuse cause

Public keys are domain-scoped. A key used from a domain you did not register is rejected with 401, but the attempt still shows up as traffic against that key. If you see requests you cannot attribute to your own pages, your public key may be in use on a domain that is not yours.
A 401 from a domain mismatch is the server refusing an unregistered origin, not a scoring event. If a public key has leaked onto a site you do not control, rotate that domain’s key set so the old key stops working. Each domain has its own public and secret keys and its own domain registration.

Traffic cause

If the integration is clean and the keys are yours, a spike is real volume: a campaign, a referral surge, or a wave of automated traffic all inflate the count the same way, because each identification bills regardless of who is behind it. This is the case where the usage is legitimate and the answer is capacity, not a code fix.

How to investigate

The dashboard Data table is one row per identification, and reading or exporting it is free. Use it to localize the spike:
1

Export the spike window

On the dashboard Data tab, filter by the date range of the spike and export the result to CSV or JSON. Reads and exports never consume requests.
2

Group to find the source

In your own tooling, group the export by identifier (visitor ID, device ID, IP, or user HID), by entry URL, and by traffic source. A handful of identifiers or a single URL dominating the count points at a loop or a hot page; a broad spread across many IPs points at a real traffic surge.
3

Watch the balance and the 402 state

Track the domain’s remaining request balance. When a domain’s balance is exhausted, the identification POST and the Server API both return HTTP 402, separate from rate limiting. The Billing page covers the balance, and the rate limits page covers the gateway 429.
Searching the Data table by a single identifier (request ID, session ID, cookie ID, user HID, visitor ID, device ID, or IP) and filtering by score range and date is the fastest way to confirm whether a suspect entity is generating the extra calls. The troubleshooting page covers the broader integration issues you might surface along the way.

Next

Wire the score into your own decisions with acting on the Risk Score, and confirm what does and does not count against your balance on the Billing page.