API reference

One endpoint, five output formats, no SDK. Everything on this page describes what the API does today.

Quickstart

Look up an address with a GET request. Sections we know nothing about are left out of the body rather than returned empty, so check for a key before reading into it.

curl
curl 'https://ip-api.dev/api?q=8.8.8.8&pretty=1&key=YOUR_KEY'

Endpoint and parameters

GET https://ip-api.dev/api

FieldTypeDescription
qstring, requiredThe IPv4 address, IPv6 address, or AS number to look up. AS numbers are written as AS13335 or as13335. A CIDR mask is rejected — the endpoint looks up single addresses.
keystring, optionalYour API key. Without it the request is counted against the keyless daily quota and answered with the reduced response body.
pretty1, optionalSet to 1 to indent JSON with two spaces. Ignored by the csv, text, html and toon formats.
formatjson | csv | text | html | toon, optionalExplicit response format for clients that cannot conveniently set Accept. Overrides the Accept header. Omit both to receive JSON.

Output formats

Request the canonical /api endpoint and select its representation with the HTTP Accept header. If setting headers is inconvenient, use format; it takes precedence over Accept. With neither, the response is JSON. The format-specific paths remain convenient fixed aliases.

AliasContent-TypeNotes
/apiapplication/jsonThe default. Nested objects are preserved.
/api/csvtext/csvOne header row plus one row per lookup. Nested keys are flattened with dots.
/api/texttext/plainOne key=value line per field. Bulk results are separated by a blank line.
/api/htmltext/htmlA plain two-column table per lookup.
/api/toontext/toonLossless, typed TOON v4 for token-efficient LLM and agent input.

CSV, text and HTML flatten nested fields into dotted keys such as location.country, and arrays are joined with spaces. TOON preserves the JSON structure and primitive types; uniform bulk arrays use its compact tabular form.

Accept negotiation
curl 'https://ip-api.dev/api?q=8.8.8.8'   -H 'Accept: text/toon'
Query-parameter convenience
curl 'https://ip-api.dev/api?q=8.8.8.8&format=toon'
GET /api/csv?q=8.8.8.8
ip,is_bogon,is_datacenter,is_anycast,is_tor,is_proxy,is_vpn,is_abuser,is_mobile,is_satellite,is_crawler,organization.name,asn.number,asn.name,location.country_code,location.latitude,location.longitude,elapsed_ms
8.8.8.8,false,true,false,false,false,false,false,false,false,false,Google LLC,15169,Google LLC,US,,,60.643
TOON response
ip: 8.8.8.8
network:
  cidr: 8.8.8.0/24
  rir: arin
asn:
  number: 15169
  name: Google LLC
services: []
location:
  country_code: US
  precision: region
is_vpn: false
elapsed_ms: 1.234

Keys and rate limits

Pass your key as the key query parameter. There is no header-based authentication.

  • No key: 100 requests per UTC day, answered with the reduced response body described below. IPv4 callers are counted per address. IPv6 callers are counted per prefix, because a single subscriber holds many addresses: 100 per /64, 200 per /56, and 300 per /48.
  • Free key: 1,000 requests per day and the full response body.
  • Paid plans raise the monthly allowance — see pricing.
  • The counter resets at 00:00 UTC. Going over returns 429.

Bulk lookups

POST to the same path with a JSON body to look up between 1 and 100 addresses at once. Bulk requires a key, always returns the full response body, and answers with an array in the order you sent.

curl
curl -X POST 'https://ip-api.dev/api?key=YOUR_KEY&pretty=1' \
  -H 'Content-Type: application/json' \
  -d '{"ips": ["1.1.1.1", "8.8.8.8", "9.9.9.9"]}'
200 application/json
[
  { "ip": "1.1.1.1", "network": { "rir": "apnic", ... }, "asn": { ... }, "elapsed_ms": 61.2 },
  { "ip": "8.8.8.8", "network": { "rir": "arin", ... }, "asn": { ... }, "elapsed_ms": 58.9 },
  { "ip": "not-an-ip", "error": "Invalid IP Address or AS Number", "elapsed_ms": 0.01 }
]

Entries that are not valid addresses come back as an object with an error field instead of failing the whole request. AS numbers are not accepted in bulk bodies.

Response fields

These are the fields in the full response body, returned when you send a key.

Top level

