OpenIPApi

API Documentation

Everything you need to integrate OpenIPApi into your application. Base URL: https://api.openipapi.com (versioned as /v1).

Signals, not verdicts. IP intelligence is probabilistic. threat_score and VPN/proxy/Tor flags are inputs to your decision logic. Combine them with account, payment, device and behavior signals — never use them as the sole basis for blocking. See Data Methodology for details.

Authentication

All API requests must include your API key in the X-API-Key request header. You can find your API key in the console.

X-API-Key: oip_your_api_key_here
Keep your API key secret. Never expose it in client-side JavaScript or public repositories.

Base URL

All API requests are made to the following base URL over HTTPS:

https://api.openipapi.com

Machine-readable spec (OpenAPI 3.1): /openapi.yaml.

Response format

All responses are JSON. Successful responses return HTTP 200. Error responses return an appropriate 4xx or 5xx status code with an error field.

{
  "error": "invalid_api_key",
  "message": "The provided API key is invalid or has been revoked.",
  "status": 401
}

GET /v1/lookup/{ip}

Look up geolocation, network, and threat data for a single IP address.

GET https://api.openipapi.com/v1/lookup/{ip}

Parameters

Parameter Type Description
ip path IPv4 or IPv6 address to look up.
fields query (optional) Comma-separated list of top-level fields to return: geo,network,threat,abuse.
date query (optional) Historical lookup — return the snapshot for this IP on the given date (YYYY-MM-DD). Up to 365 days back. Requires Pro+. Returns a no_snapshot error if no data is available for that date.

Response fields

Field Description Plan
ip The queried IP address. All
type IPv4 or IPv6. All
geo.country_code 2-letter ISO 3166-1 country code. All
geo.country Full country name. All
geo.region Region / state name. All
geo.region_code Region / state code. All
geo.city City name. All
geo.postal_code Postal / ZIP code. All
geo.latitude Latitude (decimal degrees). All
geo.longitude Longitude (decimal degrees). All
geo.timezone IANA timezone identifier (e.g. Europe/Berlin). All
geo.is_sanctioned True if the country is on OFAC, EU, or UN sanction lists. All
geo.sanction_lists Array of applicable sanction programs, e.g. ["OFAC","EU"]. All
network.asn Autonomous System Number. Starter+
network.as_name AS organisation name. Starter+
network.as_domain AS organisation domain. Starter+
network.isp Internet Service Provider name. All
network.organization Network organisation name. All
network.connection_type residential, datacenter, mobile, education, or government. All
threat.is_vpn True if the IP is a known VPN endpoint. Pro+
threat.is_proxy True if the IP is a known open proxy. Pro+
threat.is_tor True if the IP is a Tor exit node. Pro+
threat.is_relay True if the IP is an anonymous relay. Pro+
threat.is_hosting True if the IP belongs to a hosting provider. Pro+
threat.threat_score Composite risk score 0–100. A signal, not a final verdict — combine with account, payment, device and behavior data. Pro+
threat.threat_categories Array of threat category strings. Pro+
threat.bot_type Verified bot identifier (e.g. "googlebot", "bingbot") or null. Verified via reverse-DNS + forward confirmation. All
abuse.contact_email Abuse contact email from WHOIS data. Pro+

Example request

$ curl https://api.openipapi.com/v1/lookup/185.220.101.45 \
     -H "X-API-Key: oip_your_api_key_here"

Example response

