Docs
IP Intelligence

IP Intelligence API

The IP Intelligence API returns geolocation, network ownership, threat flags, and TLS fingerprints for the caller's IP address. It's the same enrichment pipeline that annotates session replays — exposed as a standalone endpoint you can call from your own backend, edge worker, or signup flow.

Requests are served from CloudFront, which is what makes JA3/JA4 capture possible: the TLS handshake is fingerprinted at the edge before the request ever reaches a Lambda.

Endpoint

GET https://api.nevision.app/v1/lookup?siteId=YOUR_SITE_ID
Authorization: Bearer YOUR_API_KEY

Find siteId and the API key in your dashboard under Sites → [your site] → Install. Both are required — the siteId identifies which account the lookup is billed to, and the key authenticates it.

Authentication

Two equivalent forms, pick whichever your caller can produce:

  • Authorization: Bearer nv_xxx — preferred for server-to-server calls.
  • ?key=nv_xxx query parameter — for callers that can't set headers (image pixels, some CDN rules).

The API key is the same nv_* key the recorder uses. Treat it as a secret for server-side calls; if you expose it in a browser, scope the risk by remembering that it grants IP lookups and ingestion against your quota, nothing more.

Which IP gets looked up?

The endpoint enriches the IP of the caller — the address that opened the TLS connection. It does not accept an arbitrary ?ip= parameter. This is deliberate: JA3/JA4 fingerprints only exist for the live handshake, so a lookup of some third-party IP could never carry them. To enrich a visitor, call the endpoint from that visitor's browser, or forward the result from your edge.

Example request

curl "https://api.nevision.app/v1/lookup?siteId=YOUR_SITE_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "ip": "203.0.113.42",

  "asn": 13335,
  "asn_org": "CLOUDFLARENET",
  "tls_version": "TLSv1.3",
  "http_version": "HTTP/2.0",

  "country": "DE",
  "country_name": "Germany",
  "region": "BE",
  "region_name": "Berlin",
  "city": "Berlin",
  "postal_code": "10115",
  "time_zone": "Europe/Berlin",
  "latitude": 52.52,
  "longitude": 13.405,
  "metro_code": null,
  "user_agent": "Mozilla/5.0 ...",

  "ja3": "e7d705a3286e19ea42f587b344ee6865",
  "ja4": "t13d1516h2_8daaf6152771_02713d6af862",

  "is_vpn": false,
  "is_proxy": false,
  "is_tor": false,
  "is_datacenter": true,
  "is_anonymizer": false,
  "risk_score": 35,

  "source": "tor-list",
  "confidence": 0.9,
  "captured_via": "edge",

  "cache_added_at": "2026-01-14T09:12:44.108Z",
  "cache_last_updated": "2026-08-02T11:03:19.552Z",
  "cache_lookup_count": 1284
}

Field reference

  • Networkasn, asn_org, tls_version, http_version.
  • Geolocationcountry, country_name, region, region_name, city, postal_code, time_zone, latitude, longitude, metro_code. City-level accuracy; treat it as a signal, not an address.
  • Fingerprintsja3 and ja4 identify the caller's TLS stack (browser + OS build). They are null when the request didn't traverse the edge. Because they describe the device and not the network, they are never cached per-IP — every response carries the fingerprint from that request's own handshake.
  • Threat flagsis_vpn, is_proxy, is_tor, is_datacenter are booleans. is_anonymizer is true when any of VPN/proxy/Tor is true. risk_score is 0–100, derived from the flags weighted by confidence.
  • Provenancesource is which dataset produced the threat verdict (tor-list, waf, maxmind, ipinfo, internal, or none when no dataset matched). confidence is 0–1. captured_via is edge when the request came through CloudFront (full fidelity, fingerprints present) or server when it didn't.
  • Cache provenancecache_added_at, cache_last_updated, and cache_lookup_count are present when the response was assembled from a persisted row. Use them to judge freshness.

Errors

All errors return JSON with an error field.

  • 400 missing_site_id — the siteId query parameter is absent.
  • 401 missing_api_key — no Authorization header and no key parameter.
  • 403 invalid_api_key / invalid_site — the key doesn't match the site, or the site isn't active.
  • 403 origin_not_allowed — the request bypassed our CloudFront edge. Call the documented hostname, not a Lambda URL directly.
  • 429 rate_limited — per-site requests-per-minute ceiling exceeded. Back off and retry.
  • 429 monthly_quota_exceeded — includes limit and used in the body. Upgrade the plan or wait for the monthly reset.
  • 429 Too many requests — the source IP is temporarily blocked after repeated bad-key attempts.

Quotas

Lookups are counted per account, per calendar month (UTC):

  • Free — 1,000 lookups/month, hard cap.
  • Lite — 25,000 lookups/month.
  • Pro — 250,000 lookups/month.
  • Business — 2,500,000 lookups/month.

Paid plans burst to 2× the cap before refusing requests, so a traffic spike degrades gracefully instead of failing at exactly the limit. Current usage is shown on the Billing page and on IP Lookups.

Caching and cost

Enrichment for a given IP is persisted and refreshed in place, so repeat lookups of the same address are served cache-first. Every request still counts against your monthly quota — the cache reduces latency, not billing.

If you're calling this on every page view, cache the result in your own session store keyed by IP. Most fraud and personalization use cases only need one lookup per session.

Try it without an account

The free IP lookup tool and threat check tool run the same pipeline against your own IP, with a small daily cap and no signup. Useful for checking the response shape before you write code.