API reference

Documentation

One API key. Two endpoints — pick LITE for geo + ASN, MAX for threat signals. Quota for each bucket is tracked independently.

Implementing with an AI assistant?

Copy a self-contained Markdown spec — paste into Claude, ChatGPT, Cursor, or Copilot and ask it to build the client.

Quick start

  1. Sign up for a free account — Hobby is free, no credit card.
  2. Pick a plan on the pricing page. MAX bucket requires Plus or higher.
  3. Open your dashboard and create an API key.
  4. Make a request:
shell
# LITE — fast geo + ASN
curl -H "Authorization: Bearer LOOKIP_KEY" \
  https://api.lookip.io/v1/lookup/lite/8.8.8.8
shell
# MAX — threat signals + city-level geo
curl -H "Authorization: Bearer LOOKIP_KEY" \
  https://api.lookip.io/v1/lookup/max/8.8.8.8

The response JSON shape is stable per bucket — write your client once and it works across every plan.

Authentication

The API accepts two authentication schemes. Bearer tokens are preferred; the query parameter form exists for environments that cannot set custom headers (e.g. simple image proxies, webhooks).

http
# Preferred
Authorization: Bearer LOOKIP_KEY

# Fallback (URL form)
GET /v1/lookup/max/8.8.8.8?token=LOOKIP_KEY

Keys are server-side only. Never embed them in client-side JavaScript, mobile apps, or public repositories. Rotate keys from the dashboard if a leak is suspected — revocation is instant.

LITE vs MAX

Every plan ships two separate quotas. The two buckets are billed independently — burning LITE volume never blocks MAX.

LITEfast lookup

Geo + ASN. Sub-5ms p95, no per-request API cost. Use it when you only need country / network ownership.

  • Country, continent, countryCode
  • ASN, organization, domain
  • IPv4 + IPv6
MAXfull enrichment

Adds city-level geo, reverse DNS, threat signals, hosting flags, and mobile carrier data.

  • City, region, postal, lat/long, timezone, DMA, geoname
  • VPN / proxy / Tor / relay / residential-proxy + service name
  • Anonymous, anycast, hosting, mobile, satellite, residential-proxy flags
  • Mobile carrier (MCC / MNC)
  • Hostname (reverse DNS)

The bucket is picked by the URL path, not a header or body field — so the same caller can mix and match in the same day.

Endpoints

All endpoints live under /v1. Production base URL: https://api.lookip.io.

MethodPathBucketDescription
GET/v1/lookup/lite/:ipLITEFast geo + ASN. Bills the LITE bucket.
POST/v1/lookup/liteLITESame as GET but accepts an optional context body.
GET/v1/lookup/max/:ipMAXCity, threats, hostname, carrier. Bills the MAX bucket.
POST/v1/lookup/maxMAXSame as GET but accepts an optional context body.
POST/v1/batch/liteLITEUp to 100 IPs in one call against the LITE bucket.
POST/v1/batch/maxMAXUp to 100 IPs in one call against the MAX bucket.
GET/v1/lookup/meMAXLook up the caller’s own IP (MAX bucket).

Example batch request:

shell
# Batch lookups against the MAX bucket (use /v1/batch/lite for LITE).
curl -X POST https://api.lookip.io/v1/batch/max \
  -H "Authorization: Bearer LOOKIP_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ips": ["8.8.8.8", "1.1.1.1", "2606:4700:4700::1111"] }'

Every response includes X-Plan, X-Lookup-Kind, X-Quota-Limit, and X-Quota-Used headers so your client can track bucket usage without an extra round-trip.

Response shape

The response JSON is stable per bucket. LITE returns location + network; MAX adds threats, flags, mobile, and richer location/network fields.

LITE response
json
{
  "ip": "8.8.8.8",
  "location": {
    "country": "United States",
    "countryCode": "US",
    "continent": "North America",
    "continentCode": "NA"
  },
  "network": {
    "asn": "AS15169",
    "organization": "Google LLC",
    "domain": "google.com"
  },
  "lastUpdated": "2026-05-14T03:21:00Z",
  "datasetUpdatedAt": "2026-05-13"
}
MAX response
json
{
  "ip": "8.8.8.8",
  "hostname": "dns.google",
  "location": {
    "city": "Mountain View",
    "region": "California",
    "regionCode": "CA",
    "country": "United States",
    "countryCode": "US",
    "continent": "North America",
    "continentCode": "NA",
    "latitude": 37.4056,
    "longitude": -122.0775,
    "timezone": "America/Los_Angeles",
    "postalCode": "94043",
    "dmaCode": "807",
    "geonameId": "5375480",
    "accuracyRadiusKm": 50,
    "geoUpdatedAt": "2026-01-04"
  },
  "network": {
    "asn": "AS15169",
    "organization": "Google LLC",
    "domain": "google.com",
    "type": "hosting",
    "asnUpdatedAt": "2021-05-01"
  },
  "threats": {
    "service": "NordVPN",
    "lastSeen": "2026-05-10",
    "recentActivityPct": 85,
    "isProxy": false,
    "isRelay": false,
    "isTor": false,
    "isVpn": false,
    "isResidentialProxy": false
  },
  "flags": {
    "isAnonymous": false,
    "isAnycast": true,
    "isHosting": true,
    "isMobile": false,
    "isSatellite": false,
    "isResidentialProxy": false
  },
  "mobile": { "carrier": "T-Mobile", "mcc": "310", "mnc": "260" },
  "lastUpdated": "2026-05-14T03:21:00Z"
}