{
  "ip": "185.220.101.45",
  "type": "IPv4",
  "geo": {
    "country_code": "DE",
    "country": "Germany",
    "region": "Hessen",
    "region_code": "HE",
    "city": "Frankfurt am Main",
    "postal_code": "60313",
    "latitude": 50.1109,
    "longitude": 8.6821,
    "timezone": "Europe/Berlin",
    "is_sanctioned": false,
    "sanction_lists": []
  },
  "network": {
    "asn": 205100,
    "as_name": "F3 Netze e.V.",
    "as_domain": "f3netze.de",
    "isp": "F3 Netze",
    "organization": "F3 Netze e.V.",
    "connection_type": "datacenter"
  },
  "threat": {
    "is_vpn": true,
    "is_proxy": false,
    "is_tor": true,
    "is_relay": false,
    "is_hosting": true,
    "threat_score": 85,
    "threat_categories": ["tor_exit_node", "known_abuser"],
    "bot_type": null
  },
  "abuse": {
    "contact_email": "abuse@f3netze.de"
  }
}

POST /v1/lookup/batch

Look up multiple IP addresses in a single request. Batch size limits depend on your plan.

POST https://api.openipapi.com/v1/lookup/batch
Batch lookups require a Starter plan or higher. Free plan accounts will receive a plan_required error.

Batch size limits

Plan Max IPs per request
Starter 100
Pro 500
Business 1,000
Enterprise 5,000

Request body

{
  "ips": [
    "185.220.101.45",
    "8.8.8.8",
    "1.1.1.1"
  ]
}

Response

Returns a wrapped array of full lookup objects, in the same order as the request.

{
  "results": [
    { "ip": "185.220.101.45", /* full lookup object */ },
    { "ip": "8.8.8.8",          /* full lookup object */ },
    { "ip": "1.1.1.1",          /* full lookup object */ }
  ],
  "count": 3
}

GET /v1/me

Returns geolocation and threat data for the IP address making the request. Useful for "What is my IP?" features.

GET https://api.openipapi.com/v1/me

The response is identical to GET /v1/lookup/{ip} with the caller's IP pre-filled.

$ curl https://api.openipapi.com/v1/me \
     -H "X-API-Key: oip_your_api_key_here"

GET /v1/asn/{asn}

Retrieve detailed information about an Autonomous System Number. Requires Starter plan or higher.

GET https://api.openipapi.com/v1/asn/{asn}

The asn parameter accepts both 13335 and AS13335 formats.

$ curl https://api.openipapi.com/v1/asn/13335 \
     -H "X-API-Key: oip_your_api_key_here"

Example response

{
  "asn": 13335,
  "as_name": "Cloudflare, Inc.",
  "as_domain": "cloudflare.com",
  "organization": "Cloudflare, Inc.",
  "country_code": "US",
  "ip_ranges_count": 1284,
  "total_ips": 2359296
}

GET /v1/validate/{ip}

Validate an IP address and determine its type. Does not consume lookup quota.

GET https://api.openipapi.com/v1/validate/{ip}

Example response

{
  "ip": "185.220.101.45",
  "valid": true,
  "type": "IPv4",
  "private": false,
  "bogon": false
}

GET /v1/fraud/{ip}

Composite fraud score (0–100) with risk level and actionable recommendation. Combines VPN, proxy, Tor, datacenter, and historical-abuse signals. Starter plan or above

GET https://api.openipapi.com/v1/fraud/{ip}

Example response

{
  "ip": "185.220.101.45",
  "fraud_score": 92,
  "risk_level": "critical",
  "signals": {
    "is_vpn": true,
    "is_proxy": false,
    "is_tor": true,
    "is_datacenter": true,
    "is_known_attacker": true,
    "high_risk_country": false
  },
  "recommendation": "block"
}

Risk levels & recommendations

Score Risk level Recommendation Typical action
0–30lowallowLet the request through
31–60mediumreviewLog for manual review
61–85highchallengeRequire CAPTCHA / 2FA / step-up auth
86–100criticalblockReject and alert

GET /v1/probe/{ip}

Real-time network probe data from our 60+ active probing nodes: open TCP ports, service banners, reverse DNS, full TLS certificate detail (subject, issuer, SANs, validity), and detected service categories. Pro plan or above

GET https://api.openipapi.com/v1/probe/{ip}