FieldTypeDescription
ipstringThe address you asked about, echoed back as you sent it.
hostnamestringThe reverse DNS hostname for the address. Omitted when we have no PTR record for it.
servicesarrayVPN, proxy and platform-egress matches. Always an array; empty when none are known.
is_bogonbooleanTrue for addresses that should never appear on the public internet: private, loopback, link-local, carrier-grade NAT, documentation, multicast and reserved space. Computed from the address itself.
is_datacenterbooleanTrue when the datacenter section is present, meaning a published cloud range or a hosting-classified AS covers the address.
is_anycastbooleanTrue when measurement data shows the prefix is announced from multiple physical sites.
is_torbooleanTrue when the address is a known Tor exit node.
is_proxybooleanTrue when the address belongs to a platform egress network such as iCloud Private Relay or Cloudflare WARP, or is otherwise flagged as a proxy.
is_vpnbooleanTrue when the address is an exit node for a commercial VPN service.
is_abuserbooleanTrue when a network covering the address appears on an abuse blocklist we ingest.
is_mobilebooleanTrue when the AS originating the route for this address is classified as a mobile carrier.
is_satellitebooleanTrue when the originating AS is classified as a satellite provider.
is_crawlerbooleanTrue when a verified crawler range covers the address. The crawler object carries its name.
crawlerobjectVerified crawler details. Present only on a match; crawler.name is the bot name.
elapsed_msnumberHow long the lookup took on our side, in milliseconds, to three decimal places. Present on every response, including errors.

asn — routing

Omitted when no BGP route covers the address.

FieldTypeDescription
asn.numbernumberThe AS number.
asn.namestring | nullThe organization that holds the AS number.
asn.domainstring | nullThe AS organization's domain when a source publishes it.
asn.country_codestring | nullTwo-letter country code registered for the organization.
asn.routestringThe most specific BGP prefix covering the address that this AS originates.
asn.rirstring | nullThe registry the AS number was assigned by.
asn.typestring | nullBusiness category such as hosting, isp, education or government.
asn.activebooleanTrue when the AS currently originates at least one prefix.
asn.updated_atstring | nullDate the source ASN organization record last changed.
asn.abuse_scoreobjectFraction of announced addresses on an abuse list as numeric score (0–1) and low/medium/high level.

network — registry assignment

The most-specific whois network, or the broad RIR allocation as a fallback. Omitted only when neither is known.

FieldTypeDescription
network.cidrstringMost-specific registered network, in CIDR notation.
network.namestring | nullRegistry network name (netname), kept distinct from the organization.
network.typestring | nullRegistry allocation or assignment type.
network.rirstring | nullLowercase regional registry code.
network.country_codestring | nullCountry code on the registry network record.
network.rdap_urlstring | nullPublic RDAP URL for the registered network.
network.abuse_scoreobject | nullNumeric score and level for this CIDR. Null only when network is a broad allocation fallback with no scored whois record.

organization — registered operator

Omitted when the registry network names no organization or domain.

FieldTypeDescription
organization.namestring | nullOrganization named on the registered network.
organization.domainstring | nullThe organization's domain, when the registry publishes one.

abuse_contact

Omitted when the registry publishes no abuse email.

FieldTypeDescription
abuse_contact.namestring | nullName of the abuse contact or team.
abuse_contact.addressstring | nullPostal address on the abuse record.
abuse_contact.country_codestring | nullThe abuse contact's country, falling back to the network's country.
abuse_contact.emailstringThe abuse email address. The section is omitted entirely when the registry publishes no email.
abuse_contact.phonestring | nullPhone number on the abuse record.

datacenter — hosting

Omitted when the address is not in a published cloud range and its AS is not classified as hosting.

FieldTypeDescription
datacenter.providerstring | nullThe provider name, such as Google or AWS.
datacenter.domainstring | nullThe provider's domain. Null when the match came from AS classification rather than a published range.
datacenter.networkstring | nullThe covering network in CIDR notation.
datacenter.country_codestring | nullCountry for the range. Null for matches from published cloud ranges, which do not carry one.
datacenter.regionstring | nullProvider region, such as us-east4, when the range names one.
datacenter.citynullReserved. Always null today.

services — VPN, proxy and platform egress

One stable array covers all relay services. The kind enum distinguishes commercial VPNs, scanned proxies and platform egress such as iCloud Private Relay or Cloudflare WARP.

FieldTypeDescription
services[].kindvpn | proxy | platform_egressWhat kind of relay service the match represents.
services[].providerstring | nullService name, such as NordVPN or iCloud Private Relay.
services[].urlstring | nullThe service's website.
services[].rolestring | nullAddress role: exit_node, server or relay.
services[].confidencestringHow the match was established: enumerated, interpolated or assumed.
services[].last_seenstringISO 8601 timestamp of the last time we saw this range.
services[].exit.regionstring | nullThe region the service advertises for this exit.
services[].exit.country_codestring | nullTwo-letter country code for the exit.
services[].exit.citystring | nullCity the service advertises for this exit.
services[].exit.latitudenumber | nullLatitude the service advertises for this exit.
services[].exit.longitudenumber | nullLongitude the service advertises for this exit.

location — geolocation

Omitted when we cannot determine a country for the address.