Optional context

Both POST /v1/lookup/lite and POST /v1/lookup/max accept an optional context object alongside the IP. These fields let you tag a request with surrounding session information so you can slice your usage logs by user, campaign, or workflow step inside the dashboard.

Context never leaves Lookip. It is stored only against your account's request log and is never forwarded to any third party.

shell
curl -X POST https://api.lookip.io/v1/lookup/max \
  -H "Authorization: Bearer LOOKIP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ip": "8.8.8.8",
    "context": {
      "userAgent": "Mozilla/5.0 …",
      "email":  "[email protected]",
      "username": "alice",
      "note": "checkout #4821",
      "tags": ["signup", "trial"]
    }
  }'
FieldPurpose
userAgentOriginating UA string of the end user.
emailEnd-user email for log correlation.
usernameYour internal username / handle.
firstName / lastNamePersonal name fields, if you collect them.
phoneE.164 phone number.
address, city, region, country, postalClaimed billing/shipping address — useful for fraud checks against IP geo.
noteFree-form short string (e.g. order id, ticket).
tagsArray of labels for filtering the dashboard.
extraOpen JSON object for anything else you need to attach.

Errors

Errors are returned as JSON with an error.code and a human-readable error.message. The HTTP status reflects the category. Quota errors also include the bucket so you can route retries.

CodeHTTPMeaning
invalid_ip400The supplied IP is not a valid IPv4 or IPv6 address.
unauthorized401Missing, invalid, or revoked API key.
no_active_plan402The account has no active subscription. Pick a plan to continue.
bucket_disabled402Your plan does not include the requested bucket. Hobby has LITE only — upgrade to Plus or higher for MAX.
quota_exhausted429The bucket's monthly allowance is used. The other bucket continues to work. The response body includes kind.
rate_limited429Per-second cap exceeded. Back off and retry with jitter.
lite_db_unavailable503LITE database not loaded yet (cold-start, <30s). Retry — no quota burned.
upstream_error5xxTransient server failure on a MAX lookup. Retry with backoff.

Rate limits & quotas

  • Monthly bucket quotas. LITE and MAX are counted separately. Resets at 00:00 UTC on your subscription anchor day.
  • Per-second soft cap. ~50 req/s per key across both buckets. Exceeding it returns rate_limited — retry after a short backoff.
  • Batch. Each IP inside a batch request counts as one lookup against the matching bucket.

The X-Lookup-Kind, X-Quota-Limit, and X-Quota-Used response headers carry live bucket usage so you can switch buckets gracefully before hitting 429.

SDKs & examples

The API is plain HTTP + JSON, so any client works. Here are minimal examples:

curl
shell
# MAX — threat signals + city-level geo
curl -H "Authorization: Bearer LOOKIP_KEY" \
  https://api.lookip.io/v1/lookup/max/8.8.8.8
JavaScript (fetch)
javascript
// Use /lite when you only need geo + ASN, /max for threat signals.
const r = await fetch("https://api.lookip.io/v1/lookup/max/8.8.8.8", {
  headers: { Authorization: `Bearer ${process.env.LOOKIP_KEY}` },
});
if (!r.ok) throw new Error(`lookup failed: ${r.status}`);
const data = await r.json();
console.log(data.location?.country, data.threats?.isVpn);
Python (requests)
python
import os, requests

# Pick the bucket explicitly via the URL path.
r = requests.get(
    "https://api.lookip.io/v1/lookup/max/8.8.8.8",
    headers={"Authorization": f"Bearer {os.environ['LOOKIP_KEY']}"},
    timeout=5,
)
r.raise_for_status()
print(r.json())

Need a typed client? The response shape per bucket is stable; copy it into your codebase as a Zod schema or TypeScript interface and you're done.