How it works

  • · Fresh data (≤ 7 days old): returned immediately from cache.
  • · Stale data (> 7 days): returned immediately plus a background re-probe is queued. Response includes "stale": true.
  • · Never probed: a probe is queued for immediate scanning. First results typically land within 1–5 minutes.
  • · Probe quota is 10% of your monthly lookup quota. Private / reserved IPs cannot be probed.

Example response

{
  "ip": "185.220.101.45",
  "reachable": true,
  "last_probed": "2026-04-18T14:23:11+00:00",
  "age_hours": 2.3,
  "stale": false,
  "refresh_queued": false,
  "probe_count_24h": 14,
  "probed_from_nodes": 8,
  "open_ports": [
    { "port": 22,   "service": "ssh",       "banner": "SSH-2.0-OpenSSH_8.9p1" },
    { "port": 80,   "service": "http",      "banner": "nginx/1.24.0" },
    { "port": 443,  "service": "https",     "banner": null },
    { "port": 9001, "service": "tor-relay", "banner": null }
  ],
  "reverse_dns": "tor-exit.f3netze.de",
  "tls": {
    "subject_cn":  "*.f3netze.de",
    "issuer":      "Let's Encrypt",
    "valid_from":  "2026-02-10T00:00:00Z",
    "valid_to":    "2026-05-10T00:00:00Z",
    "sans":        ["f3netze.de", "*.f3netze.de"],
    "fingerprint": "7e:4f:...:b2:e1"
  },
  "banners": {
    "22": "SSH-2.0-OpenSSH_8.9p1",
    "80": "nginx/1.24.0"
  },
  "detected_services": {
    "is_tor_relay":  true,
    "is_ssh_open":   true,
    "is_web_server": true,
    "is_vpn":        false,
    "is_proxy":      false
  }
}

Ports we scan

22 (ssh), 80 / 8080 (http), 443 / 8443 (https), 1080 (socks5), 3128 / 8888 (http-proxy), 9001 / 9030 (tor-relay / tor-dir), 9050 / 9150 (tor-socks), 1194 (openvpn), 4500 (ipsec-nat-t), 51820 (wireguard).

Use cases

  • · Anti-fraud — detect bot farms running identical SSH versions / TLS fingerprints
  • · Threat intel — monitor C2 servers for banner / certificate changes
  • · OSINT / bug bounty — quickly enumerate services on any public IP
  • · Hosting / ISP ops — find open proxies / Tor exits in your network

GET /v1/proxy-attribution/{ip}

Identifies whether an IP belongs to a known residential-proxy pool, commercial VPN, or datacenter range. Unlike generic is_proxy flags, this endpoint attributes the IP to the specific provider (Bright Data, Oxylabs, NordVPN, etc.) — critical for fraud teams that need to allow some providers and block others. Proxy Intel add-on

GET https://api.openipapi.com/v1/proxy-attribution/{ip}

Example response (IP in VPN range)

{
  "ip": "2.56.16.42",
  "detected": true,
  "primary_provider": {
    "provider":     "unknown_vpn",
    "display_name": "Unknown VPN",
    "network_type": "vpn",
    "confidence":   0.75
  },
  "networks": [
    {
      "provider":    "unknown_vpn",
      "network_type": "vpn",
      "confidence":   0.75,
      "source":       "x4bnet-vpn",
      "cidr":         "2.56.16.0/22"
    },
    {
      "provider":    "unknown_datacenter",
      "network_type": "datacenter",
      "confidence":   0.75,
      "source":       "x4bnet-datacenter"
    }
  ],
  "recommendation": "treat_as_vpn"
}

Network types

  • · residential — residential proxy pools (Bright Data, Oxylabs, Smartproxy, IPRoyal, Hola, Honeygain, EarnApp, Peer2Profit, SOAX)
  • · vpn — commercial VPN providers (NordVPN, ExpressVPN, Surfshark, Mullvad, ProtonVPN, PIA, IPVanish, CyberGhost)
  • · datacenter — hosting providers frequently used for commercial proxy exit (AWS, DigitalOcean, OVH, Hetzner, etc.)
  • · tor — Tor exit / relay nodes
  • · mobile — mobile carrier proxy pools