FieldTypeDescription
location.continentstring | nullContinent name, such as North America.
location.continent_codestring | nullTwo-letter continent code, such as NA or EU.
location.countrystring | nullCountry name in English.
location.country_codestringTwo-letter ISO country code. The section is omitted entirely when we cannot determine a country.
location.statestring | nullThe subdivision name, resolved from the code the geofeed published where possible.
location.citystring | nullCity name as the geofeed published it.
location.latitudenumber | nullLatitude from the geofeed, or the centre of the matched city or region.
location.longitudenumber | nullLongitude, from the same source as latitude.
location.zipstring | nullPostal code, when the geofeed published one.
location.timezonestring | nullIANA time zone name, such as America/New_York.
location.local_timestring | nullCurrent local time in that zone, as an ISO 8601 string.
location.local_time_unixnumber | nullThe same instant as a Unix timestamp in seconds.
location.is_dstboolean | nullWhether daylight saving time is in effect in that zone now.
location.utcoffsetstring | nullCurrent offset from UTC, such as -04:00.
location.is_eu_memberbooleanWhether the country is a member of the European Union.
location.calling_codestring | nullInternational dialling code for the country.
location.currency_codestring | nullISO 4217 currency code for the country.
location.precisionstreet | city | region | countrySemantic resolution of the result. This does not claim a radius we cannot substantiate.
location.basisoperator_geofeed | registry_allocationWhether the country/place came from an operator-published geofeed or registry allocation.

Keyless response

Requests without a key get a strict subset of the same canonical structure. Field names and nesting never change between plans.

FieldTypeDescription
ipstringThe address you asked about.
is_bogonbooleanSame meaning as in the full response.
is_datacenterbooleanSame as above.
is_anycastbooleanSame as above.
is_torbooleanSame as above.
is_proxybooleanSame as above.
is_vpnbooleanSame as above.
is_abuserbooleanSame as above.
is_mobilebooleanSame as above.
is_satellitebooleanSame as above.
is_crawlerbooleanSame as above.
crawler.namestringPresent only for a verified crawler match.
organization.namestring | nullPresent when a registered organization is known.
asn.number / asn.namenumber / string | nullThe canonical ASN identity subset.
location.country_code / latitude / longitudestring / number | nullThe canonical geolocation subset.
elapsed_msnumberLookup time in milliseconds.

AS number lookups

Pass an AS number as q to get the organization and every prefix the AS announces. The response shape is different from an address lookup.

GET /api?q=AS13335&pretty=1
{
  "asn": {
    "number": 13335,
    "name": "Cloudflare, Inc.",
    "domain": "cloudflare.com",
    "country_code": "US",
    "route": "1.0.0.0/24",
    "rir": "arin",
    "active": true
  },
  "prefixes": {
    "ipv4": ["1.0.0.0/24", "1.1.1.0/24", "..."],
    "ipv6": ["2400:cb00::/32", "..."]
  },
  "elapsed_ms": 412.8
}

prefixes.ipv4 and prefixes.ipv6 hold the announcements. An AS we know nothing about returns a 400.

Errors

Errors use the selected response format and always carry error and elapsed_ms. A negotiation error has no selected representation, so its explanatory body is JSON.

StatusBodyWhen
400{"error":"Invalid IP Address or AS Number","elapsed_ms":0.015}q is missing, is not a valid address or AS number, carries a CIDR mask, or names an AS we have no data for.
400{"error":"Body must be {\"ips\": [...]} with 1 to 100 addresses","elapsed_ms":0.02}A bulk POST body that is not valid JSON, or has no ips array, or has more than 100 entries.
400{"error":"Invalid output format. Use json, csv, text, html, or toon","elapsed_ms":0.01}format names something the API does not provide.
401{"error":"An API key is required for bulk lookups","elapsed_ms":0.007}A bulk POST without a key.
429{"error":"Rate limit exceeded","limit":500,"elapsed_ms":8.1}The keyless daily quota for your IP is used up.
406{"error":"No acceptable output format is available","elapsed_ms":0.01}Accept excludes every representation the API provides.

CORS and caching

  • Every response carries Access-Control-Allow-Origin: *, so you can call the API straight from a browser.
  • OPTIONS preflight is answered with 200 and allows GET, POST, OPTIONS.
  • Responses are sent with Cache-Control: no-store. Cache on your side if you need to.
  • Negotiated /api responses include Vary: Accept, so compliant caches keep representations separate.

Examples

JavaScript
const response = await fetch(
  'https://ip-api.dev/api?q=8.8.8.8&key=YOUR_KEY',
)
const data = await response.json()

console.log(data.location?.country, data.asn?.name)
if (data.is_vpn || data.is_tor || data.is_abuser) {
  console.log('flagged:', data.ip)
}
Python
import requests

response = requests.get(
    "https://ip-api.dev/api",
    params={"q": "8.8.8.8", "key": "YOUR_KEY"},
    timeout=10,
)
response.raise_for_status()
data = response.json()

print(data["asn"]["name"], data["location"]["country"])

Ready to go? Get a free key or browse the IP range explorer.