IP Checker Verify
Device and IP verification for signups and free trials
How it works
- 01
Browser collects
verify.js runs in your page, collects the browser fingerprint and sends it to us. The same request gives us the visitor’s real IP.
- 02
You get a token
We combine device and network signals into a result and return a single-use, encrypted token that expires in minutes.
- 03
Your backend exchanges
Your server sends the token with its secret key and receives the full result. Store it and apply your own rules.
The fingerprint and result never pass through the browser in readable form, so users can’t tamper with them. Nothing about your end users is stored on our side.
Integrate in two steps
<!-- 1. Load once on the page that starts the trial -->
<script src="https://ipcheckhq.com/verify.js" async></script>
<script>
async function startTrial(form) {
const verifier = Verify.init({ publicKey: "pk_live_YOUR_PUBLIC_KEY" });
const { token } = await verifier.run({ reference: "trial-signup" });
// 2. Send the token to YOUR backend with the signup request
await fetch("/api/start-trial", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: form.email.value, verificationToken: token }),
});
}
</script>- Create a project in the console, add your app’s origin and create a secret key.
- Call
run()when the user submits the signup or trial form. - Tokens are single-use and expire after 2 minute(s). Exchange them before creating the account.
- If verify.js is blocked or fails, decide your fallback (allow, require a card, or manual review).
What your backend receives
- device.id
- Stable per project: the same browser gets the same ID in your project, and a different ID in other projects.
- device.visitor_id
- Raw browser fingerprint from the open-source library, with its confidence.
- device.*_hash
- Canvas, WebGL, audio and composite hashes for similarity matching.
- device.randomized_signals
- Signals the browser deliberately randomises (privacy browsers, some anti-detect tools).
- network.*
- IP, ASN, ISP, location, network type, and hosting / VPN / proxy / Tor flags with certainty. Observed server-side.
- consistency.*
- Browser timezone and language compared with the IP location.
- reference
- Whatever you passed to run(), returned unchanged.
{
"success": true,
"verification": {
"object": "verification",
"schema_version": 1,
"id": "ver_3kP9sQ2mX7vB1nR8tY4wZ6aC",
"project_id": "prj_Hx82kLq0Pz4mN7vT",
"reference": "trial-signup",
"origin": "https://app.example.com",
"created_at": "2026-01-01T12:00:00.000Z",
"expires_at": "2026-01-01T12:02:00.000Z",
"device": {
"id": "dev_7c1e9a0b4f2d8e6a5b3c9d1e0f7a2b4c",
"visitor_id": "8c37d02f891aa0b1c2d3e4f5a6b7c8d9",
"visitor_id_confidence": 0.6,
"composite_hash": "f1e2d3c4b5a6978877665544332211009988776655443322110099887766554433",
"component_hashes": {
"userAgent": "a1b2c3d4e5f60718",
"canvas": "0f1e2d3c4b5a6978"
},
"canvas": {
"status": "exposed",
"hash": "304ab52fef01d…"
},
"webgl": {
"status": "exposed",
"hash": "827189d231a…",
"vendor": "Google Inc. (Apple)",
"renderer": "ANGLE (Apple, Apple M2)"
},
"audio": {
"status": "exposed",
"hash": "9284c22a8f…",
"sample_rate": 48000
},
"fonts": {
"status": "exposed",
"detected_count": 22,
"detected": [
"Arial",
"Helvetica Neue",
"…"
]
},
"randomized_signals": [],
"browser": {
"name": "Chrome",
"version": "140",
"engine": "Blink",
"os": "macOS",
"os_version": "15.1",
"device_type": "desktop"
},
"environment": {
"timezone": "America/New_York",
"languages": [
"en-US",
"en"
],
"locale": "en-US",
"screen": {
"width": 1512,
"height": 982
},
"device_pixel_ratio": 2,
"color_depth": 30,
"cpu_threads": 10,
"device_memory_gb": 8,
"touch_points": 0,
"webdriver": false,
"cookies_enabled": true
}
},
"network": {
"ip": "203.0.113.42",
"ip_version": 4,
"asn": 7922,
"asn_name": "Comcast Cable Communications, LLC",
"isp": "Comcast Cable Communications, LLC",
"organization": "Comcast Cable Communications, LLC",
"network_type": "residential",
"hosting": false,
"proxy": false,
"vpn": false,
"tor": false,
"location": {
"country": "United States",
"country_code": "US",
"region": "New York",
"region_code": null,
"city": "New York",
"postal_code": null,
"latitude": 40.71,
"longitude": -74.01,
"timezone": "America/New_York"
},
"certainty": {
"network_type": "inferred",
"hosting": "inferred",
"proxy": "inferred",
"vpn": "inferred",
"tor": "likely"
}
},
"consistency": {
"timezone": {
"status": "matches",
"network": "America/New_York",
"browser": "America/New_York"
},
"language_region": {
"status": "matches"
},
"locale_region": {
"status": "matches"
}
},
"client": {
"user_agent": "Mozilla/5.0 …",
"accept_language": "en-US,en;q=0.9",
"client_type": "browser"
},
"trust": {
"server_observed": [
"network",
"client",
"origin",
"created_at"
],
"client_reported": [
"device"
],
"note": "Network data is observed by the server…"
}
}
}Building your duplicate-account rules
Common, effective patterns (implemented in your backend):
- Same
device.idalready used for a trial → require a card or deny the second trial. network.hostingorvpnis true plus a timezone mismatch → add friction (phone or card).- Many signups from one IP or /24 in a short window → rate-limit or review.
- Combine with your own signals: normalised email, disposable domains, card fingerprint.
Device signals are reported by the browser and can be spoofed by determined attackers. Treat them as risk signals, not proof, and avoid hard blocks on a single match (shared computers, offices and mobile carriers exist).
API reference
| Endpoint | Called from | Auth | Purpose |
|---|---|---|---|
| GET /verify.js | Browser | — | The SDK |
| POST /api/verify/v1/collect?key=pk_… | Browser (via SDK) | Public key + allowed origin | Returns a token |
| POST /api/verify/v1/exchange | Your server | Bearer secret key | Returns the result |
Errors use { success: false, error: { code, message } }. Exchange codes: UNAUTHORIZED, INVALID_TOKEN, TOKEN_EXPIRED, TOKEN_ALREADY_USED, RATE_LIMITED. SDK errors expose the same code on VerifyError. Machine-readable spec: /openapi.json.
Privacy and your obligations
- We don’t store your end users’ fingerprints, IPs or results. Only aggregate daily counts per project are kept.
- You are the controller of the data you receive. Disclose device fingerprinting in your privacy policy.
- In the EU/UK, fingerprinting generally requires consent unless strictly necessary; fraud prevention may qualify under legitimate interests, which you should document.
- Keep secret keys on your server. Rotate or revoke them in the console at any time.