Recommendations

Recommendation Meaning
no_proxy_detectedIP not matched against any known pool — treat as normal traffic.
treat_as_commercial_proxyIP is in a residential proxy pool — likely malicious on non-scraping sites.
treat_as_vpnIP is in a commercial VPN range — apply VPN policy.
treat_as_datacenterIP is from a datacenter range — not a real residential user.
block_or_challengeTor exit — block or require strong authentication.

GET /v1/account/usage

Returns current billing period usage statistics for your account.

GET https://api.openipapi.com/v1/account/usage

Example response

{
  "plan": "Pro",
  "period_start": "2026-03-01",
  "period_end": "2026-03-31",
  "lookups_used": 184320,
  "lookups_limit": 500000,
  "lookups_remaining": 315680,
  "reset_at": "2026-04-01T00:00:00Z"
}

GET /v1/database/list

Lists MMDB databases available for your plan. Drop-in MaxMind .mmdb replacements for offline / edge use — works with any MaxMind DB reader (PHP, Go, Python, Node.js, Rust, Java).

GET https://api.openipapi.com/v1/database/list

Example response

{
  "plan": "Business",
  "sources": [
    {
      "source": "country",
      "filename": "geo-whois-asn-country.mmdb",
      "size_bytes": 8225621,
      "updated_at": "2026-04-18T02:00:00+00:00",
      "etag": "\"fa2b851f9155838b\"",
      "download_url": "https://api.openipapi.com/v1/database/download/country"
    }
  ]
}

GET /v1/database/download/{source}

Streams the raw MMDB file. Supports ETag + If-None-Match for conditional GETs (304 Not Modified when the file is unchanged). Rate limit: 10 downloads per source per day.

GET https://api.openipapi.com/v1/database/download/{source}

Available sources

Source Contents License Required Plan
countryCountry code + ASNCC0Pro
asnASN + organizationCC0Business
city-geolite2City, region, lat/lon, timezone (GeoLite2)MaxMind GeoLite2 EULABusiness
city-dbipCity (alternative to GeoLite2)DB-IP LiteEnterprise

Example: download + use in PHP

# Download latest MMDB
$ curl -H "X-API-Key: YOUR_KEY" \
    -o asn.mmdb \
    https://api.openipapi.com/v1/database/download/asn

# Use with maxmind-db/reader
$ composer require maxmind-db/reader

<?php
use MaxMind\Db\Reader;
$reader = new Reader('asn.mmdb');
$record = $reader->get('8.8.8.8');
// ['autonomous_system_number' => 15169,
//  'autonomous_system_organization' => 'Google LLC']

Sanctions Flag

Every /v1/lookup response includes two fields in the geo block that tell you whether the IP's country is subject to international sanctions.

Field Description
geo.is_sanctioned true if the country appears on any monitored sanction list; false otherwise.
geo.sanction_lists Array of sanction program identifiers that apply: OFAC, EU, UN, UK. Empty array [] when not sanctioned.

Covered sanction lists: OFAC (US), EU, UN Security Council, UK OFSI. Countries currently flagged include Iran, North Korea, Russia, Belarus, Syria, Cuba, Venezuela, Sudan, and others. The list is maintained and updated as programs change.

Sanctioned country example

"geo": {
  "country_code": "IR",
  "country": "Iran",
  ...
  "is_sanctioned": true,
  "sanction_lists": ["OFAC", "EU", "UN"]
}

Bot Detection

OpenIPApi performs double-reverse DNS verification to identify legitimate crawlers. The result is available in threat.bot_type on every lookup.

