Skip to content

IP Lookup

In short

The IP Lookup documentation covers authentication, the lookup endpoint, and every response field — location, network, ASN, VPN/proxy/Tor flags and the risk score — plus error handling and copy-ready examples. Requests use the same bearer token as the rest of the Geoverio platform.

Last reviewed 11 September 2026

Key facts

authentication_method
Bearer token
free_tier_available
true
response_format
JSON
base_url
https://ip.geoverio.com

Geolocation, network owner, VPN/proxy/Tor detection and a risk score for any IP

Base URL https://ip.geoverio.com

Overview

What IP Lookup does

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.

Perfect 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

Authentication

Every request needs a key

Send your key as a bearer token in the Authorization header. That is the only accepted scheme — there is no query-string key and no cookie login.

Header Authorization: Bearer gv_live_…
Question Answer
Where do I get one? Select a plan first — the free tier counts — then open Dashboard → Projects. Keys belong to a project: open one and choose Create key. If you are not signed in yet, that link takes you through sign-in first.
What does it look like? A long string beginning gv_live_. Copy the whole thing — a truncated key returns 401.
Where do I keep it? On your server, in an environment variable. Never in browser JavaScript, a mobile app bundle, or a public repository — anyone who reads it can spend your quota.
Leaked it? Revoke it from the project that owns it under Projects and create a new one. Revoking takes effect immediately.

New here? The getting started guide walks through account, plan, key and first call step by step.

Reference

Every endpoint, explained

Copy a sample, swap in your key, and you are live. Each endpoint lists exactly what it accepts and returns.

GET /v1/ip/{ip} Look up a single IP

Returns the full intelligence report for one IPv4 or IPv6 address.

Parameters

Name Type Required Description Example
ip string required The IPv4 or IPv6 address to look up. 8.8.8.8
fields string optional 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. location,flags,risk

Code samples

# Keep the key in the environment, not in your shell history:
#   export GEOVERIO_API_KEY="gv_live_…"

curl --fail-with-body "https://ip.geoverio.com/v1/ip/8.8.8.8?fields=location%2Cflags%2Crisk" \
  -H "Authorization: Bearer $GEOVERIO_API_KEY"
// Call this from your server, never from the browser:
// front-end JavaScript ships your key to every visitor.
const KEY = process.env.GEOVERIO_API_KEY;

async function main() {
  const res = await fetch("https://ip.geoverio.com/v1/ip/8.8.8.8?fields=location%2Cflags%2Crisk", {
    headers: {
      Authorization: `Bearer ${KEY}`
    }
  });

  // Never parse the body before checking the status: an expired key answers
  // 401 with a perfectly valid JSON envelope.
  if (!res.ok) {
    throw new Error(`HTTP ${res.status} — ${await res.text()}`);
  }

  console.log(await res.json());
}

main().catch((err) => {
  console.error(err);
  process.exitCode = 1;
});
# pip install requests
import os, requests

url = "https://ip.geoverio.com/v1/ip/8.8.8.8?fields=location%2Cflags%2Crisk"
headers = {"Authorization": "Bearer " + os.environ["GEOVERIO_API_KEY"]}

r = requests.get(url, headers=headers)
r.raise_for_status()   # turns 4xx / 5xx into an exception instead of silent bad data
print(r.json())
<?php
// Read the key from the server environment — never commit it.
$key = getenv('GEOVERIO_API_KEY');

$ch = curl_init("https://ip.geoverio.com/v1/ip/8.8.8.8?fields=location%2Cflags%2Crisk");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $key",
    "Accept: application/json",
]);

$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status < 200 || $status >= 300) {
    fwrite(STDERR, "Request failed — HTTP $status: $body\n");
    exit(1);
}

print_r(json_decode($body, true));
See a sample response
{
    "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": "\ud83c\uddfa\ud83c\uddf8",
        "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.

Parameters

Name Type Required Description Example
fields string optional 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. flags,risk

Code samples

# Keep the key in the environment, not in your shell history:
#   export GEOVERIO_API_KEY="gv_live_…"

curl --fail-with-body "https://ip.geoverio.com/v1/ip/self?fields=flags%2Crisk" \
  -H "Authorization: Bearer $GEOVERIO_API_KEY"
// Call this from your server, never from the browser:
// front-end JavaScript ships your key to every visitor.
const KEY = process.env.GEOVERIO_API_KEY;

async function main() {
  const res = await fetch("https://ip.geoverio.com/v1/ip/self?fields=flags%2Crisk", {
    headers: {
      Authorization: `Bearer ${KEY}`
    }
  });

  // Never parse the body before checking the status: an expired key answers
  // 401 with a perfectly valid JSON envelope.
  if (!res.ok) {
    throw new Error(`HTTP ${res.status} — ${await res.text()}`);
  }

  console.log(await res.json());
}

main().catch((err) => {
  console.error(err);
  process.exitCode = 1;
});
# pip install requests
import os, requests

url = "https://ip.geoverio.com/v1/ip/self?fields=flags%2Crisk"
headers = {"Authorization": "Bearer " + os.environ["GEOVERIO_API_KEY"]}

r = requests.get(url, headers=headers)
r.raise_for_status()   # turns 4xx / 5xx into an exception instead of silent bad data
print(r.json())
<?php
// Read the key from the server environment — never commit it.
$key = getenv('GEOVERIO_API_KEY');

$ch = curl_init("https://ip.geoverio.com/v1/ip/self?fields=flags%2Crisk");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $key",
    "Accept: application/json",
]);

$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status < 200 || $status >= 300) {
    fwrite(STDERR, "Request failed — HTTP $status: $body\n");
    exit(1);
}

print_r(json_decode($body, true));

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.

Code samples

# Keep the key in the environment, not in your shell history:
#   export GEOVERIO_API_KEY="gv_live_…"

