# IP Lookup Part of the Geoverio API platform. Shared conventions (auth, errors, rate limits) are in https://www.geoverio.com/llms-full.txt. Everything about an IP address in one call: geolocation, the network and owner (ASN, prefix, registry), what kind of connection it is (residential, mobile, business, hosting), and whether it is a VPN, proxy, Tor exit, iCloud Private Relay, datacenter, crawler or a known abuser β€” plus a 0-100 risk score with the exact reasons behind it. Answered from our own datasets built from public internet routing and operator data; no third-party lookup sits in the request path. - Base URL: `https://ip.geoverio.com` - Auth: `Authorization: Bearer YOUR_API_KEY` (required on every request) - Human docs: https://www.geoverio.com/docs/ip-lookup ## Use it for - Fraud and abuse prevention at signup, checkout or login - Blocking or challenging VPN / proxy / Tor traffic - Geolocating visitors for content, compliance and analytics - Separating real users from bots and datacenter traffic ## Endpoints ### GET /v1/ip/{ip} **Look up a single IP** Returns the full intelligence report for one IPv4 or IPv6 address. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `ip` | path | string | yes | The IPv4 or IPv6 address to look up. | | `fields` | query | string | no | Comma-separated sections to include, so you only pay for the parsing you need. Valid sections: location, network, flags, risk, type, usage_type, address_type, anonymized, sources, reasons. ip, version and the bogon fields are always returned. Omit for the full report. | ```bash curl "https://ip.geoverio.com/v1/ip/8.8.8.8" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Example response: ```json { "ip": "8.8.8.8", "version": 4, "bogon": false, "bogon_reason": null, "bogon_name": null, "location": { "country": "US", "region": null, "district": null, "city": null, "postal": null, "latitude": 38, "longitude": -97, "timezone": "America/Chicago", "calling_code": "1", "idd_code": "1", "area_code": "316", "coordinate_source": "country_centroid", "source": "rir-allocation", "confidence": 0.6 }, "network": { "asn": 15169, "as_name": "GOOGLE - Google LLC", "as_country": "US", "as_registry": "arin", "org": "Google LLC", "isp": "Google LLC", "as_domain": "about.google", "prefix": "8.8.8.0/24", "netname": "GOGL", "assignment_cidr": "8.8.8.0/24", "routed": true, "registry": "arin", "allocated": "2023-12-28", "allocation_status": "allocated", "net_speed": "T1", "rdns": "dns.google" }, "continent": { "code": "NA", "name": "North America", "hemisphere": [ "north", "west" ] }, "country_info": { "name": "United States", "official_name": "United States of America", "alpha2_code": "US", "alpha3_code": "USA", "numeric_code": 840, "demonym": "American", "flag_emoji": "πŸ‡ΊπŸ‡Έ", "capital": "Washington D.C.", "total_area": 9372610, "population": 326687501, "currency": { "code": "USD", "name": "United States dollar", "symbol": "$" }, "language": { "code": "eng", "name": "English" }, "tld": ".us" }, "time_zone_info": { "olson": "America/Chicago", "current_time": "2026-07-30T00:46:18-05:00", "gmt_offset": -18000, "utc_offset": "-05:00", "is_dst": true, "abbreviation": "CDT", "dst_start_date": "2026-03-08", "dst_end_date": "2026-11-01", "sunrise": "06:28", "sunset": "20:40" }, "elevation": null, "weather_station": null, "mobile": null, "ads_category": { "code": "IAB19-11", "name": "Data Centers" }, "type": "hosting", "usage_type": "Data Center / Hosting / Transit", "address_type": "anycast", "anonymized": false, "flags": { "vpn": false, "vpn_provider": null, "proxy": false, "tor": false, "icloud_relay": false, "hosting": true, "mobile": false, "crawler": false, "crawler_name": null, "abuser": false, "anycast": true, "bogon": false, "is_spammer": false, "is_scanner": false, "is_botnet": false, "is_ai_crawler": false }, "risk": { "score": 15, "level": "low", "reasons": [ "hosting: +15" ] }, "reasons": [ { "flag": "hosting", "source": "x4b_datacenter", "detail": "listed datacenter range" }, { "flag": "anycast", "source": "measurement_anycast", "detail": "latency from distant probes is physically impossible for one host" } ], "sources": { "snapshot_id": 74, "snapshot_created_at": "2026-07-30T01:33:59.279660+00:00", "engine_version": "0.9.3", "layers": [ "rir", "bgp", "classification" ], "enrichment": [] } } ``` ### GET /v1/ip/self **Look up the caller's own IP** Same report, for the public IP the request came from (X-Forwarded-For aware). Handy for client-side "where am I / am I on a VPN" checks. | Parameter | In | Type | Required | Description | |---|---|---|---|---| | `fields` | query | string | no | Comma-separated sections to include, so you only pay for the parsing you need. Valid sections: location, network, flags, risk, type, usage_type, address_type, anonymized, sources, reasons. ip, version and the bogon fields are always returned. Omit for the full report. | ```bash curl "https://ip.geoverio.com/v1/ip/self" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### POST /v1/ip/batch **Look up many IPs at once** Send up to 100 IPs in one call. Body: {"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}. Each result is returned in order; an invalid IP comes back with an "error" object instead of failing the batch. ```bash curl -X POST "https://ip.geoverio.com/v1/ip/batch" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ips": ["8.8.8.8", "1.1.1.1"], "fields": "flags,risk"}' ``` ## Response fields - `ip / version` β€” The address you asked about, echoed back, and 4 or 6 for the IP version. Useful when you are looping over a batch and need to match results to inputs. - `bogon / bogon_reason / bogon_name` β€” true when the address can never appear on the public internet β€” private ranges (10.x, 192.168.x), loopback, link-local, documentation ranges and unallocated space. bogon_reason and bogon_name tell you which category it is. A bogon in your traffic almost always means you are reading the wrong header and seeing your own load balancer instead of the visitor. - `location` β€” country, region, district, city, postal, latitude, longitude and timezone, plus calling_code / idd_code / area_code for the phone numbering plan. source names the layer that produced it (geofeed, measurement, cloud, whois, rdns or rir-allocation, best first) and confidence is 0-1. coordinate_source separately tells you whether the coordinates are a real city location or a country centroid β€” treat country_centroid as "we know the country, not the city". - `network` β€” The owning network: asn, as_name, org and isp (human-readable owner), as_domain, as_country, announced prefix, netname, assignment_cidr, routed, registry / as_registry (the RIR), allocated date, allocation_status, rdns (reverse-DNS hostname) and net_speed (a coarse T1 / DSL / Mobile bucket inferred from the connection type). - `continent` β€” code (NA, EU, AS…), name, and hemisphere as a pair like ["north","west"]. - `country_info` β€” Everything about the country in one object: name, official_name, alpha2_code, alpha3_code, numeric_code, demonym, flag_emoji, capital, population, total_area, tld, plus currency {code, name, symbol} and language {code, name}. Enough to localise a checkout without a second lookup. - `time_zone_info` β€” olson name, current_time at that location, gmt_offset in seconds, utc_offset as Β±HH:MM, is_dst, abbreviation (CDT, PST…), dst_start_date / dst_end_date, and sunrise / sunset. Handy for "do not email this user at 3am" logic. - `elevation / weather_station / mobile` β€” Derived extras, each null when we cannot determine it precisely enough: elevation in metres, the nearest NOAA weather_station, and mobile carrier details (name, MCC, MNC) for cellular addresses. - `ads_category` β€” IAB content category for the network, as {code, name} β€” for example {"code":"IAB19-11","name":"Data Centers"}. null when no category applies. - `type` β€” Connection type as a machine-readable slug: residential, mobile, business, hosting, education, government, or unknown. This is the field to branch on in code. - `usage_type` β€” The same thing as a display label ("Data Center / Hosting / Transit", "Residential / Fixed Line ISP", "Mobile / Cellular"). Show this one to humans; branch on type. - `address_type` β€” unicast, anycast, multicast or reserved. anycast means the address is announced from many physical locations at once, so a single city-level answer would be meaningless for it. - `anonymized` β€” true when the address belongs to any anonymising service β€” VPN, proxy, Tor or iCloud Private Relay. One boolean if you do not want to read four flags. - `flags` β€” The booleans you act on: vpn (+ vpn_provider), proxy, tor, icloud_relay, hosting, mobile, crawler (+ crawler_name), abuser, anycast, bogon, and the abuse breakdown is_spammer, is_scanner, is_botnet, is_ai_crawler. - `risk` β€” score 0-100, level (minimal under 15, low 15-39, medium 40-74, high 75+), and reasons listing each signal with the points it contributed, e.g. "vpn (NordVPN): +25". Weights are tuned for fraud prevention: confirmed abuse dominates, anonymisers are high, hosting is a mild signal, clean residential is ~0. - `reasons` β€” The full evidence trail: every flag that was set, with the source or heuristic that set it and a human-readable detail. Nothing in the report is a black box β€” if you ever need to justify a block to a customer, this is the field to quote. - `sources` β€” Which data layers contributed (rir, bgp, classification, geofeed, cloud, whois, rdns, measurement), the snapshot_id and snapshot_created_at of the dataset that served the answer, the engine_version, and any external enrichment used. Two lookups sharing a snapshot_id were answered from identical data. ## Speed, caching and freshness ### Typical response time A warm lookup answers in roughly 15 ms end to end. The core report β€” routing, registry, classification, risk β€” is served from an in-memory dataset with no database and no third-party API in the request path, so that part is sub-millisecond; the rest is network time to you. ### The slow path is bounded City-level detail sometimes needs a live registry (RDAP) or reverse-DNS call. Those are capped by a single wall-clock budget of 500 ms for the whole refinement stage, so no lookup waits on a slow registry beyond that. If the budget runs out, you still get a complete report β€” just with a coarser location and a lower confidence β€” and the outstanding fetch is allowed to finish in the background so the next caller gets the better answer. 500 ms is a ceiling, not a typical figure. ### Caching works per network, not per address Registry and geofeed answers describe a whole assignment, so they are cached against the prefix rather than the address. Looking up one address in a /22 warms all 1024 of them: the first lookup into an unfamiliar network may hit the slow path, and its neighbours come back fast. Reverse DNS is the exception β€” a PTR record belongs to a single address and is cached per address for 24 hours. ### You do not need to cache on your side There is no benefit to holding responses for speed, and doing so costs you accuracy: classification and routing data are recompiled continuously, so a cached report goes stale in ways a live one does not. If you do cache β€” for rate-limit reasons, say β€” key it on the address and expire within a day, and read sources.snapshot_id to tell whether two reports came from the same dataset. ### Batch is one round trip, not one hundred POST /v1/ip/batch resolves up to 100 addresses concurrently inside a single request, which is substantially faster than the same 100 lookups issued serially and counts the same against your quota. Use it whenever you have more than a handful of addresses to check at once. ## Errors - **401** Missing or invalid API key β€” The Authorization header is missing, malformed, or the key does not exist. Double-check you copied the whole key, including the gv_live_ prefix. - **403** Key not allowed β€” Your key is valid but not allowed to call this API β€” usually the key was revoked or your plan does not include this module. - **422** Invalid IP address β€” The address in the path is not a valid IPv4 or IPv6 literal. In batch calls, invalid IPs come back per-item under "error" rather than failing the whole request. - **429** Rate limit exceeded β€” You sent too many requests this minute. Check the X-RateLimit-Reset header for when the window resets, then retry. Retry `429` and `5xx` with exponential backoff; never retry other `4xx` codes unchanged. ## Rate limit headers - `X-RateLimit-Limit` β€” Your plan's requests-per-minute allowance. - `X-RateLimit-Remaining` β€” Requests you have left in the current minute. - `X-RateLimit-Reset` β€” Unix timestamp when the window resets.