Field Values
threat.bot_type null (not a verified bot) or one of: googlebot, google_special_crawl, bingbot, applebot, yandexbot, duckduckbot, facebookbot, semrushbot, ahrefsbot, mj12bot

Verification method: (1) PTR lookup on the IP → hostname. (2) Hostname must match the bot's domain pattern (e.g. *.googlebot.com). (3) Forward DNS on the hostname must resolve back to the original IP. Results are cached for 24 hours.

Verified Googlebot example

"threat": {
  "is_vpn": false,
  "is_proxy": false,
  "is_tor": false,
  "threat_score": 0,
  "bot_type": "googlebot"
}

Historical Lookup

Add a ?date=YYYY-MM-DD parameter to any single IP lookup to retrieve the snapshot from that date.

Historical lookup requires Pro plan or above. Snapshots are available for up to 365 days. Daily snapshots are taken automatically for the most queried IPs.
GET https://api.openipapi.com/v1/lookup/{ip}?date=2026-01-15

Example request

$ curl "https://api.openipapi.com/v1/lookup/185.220.101.45?date=2026-01-15" \
     -H "X-API-Key: oip_your_api_key_here"

Response envelope

{
  "ip": "185.220.101.45",
  "snapshot_date": "2026-01-15",
  "is_historical": true,
  "data": {
    /* standard lookup response */
  }
}

Error: no snapshot available

{
  "error": "No historical snapshot available for this IP on 2026-01-15",
  "code": "no_snapshot"
}

Error codes

Code HTTP status Description
invalid_api_key 401 The API key is missing, malformed, or has been revoked.
limit_exceeded 429 You have used all lookups in your current billing period.
plan_required 403 The requested feature is not available on your current plan.
invalid_ip 400 The provided IP address is not a valid IPv4 or IPv6 address.
not_found 404 No data found for the requested resource (e.g., unknown ASN).
no_snapshot 404 No historical snapshot exists for this IP on the requested date.
date_out_of_range 400 Historical lookup date is more than 365 days in the past.
invalid_date 400 The date parameter is missing, malformed, or in the future.
plan_upgrade_required 403 The requested feature requires a higher plan tier.
rate_limited 429 Too many requests in a short window. Back off and retry.
internal_error 500 An unexpected server error occurred. Contact support if this persists.

Rate limits

In addition to monthly lookup quotas, requests are rate limited per API key using a sliding window. When exceeded, the API returns HTTP 429 with a Retry-After header.

Plan Requests / minute Lookups / month
Free 30 10,000
Starter 120 100,000
Pro 300 500,000
Business 600 2,000,000
Enterprise Custom Unlimited

Additional limits: /v1/validate (unauthenticated) caps at 60 req/min per caller IP; /v1/me at 30 req/min. MMDB downloads are capped at 10/day per source per account. Rate-limit responses include Retry-After.

Webhooks

Webhooks let you receive real-time HTTP POST notifications when the status of an IP you're watching changes. Configure them in the console → Webhooks, or through the dashboard UI — no API call required.

Events

Event Fires when
vpn_detectedA watched IP is newly detected as a VPN endpoint.
tor_detectedA watched IP appears on a Tor exit node list or probed as a Tor relay.
proxy_detectedA watched IP is detected as an open or SOCKS proxy.
high_threatThreat score crosses your configured threshold (50–95).

Request headers

POST https://your-endpoint.example.com/webhook
Content-Type: application/json
User-Agent: OpenIPApi-Webhook/1.0
X-OpenIPApi-Event: high_threat
X-OpenIPApi-Signature: sha256=8c7f1a...b2e

Example payload

{
  "event": "high_threat",
  "delivered_at": "2026-04-18T12:34:56Z",
  "webhook_id": 142,
  "data": {
    "ip": "185.220.101.45",
    "threat_score": 92,
    "previous_score": 45,
    "is_tor": true,
    "is_vpn": true
  }
}

Verifying the signature

