Docs
Response schema
Every successful /v1/check call returns this structure. Fields are stable; new fields may be added over time but existing ones won't change meaning without a version bump.
More signals, fewer false positives. The response now carries eight additional signals: three that catch more abuse (drop-list, bogon, RPKI), three that reduce false positives by recognising benign networks (relay, satellite, public resolver), and two of network context (routing health and RIR allocation). Every signal is one input, not a verdict — read each with its evidence label and combine them yourself.
Breaking change (0.8.0):signals.is_datacenteris gone. A datacenter IP is nowsignals.connection_type === "datacenter", which also distinguishes"satellite"(e.g. Starlink) and"unknown". The corresponding evidence key isevidence.connection_type(wasevidence.datacenter). See the changelog.
{ "ip": "8.8.8.8", "version": 4, "geo": { "country": "United States", "country_code": "US", "region": "California", "city": "Mountain View", "latitude": 37.4, "longitude": -122.1, "timezone": "America/Los_Angeles" }, "network": { "asn": 15169, "as_org": "Google LLC", "is_announced": true, "is_bogon": false, "rpki": "valid", "allocation_date": "2000-03-30", "allocation_age_days": 9566, "registration_country": "US" }, "signals": { "connection_type": "datacenter", "datacenter_provider": "gcp", "relay_provider": null, "is_relay": false, "is_tor": false, "is_vpn": false, "is_proxy": false, "is_public_resolver": true, "is_drop_listed": false, "recent_abuse": false, "is_verified_bot": false, "verified_bot_name": null }, "evidence": { "connection_type": "authoritative", "tor": "authoritative", "vpn": "inferred", "proxy": "beta", "verified_bot": "authoritative", "relay": "authoritative", "routing": "authoritative", "allocation": "authoritative", "drop_listed": "authoritative", "recent_abuse": "beta", "public_resolver": "authoritative" }, "risk": { "score": 20, "level": "low", "reasons": [ "connection_type:datacenter", "benign_network_kind" ] }, "attribution": "https://geoq.io/attributions" }
Fields
| Field | Type | Description |
|---|---|---|
ip | string | The IP address that was checked. |
version | number | IP version: 4 or 6. |
geo.country | string | Country name. |
geo.country_code | string | ISO 3166-1 alpha-2 country code. |
geo.region | string | Region / state (best-effort). |
geo.city | string | City (best-effort). |
geo.latitude | number | Approximate latitude of the network. |
geo.longitude | number | Approximate longitude of the network. |
geo.timezone | string | IANA timezone, e.g. America/Los_Angeles. |
network.asn | number | Autonomous System Number. |
network.as_org | string | AS organisation name. |
network.is_announced | boolean | Whether a route for this IP is seen in public BGP tables. |
network.is_bogon | boolean | IP is a bogon — unallocated or reserved space that should never source traffic. |
network.rpki | string | Route origin validation state: valid / invalid / unknown. |
network.allocation_date | string|null | When the RIR delegated this range (ISO date), or null. |
network.allocation_age_days | number|null | Days since allocation_date, or null. |
network.registration_country | string|null | ISO country code the range is registered to (may differ from geo). |
signals.connection_type | string | Network kind: "datacenter" | "satellite" | "unknown". Replaces is_datacenter. |
signals.datacenter_provider | string|null | Provider code (aws, gcp, azure, …) when connection_type is datacenter, else null. |
signals.relay_provider | string|null | Privacy-relay operator (e.g. "icloud") when is_relay is true, else null. |
signals.is_relay | boolean | IP is a privacy-relay exit (e.g. Apple iCloud Private Relay) — a benign network kind. |
signals.is_vpn | boolean | IP is in a known commercial VPN range. |
signals.is_proxy | boolean | Open/anonymising proxy (residential detection beta). |
signals.is_tor | boolean | IP is a current Tor exit node. |
signals.is_public_resolver | boolean | IP is a public DNS resolver (e.g. 8.8.8.8, 1.1.1.1) — benign infrastructure. |
signals.is_drop_listed | boolean | IP is on the Spamhaus DROP list — a do-not-route range of known hostile networks. |
signals.recent_abuse | boolean | IP appears on recent abuse feeds (Emerging Threats / CINS). Beta — scores zero today. |
signals.is_verified_bot | boolean | IP is a verified good crawler from a search/AI operator's published ranges (the bot you must NOT block). NOT bad-bot detection. |
signals.verified_bot_name | string|null | The verified crawler's name (e.g. "googlebot", "bingbot") when is_verified_bot is true, else null. |
evidence.connection_type | string | How directly we observe the connection-type signal: authoritative / inferred / beta. |
evidence.tor | string | Evidence label for the Tor signal. |
evidence.vpn | string | Evidence label for the VPN signal. |
evidence.proxy | string | Evidence label for the proxy signal. |
evidence.verified_bot | string | Evidence label for the verified-bot signal. |
evidence.relay | string | Evidence label for the relay signal (authoritative). |
evidence.routing | string | Evidence label for the routing signals — is_announced / is_bogon / rpki (authoritative). |
evidence.allocation | string | Evidence label for the allocation fields (authoritative). |
evidence.drop_listed | string | Evidence label for the drop-list signal (authoritative). |
evidence.recent_abuse | string | Evidence label for recent-abuse (beta). |
evidence.public_resolver | string | Evidence label for the public-resolver signal (authoritative). |
risk.score | number | 0–100 capped sum of triggered signal weights (after the benign-network suppressor). |
risk.level | string | low (<30), medium (30–59), high (≥60). |
risk.reasons | string[] | The signal keys that contributed to the score (plus benign_network_kind when the score was suppressed). |
attribution | string | URL to the required attribution page. |
The evidence object
For each signal we tell you how directly we observe it — not how likely it is to be correct. This is a transparency label, not a probability or accuracy claim.
| Label | Meaning |
|---|---|
authoritative | Drawn from operator-published ranges or lists (e.g. cloud provider CIDRs, the Tor exit list, Googlebot's published ranges). The most direct observation we have. |
inferred | Derived at the ASN level rather than from a published list — a reasoned attribution, not a direct one. |
beta | Heuristic and partial; still maturing. Weight it accordingly. |
The new signal groups
The eight signals added in 0.8.0 fall into three groups:
- False-positive reducers —
is_relay(withrelay_provider),connection_type === "satellite"andis_public_resolver. These describe benign network kinds. When any of them is true, the risk score is capped at 20 andbenign_network_kindappears inreasons[]— so an Apple iCloud Private Relay or Starlink user isn't scored like a hostile datacenter. See the risk-score methodology. - Precision signals —
is_drop_listed(Spamhaus DROP),is_bogonandrpkirouting validation. These add weight where the network itself is hostile or malformed. - Network context — routing (
is_announced) and RIR allocation (allocation_date,allocation_age_days,registration_country). Context to reason with, not scored on their own.
recent_abuse is beta and carries zero weight today — treat it as a hint, weighted by its evidence label.
About is_verified_bot
is_verified_bot identifies a verified good crawler — Googlebot, Bingbot and similar, matched against the operator's own published ranges. It is the bot you must not block. It is not behavioural bad-bot detection (that's on the roadmap). Because a verified Googlebot is not fraud, it carries zero risk weight and never appears in risk.reasons.
Notes on honesty
geois approximate and locates the network, not the device.signals.is_proxyis beta (residential-proxy detection in particular) — see itsevidencelabel.- The
evidencelabels describe observation directness, not accuracy or probability. connection_type,torandverified_botsignals cover both IPv4 and IPv6; geo and ASN always have.connection_typeonly ever emitsdatacenter,satelliteorunknown— we never guess "residential" or "mobile".- All signals are probabilistic.
risk.reasonsshows exactly what fired so you can audit any score. attributionmust be honoured on the Free tier — see attributions.