Documentation
The ipapi.is API returns ownership, network, location and security data for any IPv4 or IPv6 address, and registry data for any autonomous system (ASN). A response answers three questions:
- Who owns the address? —
company,asnand the abuse contact inabuse. - Where is it? —
location. - What kind of address is it? —
is_datacenter,is_vpn,is_proxy,is_tor,is_abuser,is_mobile,is_satellite,is_anycast,is_crawlerandegress_service.
&key=your_api_key to see the full
response described here.
# Quickstart
Send the address as q and your API key as key:
curl "https://api.ipapi.is/?q=32.43.11.44&key=your_api_key"
Leave out q to look up the address the request comes from. Leave out key to use
the API anonymously, with a shorter response and a low daily limit (see
below). Always use api.ipapi.is: it routes every request to the
nearest healthy server.
Node.js 18+ (built-in fetch):
fetch('https://api.ipapi.is/?q=32.43.11.44&key=your_api_key')
.then((res) => res.json())
.then((data) => console.log(data.is_datacenter, data.is_vpn));
Python (requests):
import requests
res = requests.get('https://api.ipapi.is',
params={'q': '32.43.11.44', 'key': 'your_api_key'}, timeout=5)
res.raise_for_status()
data = res.json()
print(data['is_datacenter'], data['is_vpn'])
# Authentication and Limits
Requests with a key get the complete response documented on this page, plus bulk lookups and ASN queries. A free account includes 1,000 requests per day, commercial use included. One-time credits that never expire, or a subscription, raise that limit (see pricing). Your key is on the dashboard.
- Send the key as the
keyquery parameter, or in the JSON body of a POST request, which keeps it out of URLs and logs. - Keep the key on your server. A key in browser JavaScript or a public repository will be found and spent by someone else. You can regenerate it in the dashboard.
- Every lookup counts as one request, also inside a bulk request. Requests answered with an error are not counted.
# Without a key
Keyless requests are limited to 30 lookups per IP address per UTC day and return a
minimal response: ownership and location only, without the detection flags. Bulk lookups and ASN queries
are refused with 403 ERR_FORBIDDEN_API_KEY_REQUIRED.
{
"ip": "82.66.115.249",
"is_bogon": false,
"company": "Proxad / Free SAS",
"asn": "AS12322 Free SAS",
"city": "Goven",
"region": "Brittany",
"country": "France",
"lat": 48.0063,
"lon": -1.84667,
"timezone": "Europe/Paris",
"docs": "https://ipapi.is/free-tier.html?ref=api"
}
Every field is always present and null when unknown. The docs field marks the
response as anonymous and links to a page explaining it.
After the 30th lookup, the next 10 requests are answered with 429 ERR_FREE_TIER_EXHAUSTED and
a Retry-After header holding the seconds until UTC midnight. A client that keeps sending
after that is blocked at the firewall for 24 hours. The budget is counted per IPv4 address and per IPv6
/64, so everyone behind a shared NAT shares it.
# Errors
Errors are JSON, whatever output format you asked for, with a message in error and a stable
error_code:
{
"error": "Invalid IP Address or AS Number",
"error_code": "ERR_INVALID_IP_OR_ASN",
"elapsed_ms": 0.16
}
Branch on error_code or the HTTP status. The message text may change.
| Status | error_code |
Meaning |
|---|---|---|
400 |
ERR_INVALID_IP_OR_ASN |
q is neither an IP address nor an AS number |
400 |
ERR_INVALID_JSON_BODY |
The POST body is not valid JSON |
400 |
ERR_BULK_LIMIT_EXCEEDED |
More than 100 unique entries in ips |
400 |
ERR_INVALID_BULK_INPUT_NOT_ARRAYERR_INVALID_BULK_INPUT_EMPTYERR_INVALID_BULK_INPUT_NO_VALID_ENTRIES |
ips is not an array, is empty, or holds no valid entry |
403 |
ERR_FORBIDDEN_INVALID_API_KEY |
The key is unknown or disabled |
403 |
ERR_FORBIDDEN_NOT_ALLOWED |
The account may not make requests; contact support |
403 |
ERR_FORBIDDEN_API_KEY_REQUIRED |
A bulk lookup or an ASN query without a key |
405 |
ERR_INVALID_HTTP_METHOD |
Anything other than GET or POST |
429 |
ERR_QUOTA_EXCEEDED |
The account behind the key has used up its quota |
429 |
ERR_FREE_TIER_EXHAUSTED |
The keyless daily limit is used up; see Retry-After |
500 |
ERR_UNEXPECTED_SERVER_ERROR |
Our fault. Retry, and tell us if it persists |
Two responses don't follow this pattern:
- An AS number we have no data for returns
200with anerrormessage and noerror_code: the request was valid, there is just nothing to report. - A client banned at the firewall gets a bare
403with an empty body. Clients are banned for ignoring the keyless limit, and for sending many requests that keep being refused, so back off on429and403instead of retrying.
# Endpoints
There is one endpoint, https://api.ipapi.is, for GET and POST.
Every response carries Access-Control-Allow-Origin: *, so browsers can call it directly.
# GET Requests
| Parameter | Also accepted as | Meaning |
|---|---|---|
q |
query, ip, as, asn |
The IPv4 or IPv6 address, or the AS number with its AS prefix
(AS15169), to look up. Leave it out to look up the address the request comes from |
key |
api_key, apiKey |
Your API key |
format |
fmt, output |
json (default), html, toon, csv or
text; see output formats |
pretty |
— | 1 indents the JSON. Browsers get indented JSON without it |
geoloc_meta |
geolocMeta |
1 adds location.geofeed: the RFC 8805 geofeed URLs used to locate
the address |
all_companies |
— | 1 returns every WHOIS record covering the address instead of the most specific
one. This changes the response shape, see below |
The switches are on only for the exact value 1; in a JSON body, the string
"1". true, yes and the number 1 are ignored.
curl "https://api.ipapi.is/?q=3.5.140.2&key=your_api_key"
curl "https://api.ipapi.is/?q=AS42831&key=your_api_key"
curl "https://api.ipapi.is/?q=86.53.104.0&geoloc_meta=1&key=your_api_key"
#
Every matching network with all_companies=1
An address is usually covered by several nested WHOIS records: a large allocation to a provider, a
sub-allocation to a reseller, an assignment to the end customer. By default company holds the
most specific one. With all_companies=1:
companybecomes an array of every matching record, each with its ownrirandabuse. The first entry is the one you get by default.- The top-level
abuseobject is dropped and the top-levelririsnull.
Code that reads response.company.name has to read response.company[0].name
instead, so check your parser before turning this on.
# POST and Bulk Requests
POST a JSON body (Content-Type: application/json) with the same parameters as GET. Use
q for one lookup, or ips for up to 100 lookups in one request.
Bulk lookups need a key.
curl https://api.ipapi.is \
-H "Content-Type: application/json" \
-d '{"ips": ["32.5.140.2", "45.33.32.156", "2606:4700::1111", "AS15169"], "key": "your_api_key"}'
The response is an object keyed by the entries you sent. Each value is the same object a single lookup
returns, with its own elapsed_ms, and total_elapsed_ms times the whole request:
{
"32.5.140.2": { "ip": "32.5.140.2", "rir": "ARIN", "...": "...", "elapsed_ms": 0.08 },
"45.33.32.156": { "ip": "45.33.32.156", "...": "..." },
"2606:4700::1111": { "ip": "2606:4700::1111", "...": "..." },
"AS15169": { "asn": 15169, "org": "Google LLC", "...": "..." },
"total_elapsed_ms": 0.42
}
ipsmay mix IPv4, IPv6 and AS numbers. Duplicates and invalid entries are dropped silently, and the limit of 100 applies to what remains.- Results keep the order you sent them in.
total_elapsed_mssits next to them, so iterate over the entries you sent, not over every key. - Every entry counts as one request against your quota.
geoloc_metaandall_companiesonly apply toq; bulk entries always use the defaults.- A single
qlookup sent by POST has no top-levelelapsed_ms.
The same request with fetch():
fetch('https://api.ipapi.is', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
ips: ['32.5.140.2', '45.33.32.156', '2606:4700::1111', 'AS15169'],
key: 'your_api_key',
}),
})
.then((res) => res.json())
.then((data) => console.log(data));
# Output Formats
Add format= or use the matching path. Every format carries the same data and costs the same.
| Format | Example | Good for |
|---|---|---|
| JSON (default) | api.ipapi.is/?q=172.83.212.160 | Code. Compact unless you add pretty=1 or call it from a browser |
| HTML | api.ipapi.is/html?q=172.83.212.160 | Reading in a browser |
| TOON | api.ipapi.is/toon?q=172.83.212.160 | LLM prompts: the same data in fewer tokens than JSON |
| CSV | api.ipapi.is/csv?q=172.83.212.160 | Spreadsheets |
| Text | api.ipapi.is/text?q=172.83.212.160 | Shell scripts and reading by eye |
# Example Response
A keyed lookup of 107.174.138.172 returns:
{
"ip": "107.174.138.172",
"rir": "ARIN",
"is_bogon": false,
"is_mobile": false,
"is_satellite": false,
"is_anycast": false,
"is_crawler": false,
"is_datacenter": true,
"is_tor": true,
"is_proxy": false,
"is_vpn": false,
"is_abuser": true,
"datacenter": {
"datacenter": "HostPapa",
"domain": "hostpapa.com",
"network": "107.172.0.0 - 107.175.255.255"
},
"company": {
"name": "VPS ACE",
"abuser_score": "0.1016 (High)",
"domain": "vpsace.com",
"type": "business",
"network": "107.174.138.128 - 107.174.138.255",
"netname": "CC-107-174-138-0-25"
},
"abuse": {
"name": "VPS ACE",
"address": "350 Main Street, Buffalo, NY, 14202, US",
"email": "support@vpsace.com",
"phone": "+1-855-587-7223"
},
"asn": {
"asn": 36352,
"abuser_score": "0.0374 (High)",
"route": "107.174.138.0/24",
"descr": "AS-COLOCROSSING - HostPapa, US",
"country": "us",
"active": true,
"org": "HostPapa",
"domain": "hostpapa.com",
"abuse": "net-abuse-global@hostpapa.com",
"type": "hosting",
"created": "2005-12-12",
"updated": "2024-01-04",
"rir": "ARIN"
},
"location": {
"is_eu_member": false,
"calling_code": "1",
"currency_code": "USD",
"continent": "NA",
"country": "United States",
"country_code": "US",
"state": "New York",
"city": "Buffalo",
"latitude": 42.88645,
"longitude": -78.87837,
"zip": "14205",
"timezone": "America/New_York",
"local_time": "2026-08-01T14:32:13-04:00",
"local_time_unix": 1785609133,
"is_dst": true,
"utcoffset": "-04:00",
"accuracy": "VERY_HIGH"
},
"elapsed_ms": 0.05
}
- Always present:
ip,rir, theis_*flags,companyandlocation(nullwhen unknown). - Present only when they apply:
vpn,egress_service,datacenter,abuseandasn. elapsed_msis only added to GET requests.- A bogon address (private or reserved) returns only
ip,rirand the flags.
# Top-Level Fields
| Field | Type | Meaning |
|---|---|---|
ip |
string | The address looked up. Without q, the address the request came from |
rir |
string or null | The registry the address's WHOIS record comes from: one of the five
Regional Internet
Registries (ARIN, RIPE, APNIC, LACNIC,
AFRINIC), a national registry (JPNIC, KRNIC,
TWNIC), ARIN_CUST for customer records in ARIN's database, or
RWHOIS for records from an ISP's own RWhois server |
is_bogon |
boolean | The address cannot appear on the public internet: private, reserved or special, such as
127.0.0.1 |
is_mobile |
boolean | Belongs to a mobile carrier. Carriers put many users behind one address (carrier-grade NAT), so don't block these lightly. Coverage is incomplete; false positives are rare |
is_satellite |
boolean | Belongs to a satellite ISP such as Starlink or Viasat; egress_service names it.
The user can be far from the location given |
is_anycast |
boolean | The address answers from many locations at once. Details |
is_crawler |
false or string | false, or the name of the bot using the address.
Details |
is_datacenter |
boolean | Belongs to a hosting provider. Details |
is_tor |
boolean | A Tor exit node. This detection is reliable |
is_proxy |
boolean | A known open proxy. Details |
is_vpn |
boolean | A VPN exit node or server. Details |
is_abuser |
boolean | Recently seen in abuse (attacks, spam, scanning) by blocklists or our honeypots. The kind of
abuse is not given; company.abuser_score rates the whole network |
vpn |
object | The VPN provider. Details |
egress_service |
object | The managed service whose users send traffic from this address. Details |
datacenter |
object | The hosting provider. Details |
company |
object or null | The organization the address is registered to. Details |
abuse |
object | Where to report abuse. Details |
asn |
object | The autonomous system announcing the address. Details |
location |
object or null | Where the address is. Details |
elapsed_ms |
number | Server processing time in milliseconds. GET requests only |
#
is_anycast
The same address is announced from many sites and each client reaches the nearest one, as with public DNS
resolvers, root name servers and CDN edges: 1.1.1.1,
8.8.8.8 or
9.9.9.9. Such an address has no single location, so its
location.accuracy is always VERY_LOW. The flag covers IPv4 /24 and IPv6 /48
prefixes that daily measurements from many vantage points saw answering from several sites on most days
of the past week.
#
is_crawler
false, or the name of the crawler or bot that operates from this address, for example
GoogleBot (66.249.70.224),
BingBot (40.77.190.180),
DuckDuckGoBot (40.76.173.151),
YandexBot (213.180.203.183) or
TwitterBot (199.59.148.0).
Several hundred names occur. The most common are GoogleBot, AhrefsBot,
ByteSpider, AppleBot, ClaudeBot, ChatGPTUser,
AmazonBot, FacebookBot, YandexBot, BingBot,
DuckDuckGoBot, BaiduSpider, UptimeRobot and
StripeWebhook. Not all of them are search engines: SEO and AI crawlers, uptime monitors,
webhooks and scrapers are listed too. To let good bots through, allow the names you trust rather than
every truthy value.
#
is_datacenter
true when the address belongs to a hosting provider; the
datacenter object then names it. We count as hosting every organization that lets
anyone rent servers or addresses quickly: hosting and cloud providers (Hetzner, AWS, Azure), CDNs
(Cloudflare, Fastly), DDoS protection (DDoS-Guard, Qrator), IP leasing (IPXO) and app platforms (fly.io,
Heroku). A true here is reliable.
Two exceptions: secure web gateways (Zscaler, Netskope) and satellite ISPs (Starlink, Viasat) are
false even though they look like datacenter space, because their traffic comes from real
users; egress_service identifies them. Privacy relays (iCloud
Private Relay, Cloudflare WARP) are true and also carry an egress_service. See
the list of hosting providers.
#
is_proxy
true for known open proxies (HTTP, SOCKS4, SOCKS5) from public proxy lists, which we check
continuously, and from feeds such as FireHOL's
firehol_anonymous.
Residential and mobile proxies are mostly not detected: they route through real users' devices, and no
database knows an address is a proxy before it has been seen acting as one. To catch proxies while they
are being used, see proxydetect.live.
#
is_vpn
true when the address is a VPN exit node or server. Test it for truthiness
(if (res.is_vpn)) rather than === true: a VPN record may carry a truthy value
other than true. When the provider is known, the vpn object
names it; addresses flagged by generic signals have no vpn object. For example,
67.170.172.12 is not a VPN and
187.13.12.227 is a NordVPN exit node.
We find exit nodes by connecting to VPN providers and recording the addresses their traffic leaves from (how), so coverage grows every day. See the list of VPN exit nodes.
#
The vpn object
Present when the VPN provider is known. For an exit node or server we enumerated (example 187.13.12.227):
"vpn": {
"ip": "187.13.12.227",
"service": "NordVPN",
"url": "https://nordvpn.com",
"type": "exit_node",
"last_seen": 1791556837158,
"last_seen_str": "2026-10-09T14:40:37.158Z",
"exit_node_region": "France #1354",
"country_code": "FR",
"city_name": "Bordeaux",
"latitude": 44.84048,
"longitude": -0.58079
}
| Field | Type | Meaning |
|---|---|---|
service |
string | The VPN provider |
url |
string | The provider's website |
type |
string | exit_node: we saw traffic leave the VPN from this address.
vpn_server: the provider lists this address as one of its servers |
ip |
string | The address or range (CIDR) the record was enumerated for |
last_seen, last_seen_str |
number, string | When we last saw it, as Unix time in milliseconds and as ISO 8601 |
exit_node_region |
string | The provider's own name for the location, free text |
hostname |
string | The provider's hostname for the server |
country_code, city_name, latitude,
longitude |
string, number | Where the exit node actually is |
claimed_country_code, claimed_city_name,
claimed_latitude, claimed_longitude |
string, number | Where the provider claims it is, only when that is a different country (a virtual location) |
Ranges we attribute to a provider because its enumerated exit nodes cluster there return a shorter
object (example 45.86.210.136). When several
providers share a range, service names all of them, joined with " + ":
"vpn": {
"is_vpn": true,
"service": "NordVPN",
"url": "https://nordvpn.com"
}
Only service is always present; read the other fields defensively.
#
The egress_service object
Present when the address is an egress node of a managed service: its traffic comes from that service's
users, not from the organization that owns the address. type is the kind of service,
provider the operator:
"egress_service": {
"type": "secure_web_gateway",
"provider": "Zscaler"
}
type |
Meaning | Example |
|---|---|---|
secure_web_gateway |
A cloud security proxy (SWG, SASE) carrying the web traffic of the vendor's corporate
customers: Zscaler, Netskope, Cisco Umbrella, Forcepoint, Cato Networks and others.
is_datacenter is false |
136.226.50.55 |
private_relay |
A privacy relay that keeps the user's rough location: iCloud Private Relay, Cloudflare WARP.
is_datacenter is true |
172.224.226.5 |
satellite |
A satellite ISP such as Starlink or Viasat; names the provider that is_satellite
cannot |
129.222.200.0 |
dns_resolver |
A public DNS resolver such as Google Public DNS or Cisco OpenDNS. Its traffic is resolver queries, not users | 208.67.222.222 |
email_gateway |
An email security relay such as Proofpoint, Mimecast or Barracuda | 67.231.144.10 |
#
The datacenter object
Present when is_datacenter is true:
"datacenter": {
"datacenter": "HostPapa",
"domain": "hostpapa.com",
"network": "107.172.0.0 - 107.175.255.255"
}
| Field | Type | Meaning |
|---|---|---|
datacenter |
string | The hosting provider; see the list of providers |
network |
string | The provider's range that contains the address |
domain |
string | The provider's domain, when known |
country, state, city |
string | The location of the range, when the provider publishes it in a geofeed |
Large clouds add fields from the ranges they publish: Amazon AWS adds region,
service and network_border_group
(3.5.140.2), DigitalOcean adds code,
city, state and country
(167.99.241.130), Linode adds name,
city and country (72.14.182.54).
#
The company object
"company": {
"name": "VPS ACE",
"abuser_score": "0.1016 (High)",
"domain": "vpsace.com",
"type": "business",
"network": "107.174.138.128 - 107.174.138.255",
"netname": "CC-107-174-138-0-25"
}
The organization the address is registered to, from the most specific WHOIS record covering it. In this
example that is the customer VPS ACE, not HostPapa, which holds the surrounding /14; use
all_companies=1 to get both. null when no record is
known.
| Field | Type | Meaning |
|---|---|---|
name |
string | The organization's name from WHOIS |
abuser_score |
string | The share of the network's addresses seen in abuse, followed by a label: Very Low,
Low, Elevated, High or Very High
(how it is computed) |
domain |
string | The organization's domain |
type |
string | The organization type, see the table below |
network |
string | The range the WHOIS record covers |
netname |
string or null | The network name from WHOIS (NetName at ARIN, netname elsewhere). It
differs between networks of the same organization. null for LACNIC, which has
none |
Organization types, used by company.type and asn.type:
type |
The organization is | Example |
|---|---|---|
hosting |
A hosting, cloud or CDN provider | 37.148.167.137 |
isp |
An internet service provider | 108.15.237.36 |
education |
A university or other educational institution | 128.146.65.112 |
government |
A government body | 192.91.184.0 |
banking |
A bank or other financial institution | 199.67.175.0 |
business |
Anything else | 17.133.85.230 |
#
The abuse object
"abuse": {
"name": "VPS ACE",
"address": "350 Main Street, Buffalo, NY, 14202, US",
"email": "support@vpsace.com",
"phone": "+1-855-587-7223"
}
Where to report abuse from this address: the abuse contact of the WHOIS record in company,
so it covers the same network. Present when that record has one. All four fields, the
contact's name, postal address, email and phone, are
strings.
#
The asn object
"asn": {
"asn": 36352,
"abuser_score": "0.0374 (High)",
"route": "107.174.138.0/24",
"descr": "AS-COLOCROSSING - HostPapa, US",
"country": "us",
"active": true,
"org": "HostPapa",
"domain": "hostpapa.com",
"abuse": "net-abuse-global@hostpapa.com",
"type": "hosting",
"created": "2005-12-12",
"updated": "2024-01-04",
"rir": "ARIN"
}
The autonomous system announcing the address, from BGP routing data and the AS's WHOIS record. Absent when no AS announces the address.
| Field | Type | Meaning |
|---|---|---|
asn |
number | The AS number |
abuser_score |
string | Like company.abuser_score, over all IPv4 routes of the AS |
route |
string | The announced prefix containing the address, in CIDR notation |
descr |
string | The AS's description |
country |
string | The country the AS is registered in, as a lowercase ISO code |
active |
boolean | Whether the AS announces any routes |
org, domain |
string | The organization holding the AS and its domain |
abuse |
string | The abuse email address |
type |
string | The organization type |
created, updated |
string | Registration date and last update in WHOIS, YYYY-MM-DD |
rir |
string | The registry of the AS |
# Querying an ASN
Send an AS number as q (?q=AS15169&key=your_api_key) to look up the AS
itself; this needs a key. The response has the fields of the asn object at the top level,
without route, plus every prefix the AS announces:
{
"asn": 15169,
"abuser_score": "0.0012 (Low)",
"descr": "GOOGLE - Google LLC, US",
"country": "us",
"active": true,
"org": "Google LLC",
"domain": "google.com",
"abuse": "network-abuse@google.com",
"type": "hosting",
"created": "2000-03-30",
"updated": "2012-02-24",
"rir": "ARIN",
"prefixes": ["8.8.4.0/24", "8.8.8.0/24", "... 1140 in total"],
"prefixesIPv6": ["2001:4860::/32", "2001:4860:4820::/48", "... 146 in total"],
"elapsed_ms": 1.21
}
Inactive ASes carry fewer fields. An AS number we have no data for returns 200 with
{"error": "No data for ASN 4294967290"} and no error_code.
#
The location object
"location": {
"is_eu_member": false,
"calling_code": "1",
"currency_code": "USD",
"continent": "NA",
"country": "United States",
"country_code": "US",
"state": "New York",
"city": "Buffalo",
"latitude": 42.88645,
"longitude": -78.87837,
"zip": "14205",
"timezone": "America/New_York",
"local_time": "2026-08-01T14:32:13-04:00",
"local_time_unix": 1785609133,
"is_dst": true,
"utcoffset": "-04:00",
"accuracy": "VERY_HIGH"
}
Where the address is, from our own geolocation database.
null when the address cannot be located.
| Field | Type | Meaning |
|---|---|---|
is_eu_member |
boolean | Whether the country is an EU member state |
calling_code |
string | The country calling code |
currency_code |
string | The country's currency, ISO 4217 |
continent |
string | Two-letter continent code, e.g. NA for North America |
country, country_code |
string | Country name and ISO 3166-1 alpha-2 code |
state, city, zip |
string | State or region, city and postal code |
latitude, longitude |
number | Coordinates |
timezone |
string | IANA time zone, e.g. America/New_York |
local_time |
string | The current local time there, ISO 8601 |
local_time_unix |
number | The current time as a Unix timestamp in seconds |
is_dst |
boolean | Whether daylight saving time is in effect there |
utcoffset |
string | The current UTC offset as ±HH:MM, daylight saving time included |
accuracy |
string or null | VERY_HIGH, HIGH, MEDIUM, LOW or
VERY_LOW. VERY_HIGH usually means the network operator published the
location in an RFC 8805 geofeed.
Anycast addresses are always VERY_LOW |
geofeed |
array | Only with geoloc_meta=1: the geofeed URLs that located this address, empty when
none did |
# Data Sources
- WHOIS records of the five RIRs and the national registries. Their bulk downloads are
incomplete, so we also query millions of networks and ASNs one by one. This is where
company,abuseand most ofasncome from. - BGP routing tables (APNIC) for the routes and prefixes of every AS.
- Hosting ranges that cloud providers publish (Google Cloud, Azure, AWS and many more), WHOIS data, and an automatic classification of millions of organization websites.
- VPN exit node enumeration: we connect to VPN providers and record the addresses their traffic leaves from (VPN detection).
- Abuse data from public blocklists such as FireHOL and ipsum, and from our own honeypots.
- Geolocation from our own database, built from WHOIS, RFC 8805 geofeeds, reverse DNS and latency measurements.
- Reverse DNS of the whole IPv4 address space, from our own crawl (Reverse DNS Database). It feeds VPN, mobile and geolocation detection.
# API Servers
Use api.ipapi.is. It sends each request to the nearest healthy server of our fleet in Europe,
North America and Asia, and fails over automatically. The regional names
de.ipapi.is,
us.ipapi.is and
sg.ipapi.is reach one server each and exist for
diagnostics; pinning a client to one only costs latency and failover. The
status page shows the health and response time of every server.
# Data Corrections
Found wrong data? Report it on the corrections page, or submit corrections through the API.
# IP Databases
The data behind the API is also sold as downloadable databases, for offline use or for importing into your own systems. The business subscription includes all of them, plus a self-hosted copy of the API, for less than buying them separately.
| Database | Contents | Price |
|---|---|---|
| IP to Hosting | Every known hosting and cloud IP range | 89$ per month |
| IP to VPN | Exit nodes and servers of VPN providers such as NordVPN and ExpressVPN | 79$ per month |
| IP to Geolocation | The location of every IPv4 and IPv6 range | 59$ per month |
| IP to ASN | Every autonomous system, with its routes and registry data | 49$ per month |
| IP to Company | The owner, domain and type of every network | 89$ per month |
| IP to Abuser | Addresses and networks seen in abuse: spam, malicious bots, attacks | 49$ per month |
| Reverse DNS | The PTR record (reverse DNS hostname) of every IPv4 address, plus IPv6 reverse DNS | 149$ per month |