curl --fail-with-body -X POST "https://ip.geoverio.com/v1/ip/batch" \
  -H "Authorization: Bearer $GEOVERIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

This endpoint takes a JSON body. The object above is a placeholder built from the documented parameters — the exact shape is described in the endpoint summary above.

// Call this from your server, never from the browser:
// front-end JavaScript ships your key to every visitor.
const KEY = process.env.GEOVERIO_API_KEY;
const payload = {};

async function main() {
  const res = await fetch("https://ip.geoverio.com/v1/ip/batch", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  });

  // Never parse the body before checking the status: an expired key answers
  // 401 with a perfectly valid JSON envelope.
  if (!res.ok) {
    throw new Error(`HTTP ${res.status} — ${await res.text()}`);
  }

  console.log(await res.json());
}

main().catch((err) => {
  console.error(err);
  process.exitCode = 1;
});

This endpoint takes a JSON body. The object above is a placeholder built from the documented parameters — the exact shape is described in the endpoint summary above.

# pip install requests
import os, requests

url = "https://ip.geoverio.com/v1/ip/batch"
headers = {"Authorization": "Bearer " + os.environ["GEOVERIO_API_KEY"]}
payload = {}

r = requests.post(url, headers=headers, json=payload)
r.raise_for_status()   # turns 4xx / 5xx into an exception instead of silent bad data
print(r.json())

This endpoint takes a JSON body. The object above is a placeholder built from the documented parameters — the exact shape is described in the endpoint summary above.

<?php
// Read the key from the server environment — never commit it.
$key = getenv('GEOVERIO_API_KEY');
$payload = <<<'JSON'
{}
JSON;

$ch = curl_init("https://ip.geoverio.com/v1/ip/batch");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $key",
    "Accept: application/json",
    "Content-Type: application/json",
]);

$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status < 200 || $status >= 300) {
    fwrite(STDERR, "Request failed — HTTP $status: $body\n");
    exit(1);
}

print_r(json_decode($body, true));

This endpoint takes a JSON body. The object above is a placeholder built from the documented parameters — the exact shape is described in the endpoint summary above.

Response fields

What every field means

No mystery keys. Here is a plain-English explanation of everything the API sends back.

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.

Good to know

Speed, caching and freshness

What to expect from the API in production — response times, what is cached where, and how current the data is.

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

When things go wrong

Every failure is JSON, and every failure carries the same field. Here is the shape, then every status code you can actually receive.

The error envelope

The API returns one field, detail, with a human-readable explanation. Parse that and you have handled every error on every endpoint.

401 https://ip.geoverio.com
{
  "detail": "Invalid API key"
}
Status Name What to do
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.
400 Bad request The query string or JSON body could not be parsed — an unencoded character, a truncated body, or the wrong Content-Type. Fix the request; retrying it unchanged will fail again.
404 No such endpoint The path does not exist on this API. Check the base URL and the version prefix — every path on this page starts with /v1.
405 Wrong method Right path, wrong verb — for example a GET against an endpoint documented as POST. The reference above lists the verb for every endpoint.
429 Monthly quota exhausted Different from the rate limit above: your plan's allowance for the calendar month is gone, so waiting a minute will not help. X-RateLimit-Remaining is 0 and X-RateLimit-Reset points at the start of the next period. Upgrade your plan or wait for the reset.
500 Internal error Something failed on our side. The request was not your fault and was not billed — retry with backoff.
502 Bad gateway An upstream hop failed mid-request. Safe to retry with backoff.
503 Temporarily unavailable The API is draining or deploying. Safe to retry with backoff; honour Retry-After when it is present.
504 Upstream timeout The request took longer than the gateway allows. Safe to retry with backoff — and make sure your own client timeout is generous enough not to abandon a request the API is still answering.

Retrying safely

4xx means stop. The request is wrong and will stay wrong — fix it rather than repeating it. The one exception is 429: wait for the window in X-RateLimit-Reset and try again.

5xx means retry, but back off. Every endpoint on this page is a read, so a retry is safe. Wait 1s, then 2s, then 4s, then 8s — up to four attempts — and add a random 0–250 ms jitter so a fleet of your servers does not retry in lockstep. Give up after that and surface the failure; honour Retry-After whenever the response carries one.

Limits

How much you can call, and how fast

Two separate budgets, and you can hit either one. Quota is a monthly total, counted per account — every key in every project spends the same pool. Rate is a per-minute ceiling on how quickly you spend it. Both are reported on every response by the X-RateLimit-* headers, so your code can see either limit coming before it arrives.

Plan Requests / month Requests / minute API keys
Free 1,000 30 1
Pay As You Go Metered — no monthly cap 600 5
Starter 100,000 300 3
Growth 1,000,000 1,200 10
Scale 5,000,000 5,000 50

Pay As You Go is metered rather than capped: it has no monthly allowance to exhaust, it draws on a prepaid balance, and it stops when that balance runs out. Every other plan returns 429 with X-RateLimit-Remaining: 0 once its monthly quota is spent, until the next period begins. Full pricing lives on the pricing page.

Rate limits

Headers that keep you informed

Check these response headers to see exactly how much room you have left — before you ever hit a limit.

Header Meaning
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.

Ready for your own key?

Create a free account, mint a key, and try every endpoint live from your dashboard’s Test APIs page.

Frequently asked questions

What does the IP Lookup API do?
Geolocation, network owner, VPN/proxy/Tor detection and a risk score for any IP.
How do I authenticate with the IP Lookup API?
Send your key as a bearer token: `Authorization: Bearer <your key>`. One key works across every Geoverio API your plan includes.
Is there a free tier?
Yes. Every plan reaches every API; the free tier differs on monthly volume and rate limit, not on which endpoints you may call.