Every webhook is signed using HMAC-SHA256 of the raw JSON body with your webhook secret. The signature is sent in the X-OpenIPApi-Signature header as sha256=<hex>. Always verify before acting on the payload:

PHP

$body = file_get_contents('php://input');
$hdr  = $_SERVER['HTTP_X_OPENIPAPI_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac(
    'sha256', $body, $webhookSecret
);
if (!hash_equals($expected, $hdr)) {
    http_response_code(401);
    exit;
}
// Safe to process $body now

Node.js (Express)

const crypto = require('crypto');

app.post('/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const hdr = req.headers['x-openipapi-signature'];
    const expected = 'sha256=' + crypto
      .createHmac('sha256', process.env.WEBHOOK_SECRET)
      .update(req.body).digest('hex');
    if (expected !== hdr) return res.status(401).end();
    // Process JSON.parse(req.body)
  });

Retry policy

Your endpoint must respond within 10 seconds with HTTP 2xx to acknowledge receipt. Any other status (or timeout) triggers exponential back-off retries: 1 min, 5 min, 30 min, 2 h, 12 h. After the final attempt the webhook is marked as failed and a dashboard alert is raised.

Delivery history & replay

The console → Webhooks keeps the last 100 delivery attempts per account, showing the event type, HTTP status code, and the response body returned by your endpoint. You can replay any previous delivery to re-send the original signed payload, and use the built-in Signature Debugger to verify HMAC-SHA256 signatures client-side without sharing your secret with any server.

Replayed deliveries include an additional X-OpenIPApi-Replay: 1 header so your endpoint can distinguish them from live events.

Code examples — curl

Single lookup

$ curl https://api.openipapi.com/v1/lookup/8.8.8.8 \
     -H "X-API-Key: oip_your_api_key_here"

Batch lookup

$ curl -X POST https://api.openipapi.com/v1/lookup/batch \
     -H "X-API-Key: oip_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{"ips": ["8.8.8.8", "1.1.1.1", "185.220.101.45"]}'

Your IP

$ curl https://api.openipapi.com/v1/me \
     -H "X-API-Key: oip_your_api_key_here"

Code examples — JavaScript

Single lookup (fetch)

const response = await fetch('https://api.openipapi.com/v1/lookup/8.8.8.8', {
  headers: {
    'X-API-Key': 'oip_your_api_key_here'
  }
});

const data = await response.json();
console.log(data.geo.country);       // "United States"
console.log(data.threat.is_vpn);     // false
console.log(data.threat.threat_score); // 0

Batch lookup

const response = await fetch('https://api.openipapi.com/v1/lookup/batch', {
  method: 'POST',
  headers: {
    'X-API-Key': 'oip_your_api_key_here',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    ips: ['8.8.8.8', '1.1.1.1', '185.220.101.45']
  })
});

const { results } = await response.json();
for (const info of results) {
  console.log(info.ip, info.geo.city, info.threat.threat_score);
}

Code examples — Python

Single lookup (requests)

import requests

API_KEY = "oip_your_api_key_here"
headers = {"X-API-Key": API_KEY}

r = requests.get(
    "https://api.openipapi.com/v1/lookup/8.8.8.8",
    headers=headers
)
r.raise_for_status()
data = r.json()

print(data["geo"]["country"])       # United States
print(data["threat"]["is_vpn"])     # False
print(data["threat"]["threat_score"]) # 0

Batch lookup

import requests

API_KEY = "oip_your_api_key_here"
headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json"
}

payload = {
    "ips": ["8.8.8.8", "1.1.1.1", "185.220.101.45"]
}

r = requests.post(
    "https://api.openipapi.com/v1/lookup/batch",
    json=payload,
    headers=headers
)
r.raise_for_status()

for info in r.json()["results"]:
    print(info["ip"], info["geo"]["city"], info["threat"]["threat_score"])

Ready to get started?

10,000 free lookups per month. No credit card required.

Get your free API key