ipinfo.app  ·  Successor to ip2asn.ipinfo.app

Atlas API

IP geolocation, ASN lookups, and BGP prefix resolution.
Base URL: https://atlas.ipinfo.app  ·  CORS enabled  ·  No auth required  ·  Data: iptoasn.com

Free No API key IPv4 + IPv6 Hostname resolution BGP prefix data
// API v2 — JSON responses
GET /api/v2/ip/:ip_or_hostname

Full geolocation and network lookup for an IPv4 address, IPv6 address, or resolvable hostname. Returns country, continent, ASN, organisation name, and — as of v0.1 — the most-specific announced BGP prefix covering the IP. Cached for 7 days.

Response fields
FieldTypeDescription
ipstringResolved IP address. May differ from input when a hostname is provided.
country_codestringISO 3166-1 alpha-2 code, e.g. "AU".
countrystringFull country name, e.g. "Australia".
country_flagstringUnicode flag emoji, e.g. "🇦🇺".
continentstringFull continent name, e.g. "Oceania".
as_numbernumberASN as an integer, e.g. 13335.
as_descriptionstringOrganisation name from the routing registry, e.g. "CLOUDFLARENET".
cidrnewstring|nullMost-specific announced BGP prefix containing this IP, e.g. "1.1.1.0/24". null when the ASN has no prefix entries in the database.
errornull|stringNull on success. Error message on failure.
Example requests
curl https://atlas.ipinfo.app/api/v2/ip/1.1.1.1 curl https://atlas.ipinfo.app/api/v2/ip/2606:4700::1111 curl https://atlas.ipinfo.app/api/v2/ip/cloudflare.com
Example response — 200 OK
{ "ip": "1.1.1.1", "country_code": "AU", "country": "Australia", "country_flag": "🇦🇺", "continent": "Oceania", "as_number": 13335, "as_description": "CLOUDFLARENET", "cidr": "1.1.1.0/24", "error": null }
Error response — 404 Not Found
{ "ip": "127.0.0.1", // private/reserved addresses are not in the database "country_code": null, "country": null, "country_flag": null, "continent": null, "as_number": null, "as_description": null, "cidr": null, "error": "IP address not found in database." }
GET /api/v2/asn/:as_number

Returns all CIDR prefixes announced by an ASN, annotated with country and continent data. Now also includes pre-computed announcement size stats so consumers don't need to iterate the full prefix list. :as_number accepts a bare integer or an AS-prefixed string (e.g. 13335 or AS13335). Cached for 24 hours.

Response fields
FieldTypeDescription
as_numbernumberThe ASN as an integer.
as_descriptionstringOrganisation name from the registry.
ipv4_countnewnumberTotal IPv4 host addresses across all announced prefixes (sum of 2^(32−prefixLen)).
ipv6_prefix_countnewnumberNumber of IPv6 prefix entries. IPv6 address space is too large to represent as a single sum.
cidrsobject[]Array of prefix objects.
cidrs[].cidrstringCIDR notation, e.g. "1.1.1.0/24".
cidrs[].country_codestringISO 3166-1 alpha-2 code for this prefix's allocation.
cidrs[].countrystringFull country name for this prefix.
cidrs[].continentstringFull continent name for this prefix.
errornull|stringNull on success.
Example requests
curl https://atlas.ipinfo.app/api/v2/asn/AS13335 curl https://atlas.ipinfo.app/api/v2/asn/13335
Example response — 200 OK
{ "as_number": 13335, "as_description": "CLOUDFLARENET", "ipv4_count": 1572864, // total IPv4 addresses announced "ipv6_prefix_count": 4, // number of IPv6 prefix entries "cidrs": [ { "cidr": "1.1.1.0/24", "country_code": "AU", "country": "Australia", "continent": "Oceania" } // ... more prefixes ], "error": null }
Error response — 404 Not Found
{ "as_number": 999999, "as_description": null, "ipv4_count": 0, "ipv6_prefix_count": 0, "cidrs": [], "error": "AS number not found or has no associated CIDRs in the database." }
GET /api/v2/hop/:ipnew

Traceroute hop annotation — returns the reverse-DNS record, the ASN details, the internet exchange the address sits on if any, and a best-effort "PoP" (point of presence) match. An address inside a peering LAN is at that exchange by definition, so it places the hop outright; otherwise the PoP is derived from parsing the PTR against known carrier naming conventions (Hurricane Electric, Cogent, GTT, Zayo, Lumen/Level3, NTT). When no carrier pattern fits, a tier-2 "scatter" pass looks for 3-letter IATA hub codes and known city names anywhere in the hostname. Supports IPv4 and IPv6. Auto-generated IPv6 PTRs (e.g. residential Comcast nibble-hex labels) are detected and stripped before matching. Cached for 24 hours; rate-limited to 300 requests/minute per client IP.

Response fields
FieldTypeDescription
ipstringThe IP as supplied in the URL.
ip_version4 | 6 | nullAddress family. null only on 400 errors.
ptrstring|nullReverse-DNS hostname. null when no PTR is published or lookup timed out (2s).
asnobject|nullASN metadata (same shape as /api/v2/ip). null when the IP is not in the routing table.
asn.numbernumberAS number as integer.
asn.namestringOrganisation name from the registry.
asn.country_codestringISO 3166-1 alpha-2 country code.
ixobject|nullThe internet exchange whose peering LAN contains this address, or null — which is the answer for almost every address. The full record, including every LAN and member, is at /api/v2/ix/ip/:ip.
ix.name / ix.city / ix.countrystring|nullExchange name and location. country is PCH's country name ("Germany"), not a code — unlike every other country field here.
ix.prefixstringThe peering LAN that matched.
ix.memberobject|nullThe network whose port holds this exact address, when PCH has it on file. null means not recorded, never "probably".
ix.source / ix.licensestring|nullAttribution for the exchange data (Packet Clearing House, CC BY-NC-SA 3.0). Render these wherever you render ix.
popobject|nullMatched PoP annotation, or null when nothing matched.
pop.iatastringCanonical 3-letter IATA code for the PoP city. null on an exchange match when the exchange has no IATA code.
pop.citystringCity name.
pop.countrystringISO alpha-2 country code.
pop.lat / pop.lonnumberAirport coordinates (proxy for PoP location).
pop.matchedstringOrigin of the match: ix:<id>, carrier:<suffix> or scatter:<token>.
pop.confidence"high"|"medium"|"low"high = peering-LAN match or carrier pattern; medium = scatter hit whose country agrees with the ASN country; low = scatter hit only.
ptr_style"normal"|"auto"|"auto-prefix"|nullauto = PTR was auto-generated (no hint); auto-prefix = we stripped the auto label and still matched the suffix.
errornull|stringNull on success. Error message on 400 (invalid IP) or 429 (rate-limited).
Example requests
curl https://atlas.ipinfo.app/api/v2/hop/184.105.213.157 curl https://atlas.ipinfo.app/api/v2/hop/2001:470:0:68::2 curl https://atlas.ipinfo.app/api/v2/hop/154.54.30.173
Example response — 200 OK (carrier hit, high confidence)
{ "ip": "184.105.213.157", "ip_version": 4, "ptr": "100ge15-1.core1.lax1.he.net", "asn": { "number": 6939, "name": "HURRICANE", "country_code": "US", "country": "United States", "country_flag": "🇺🇸", "continent": "North America" }, "pop": { "iata": "LAX", "city": "Los Angeles", "country": "US", "lat": 33.9425, "lon": -118.408, "matched": "carrier:he.net", "confidence": "high" }, "ptr_style": "normal", "error": null }
Example response — 200 OK (no match)
{ "ip": "8.8.8.8", // google — generic PTR, no PoP hint "ip_version": 4, "ptr": "dns.google", "asn": { /* ... */ }, "pop": null, "ptr_style": "normal", "error": null }
Error response — 429 Too Many Requests
{ "ip": "...", "error": "Rate limit exceeded. Try again in 60 seconds." }
GET /api/v2/routing/snapshotnew

Provenance for the topology dataset this instance is serving. Every other routing response embeds the same block, so a caller can always state what it is showing and when it was observed. Fetch this once to decide whether the routing endpoints are worth calling at all: 404 means no topology is loaded here, which is a normal state — a database image built before the routing tables existed, or a web image newer than its database image mid-rollout — and not an error. Cached for 24 hours.

Response fields
FieldTypeDescription
rib_tsstringISO 8601 timestamp of the RIB dump the data was parsed from — when the internet looked like this, not when it was loaded.
collectorsstringComma-separated collectors that contributed, e.g. "route-views2,rrc00".
peer_countnumberDistinct collector peers whose AS_PATHs went into the snapshot. This is the denominator for every observations and peers_seen figure elsewhere.
route_entriesnumberRoute entries parsed across all collectors.
Example request
curl https://atlas.ipinfo.app/api/v2/routing/snapshot
Example response — 200 OK
{ "rib_ts": "2026-08-05T02:00:00.000Z", "collectors": "route-views2,rrc00", "peer_count": 74, "route_entries": 73320063 }
Response — 404 Not Found (no topology loaded)
{ "error": "no routing snapshot loaded" }
GET /api/v2/routing/asn/:as_numbernew

Observed BGP topology for an autonomous system: the networks that carry its traffic, the networks it carries traffic for, and every AS seen adjacent to it in a routing path. Derived from bulk MRT routing tables (RouteViews route-views2 and RIPE RIS rrc00), which is the only way to answer "who provides transit to this network". Related networks come back named, joined in-database, so rendering a peering table costs one request rather than one lookup per row. Accepts 15169 or AS15169. Cached for 24 hours.

Everything here is an observation. These are AS_PATHs seen by a specific set of collector peers at a specific time, so every response embeds a snapshot block and callers are expected to show it. Two claims the data does not support: adjacency is not peering (two ASes next to each other in a path may be in a transit, settlement-free peering or customer relationship, and routing data cannot tell them apart, which is why the field is neighbours), and upstreams are inferred from the AS immediately preceding the origin, so for a tier-1 that buys transit from nobody it surfaces that network's peers instead. The response carries inferred: true so this cannot be missed.

Query parameters
ParameterDefaultDescription
limit100Caps each of upstreams, downstreams and neighbours independently. Clamped to 500. The *_count fields are unaffected, so a truncated list is always detectable.
Response fields
FieldTypeDescription
as_numbernumberThe AS as supplied in the URL, normalised to an integer.
namestring|nullOperator name. null when the AS is observed in paths but absent from the name dataset.
prefixes_v4 / prefixes_v6numberDistinct prefixes observed originating from this AS, per address family.
addresses_v4numberAddresses covered by those v4 prefixes. Summed from the observed prefix list, not from the registry or this service's own per-ASN size fields, which disagree with observed BGP in both directions. There is deliberately no addresses_v6: the figure exceeds any integer type worth carrying and nobody reasons about it, so IPv6 is counted in prefixes.
degree_v4 / degree_v6numberDistinct adjacent ASes per address family.
upstream_countnumberTotal upstreams, before limit is applied.
downstream_countnumberTotal downstreams, before limit is applied.
upstreamsarrayNetworks seen immediately before this AS where it is the origin, most-observed first. Inferred — see above.
downstreamsarrayThe inverse: networks for which this AS appears as the upstream.
neighboursarrayEvery adjacent AS in either direction, most-observed first.
upstreams[].as_numbernumberThe related AS.
upstreams[].namestring|nullOperator name for the related AS, already resolved.
upstreams[].af4 | 6Address family the adjacency was observed in. An AS pair adjacent over both appears twice.
upstreams[].observationsnumberHow many collector peers saw this adjacency. Weight it against snapshot.peer_count; a 1-of-74 adjacency is not the same claim as 68-of-74.
inferredtrueAlways present and always true. Do not render these relationships as fact.
snapshotobjectProvenance block, same shape as /api/v2/routing/snapshot.
Example requests
curl https://atlas.ipinfo.app/api/v2/routing/asn/AS15169 curl https://atlas.ipinfo.app/api/v2/routing/asn/15169 curl "https://atlas.ipinfo.app/api/v2/routing/asn/AS15169?limit=10"
Example response — 200 OK
{ "as_number": 15169, "name": "Google LLC", "prefixes_v4": 1227, "prefixes_v6": 176, "addresses_v4": 2615808, "degree_v4": 302, "degree_v6": 118, "upstream_count": 21, "downstream_count": 37, "upstreams": [ { "as_number": 6453, "name": "TATA COMMUNICATIONS (AMERICA) INC", "af": 4, "observations": 41 }, { "as_number": 6939, "name": "Hurricane Electric LLC", "af": 4, "observations": 35 } ], "downstreams": [ { "as_number": 396982, "name": "Google LLC", "af": 4, "observations": 69 }, { "as_number": 36492, "name": "Google, LLC", "af": 4, "observations": 33 } ], "neighbours": [ /* same shape, both directions, most-observed first */ ], "inferred": true, "snapshot": { "rib_ts": "2026-08-05T02:00:00.000Z", "collectors": "route-views2,rrc00", "peer_count": 74, "route_entries": 73320063 } }
Response — 404 Not Found (AS not observed)
{ // allocated but not announcing, or announced only where our collectors cannot see "error": "AS not seen in any observed path", "as_number": 64496, "snapshot": { /* ... */ } }
Error response — 400 Bad Request
{ "error": "invalid AS number" }
GET /api/v2/routing/ip/:ipnew

How the internet actually reaches one address: every announcement covering it, most specific first, each with the AS originating it and how many collector peers saw that origin. This is strict BGP origin, which is a different and stricter question than "which ASN is this IP registered to" that /api/v2/ip/:ip answers. IPv4 and IPv6. Cached for 24 hours.

routed: false is a real answer, not a 404 — the address is allocated but not reachable, or announced only where our collectors cannot see. moas: true means the most specific prefix is announced by more than one AS: legitimate multi-homing or anycast, or a hijack. It is reported, never adjudicated. Note that two different prefixes covering one address is ordinary deaggregation and does not set moas.

Response fields
FieldTypeDescription
ipstringThe address as supplied in the URL.
routedbooleanfalse when no observed prefix covers the address.
coveringarrayCovering announcements, longest prefix first, then most-observed. Capped at 8.
covering[].prefixstringThe announced CIDR block.
covering[].origin_asnnumberAS at the end of the AS_PATH for that prefix.
covering[].namestring|nullOperator name for the origin, already resolved.
covering[].peers_seennumberCollector peers that saw this prefix from this origin. Weigh against snapshot.peer_count.
moasbooleanMore than one origin on the most specific covering prefix.
origin_countnumberDistinct origins on that most specific prefix. 0 when unrouted.
snapshotobjectProvenance block, same shape as /api/v2/routing/snapshot.
Example requests
curl https://atlas.ipinfo.app/api/v2/routing/ip/8.8.8.8 curl https://atlas.ipinfo.app/api/v2/routing/ip/2001:4860:4860::8888
Example response — 200 OK
{ "ip": "8.8.8.8", "routed": true, "covering": [ { "prefix": "8.8.8.0/24", "origin_asn": 15169, "name": "Google LLC", "peers_seen": 74 }, { "prefix": "8.8.8.0/23", "origin_asn": 15169, "name": "Google LLC", "peers_seen": 72 } ], "moas": false, // two different prefixes, one origin — deaggregation, not MOAS "origin_count": 1, "snapshot": { /* ... */ } }
Example response — 200 OK (unrouted)
{ "ip": "192.0.2.1", "routed": false, "covering": [], "moas": false, "origin_count": 0, "snapshot": { /* ... */ } }
Error response — 400 Bad Request
{ "error": "invalid IP address" }
GET /api/v2/threat/:ipalpha
Alpha. The FBI IC3 threat feed is an early-stage feature — coverage (which advisories are parsed) and the response shape may still change without notice. Treat it as experimental provenance, not a stable contract, and don't build automated blocking solely on it yet.

FBI IC3 malicious-IP reputation. Answers "is this IP listed in an FBI IC3 Cybersecurity Advisory (a 'flash')?" from the rolling last year of advisories at ic3.gov/CSA, alongside the geo/ASN summary — one call for where/whose an IP is and whether the FBI has named it. A clean IP returns 200 with ic3.listed = false (not 404). This is provenance, not a verdict: advisory IPs age and get reassigned, and advisories occasionally list victim/sinkhole IPs — weigh it as one signal, never a standalone block. Cached 24h.

Response fields
FieldTypeDescription
ipstringThe IP as supplied.
country / country_code / country_flag / continentstring|nullGeo summary (same source as /api/v2/ip); null when the IP isn't in the DB.
as_number / as_descriptionnumber|string|nullOwning ASN.
ic3.listedbooleanTrue if the IP matches one or more advisories.
ic3.countnumberNumber of matching advisory rows.
ic3.advisories[]arrayEach: advisory_id, title, pub_date, source_url (the advisory PDF), cidr (the listed entry).
errornull|stringNull on success; message on 400 (invalid IP).
Companion routes
RouteReturns
/api/v2/threat/listPlain text — every listed IP, one per line (hosts bare, ranges as CIDR). Firewall URL-table / external-connector feed.
/api/v2/threat/asn/:asJSON { as_number, ic3_count, iocs[] } — listed IOCs attributed to an ASN, each ioc carrying the advisory that named it (advisory_id, title, pub_date, source_url).
Example requests
curl https://atlas.ipinfo.app/api/v2/threat/45.148.10.212 curl https://atlas.ipinfo.app/api/v2/threat/list curl https://atlas.ipinfo.app/api/v2/threat/asn/AS12345
Example response — 200 OK (listed)
{ "ip": "45.148.10.212", "country_code": "NL", "as_number": 12345, "as_description": "Example BV", "ic3": { "listed": true, "count": 1, "advisories": [ { "advisory_id": "260702", "title": "Cyber Criminal Group TeamPCP", "pub_date": "2026-07-02", "source_url": "https://www.ic3.gov/CSA/2026/260702.pdf", "cidr": "45.148.10.212" } ] }, "error": null }
GET /api/v2/description/:as_number

A curated, plain-language "what is this network" TLDR for well-known ASNs — what it's used for, its location, and notable / interesting facts. Accepts 13335 or AS13335. AI-generated, best-effort context (see the disclaimer field): not authoritative and carries no guarantee of accuracy. body_markdown is a small markdown subset meant for client-side rendering. Only a curated set of ASNs has an entry — any other returns 404 (a normal answer, not an error). The source files are public and editable via source_url (the co-op repo). Cached 24h.

Response fields
FieldTypeDescription
as_numbernumberThe ASN, as an integer.
namestring|nullOperator name.
locationstring|nullPrimary geography, or Global.
tags[]arrayShort lowercase labels (e.g. cdn, transit).
generatedstringProvenance of the text — currently always ai.
body_markdownstringThe description body, a small markdown subset (headings, lists, bold/italic, links).
disclaimerstringFixed AI-generated / no-accuracy-guarantee notice.
source_urlstringLink to the source markdown in the public co-op repo.
errornull|stringNull on success; message on 400 (invalid AS) / 404 (no entry).
Example request
curl https://atlas.ipinfo.app/api/v2/description/AS13335
Example response — 200 OK
{ "as_number": 13335, "name": "Cloudflare, Inc.", "location": "Global", "tags": ["cdn", "dns", "security"], "generated": "ai", "body_markdown": "Cloudflare runs one of the world's largest edge networks...", "disclaimer": "This description is AI-generated...", "source_url": "https://gitlab.com/cmunroe/co-op/-/blob/main/asn/details/AS13335.md", "error": null }
GET /api/v2/ix/snapshotnew

Provenance and licence for the internet-exchange dataset this instance is serving. Every other /api/v2/ix/* response embeds the same block. Fetch this once to decide whether the exchange endpoints are worth calling at all: 404 means no exchange data is loaded here, which is a normal state on a database image built before these tables existed, and not an error. Cached for 24 hours.

Attribution is required, not optional. The data is Packet Clearing House's, under CC BY-NC-SA 3.0: free to redistribute non-commercially, with attribution, under the same terms. That is why source and license are fields in the response rather than a footnote here — if you render this data, render those too.

Response fields
FieldTypeDescription
collected_atstringISO 8601 timestamp of the collection run, not of the image build.
sourcestringUpstream dataset. Display it.
licensestringLicence the data is redistributed under. Display it.
exchangesnumberExchanges in the directory, including planned, deprecated and defunct ones.
prefixesnumberPeering LANs across all exchanges, both address families.
membersnumberIndividual member addresses recorded on those LANs.
Example request
curl https://atlas.ipinfo.app/api/v2/ix/snapshot
Example response — 200 OK
{ "collected_at": "2026-08-06T15:12:39.005Z", "source": "Packet Clearing House (https://www.pch.net/ixp/data)", "license": "CC BY-NC-SA 3.0", "exchanges": 1327, "prefixes": 1940, "members": 93127 }
Response — 404 Not Found (no exchange data loaded)
{ "error": "no exchange snapshot loaded" }
GET /api/v2/ix/ip/:ipnew

Is this address sitting on a peering LAN, and whose port is it? This is the endpoint worth building on: a traceroute hop inside a peering LAN is at that exchange, which locates it far more reliably than an rDNS guess — the exchange's coordinates apply to the interface, not to the network that owns it. IPv4 and IPv6. Cached for 24 hours.

on_ix: false is the ordinary answer, not a 404 — most addresses are not on an exchange. ix.member names the network holding the port when Packet Clearing House has that exact address on file, and is null otherwise; it is an exact-address match, so there is no half-answer to misread. Deprecated LANs are matched deliberately — a hop on a retired peering LAN still crossed that exchange — and ix.prefix_status says so.

ix is singular, and sometimes that is one of two true answers. Packet Clearing House records a handful of fabrics under more than one exchange id — 206.72.210.0/23 is listed by both 373 and 2450, which are the same Los Angeles exchange — so also_recorded_as sits beside ix and names every other exchange whose active LAN also covers the address. It is [] for an unambiguous address, which is nearly all of them. ix itself is unchanged; nothing that worked before moves.

Anything covering the address counts here, not only an identical LAN: a carve-out registered by a different exchange is a competing claim on where the address lives, and that is precisely what the field exists to expose. Compare related on /api/v2/ix/:id, which asks the narrower question of whether two entries are the same fabric.

Response fields
FieldTypeDescription
ipstringThe address as supplied in the URL.
on_ixbooleanfalse when no peering LAN covers the address.
ixobject|nullThe matched exchange, or null. Most specific LAN wins where blocks overlap.
ix.prefixstringThe peering LAN that matched.
ix.prefix_statusstringActive, Deprecated, Unknown or Defunct — the LAN's status, not the exchange's.
ix.participantsnumberPorts on that LAN as recorded upstream.
ix.lat / ix.lonnumber|nullExchange coordinates. This is what makes the match useful for geolocating a hop.
ix.iatastring|nullNearest airport code, for ~25% of exchanges. Joins onto the same PoP vocabulary /api/v2/hop/:ip uses.
ix.memberobject|nullThe network occupying the address, when recorded.
ix.member.as_numbernumberAS holding the port.
ix.member.namestring|nullOperator name, resolved in-database from the routing dataset, falling back to the upstream string.
ix.member.rdnsstring|nullPTR recorded upstream. Compare against the live PTR from /api/v2/hop/:ip.
ix.member.peering_policystring|nullOpen, Selective, Restrictive — as declared by the member.
also_recorded_asarrayOther exchanges whose active LAN also covers this address. [] when the address is unambiguous. Always present.
also_recorded_as[].pch_idnumberThe other exchange's id. Named pch_id, not id, because it identifies a different record from ix.
also_recorded_as[].name / city / country / status / portsThe other exchange's own directory entry.
also_recorded_as[].member_countnumberMember addresses on that record. The size gap is usually how you tell the fuller entry from the thinner one.
also_recorded_as[].prefix / prefix_status / participantsThe covering LAN that made it a match, as recorded against that exchange.
also_recorded_as[].pch_urlstringDeep link to the other exchange upstream.
snapshotobjectProvenance block, same shape as /api/v2/ix/snapshot.
Example requests
curl https://atlas.ipinfo.app/api/v2/ix/ip/80.249.208.247 curl https://atlas.ipinfo.app/api/v2/ix/ip/2001:7f8:1::a501:5169:1 # a LAN the directory records twice curl https://atlas.ipinfo.app/api/v2/ix/ip/206.72.210.1
Example response — 200 OK (on an exchange)
{ "ip": "80.249.208.247", "on_ix": true, "ix": { "id": 160, "name": "AMS-IX Amsterdam", "city": "Amsterdam", "country": "Netherlands", "iata": "AMS", "lat": 52.3, "lon": 4.77, "prefix": "80.249.208.0/21", "prefix_status": "Active", "participants": 893, "member": { "as_number": 15169, "name": "Google LLC", "ip": "80.249.208.247", "rdns": "core1.ams.net.google.com", "peering_policy": "Open" } }, "also_recorded_as": [], "snapshot": { /* ... */ } }
Example response — 200 OK (a LAN recorded under two exchange ids)
{ "ip": "206.72.210.1", "on_ix": true, "ix": { "id": 373, "name": "Any2 California", "city": "Los Angeles", "prefix": "206.72.210.0/23", "prefix_status": "Active", "member": null }, // the same fabric, under the exchange's current branding "also_recorded_as": [ { "pch_id": 2450, "name": "Coresite - Any2 West", "city": "Los Angeles", "country": "United States", "status": "Active", "ports": 344, "member_count": 586, "prefix": "206.72.210.0/23", "prefix_status": "Active", "participants": 344, "pch_url": "https://www.pch.net/ixp/details/2450" } ], "snapshot": { /* ... */ } }
Example response — 200 OK (not on an exchange)
{ "ip": "8.8.8.8", "on_ix": false, "ix": null, "also_recorded_as": [], "snapshot": { /* ... */ } }
Error response — 400 Bad Request
{ "error": "invalid IP address" }
GET /api/v2/ix/asn/:as_numbernew

Every internet exchange a network is present on, with the address it holds there. One entry per exchange even for a dual-stack member on several ports. Pairs naturally with /api/v2/routing/asn/:as: that says who a network exchanges traffic with, this says where it does so. Accepts 15169 or AS15169. Cached for 24 hours.

An empty list is a real answer. Membership here is what Packet Clearing House has recorded, not what is live: roughly half the exchanges in the directory have no membership on file at all, and a network may also peer entirely privately. Absence is not evidence of absence.

Query parameters
ParameterDefaultDescription
limit200Maximum exchanges returned. Clamped to 500.
Response fields
FieldTypeDescription
as_numbernumberThe AS as supplied in the URL, normalised to an integer.
countnumberExchanges returned, after limit.
exchangesarrayExchange summaries, each with the member's own ip, rdns and peering_policy at that exchange.
snapshotobjectProvenance block, same shape as /api/v2/ix/snapshot.
Example requests
curl https://atlas.ipinfo.app/api/v2/ix/asn/AS13335 curl "https://atlas.ipinfo.app/api/v2/ix/asn/15169?limit=10"
Example response — 200 OK
{ "as_number": 13335, "count": 245, "exchanges": [ { "id": 160, "name": "AMS-IX Amsterdam", "city": "Amsterdam", "country": "Netherlands", "iata": "AMS", "ip": "80.249.208.6", "rdns": "cloudflare.ams-ix.net", "peering_policy": "Open" } ], "snapshot": { /* ... */ } }
Error response — 400 Bad Request
{ "error": "invalid AS number" }
GET /api/v2/ix/:idnew

One exchange in full: where it is, its peering LANs across both address families, and the networks on them. Members come back named and ordered by their own connectivity, joined in-database against the routing dataset — so rendering a members table costs one request rather than one lookup per row, and the default page is the networks worth naming. The :id is Packet Clearing House's own exchange id, which pch_url links back to. Cached for 24 hours.

Deprecated and defunct LANs are included with their own status, because a hop on a retired peering LAN still identifies the exchange it crossed. Filter on that field if you only want live ones.

One fabric is sometimes two directory entries. Packet Clearing House occasionally carries the same physical exchange under two ids, with nothing upstream linking them: 373 “Any2 California” and 2450 “Coresite - Any2 West” are both in Los Angeles, both advertise the identical active LANs 206.72.210.0/23 and 2001:504:13::/64, and 2450's 586 member addresses are a strict subset of 373's 718. related names the other entries. It is [] for 1,296 of the 1,327 exchanges in the directory, so treat a non-empty one as a signal, not noise: counting both ids as separate exchanges inflates any total you derive.

Matched on the peering LAN, never on the name. A peering LAN is one L2 broadcast domain, so two entries advertising the same active prefix are the same fabric — a fact about the network, not a guess. Names are the opposite of a signal here: “Any2 California” and “Coresite - Any2 West” share no words, while the genuinely separate Any2 metros (Denver, Chicago, New York, each on its own LAN) share plenty. Deprecated prefixes are excluded because a retired block can be reassigned, and the prefixes must be identical rather than overlapping, because a carve-out of a larger block is a different claim.

We cross-link, we do not merge. Merging would mean electing an authoritative record, which throws away the retired-LAN history only 373 carries, and every member port is recorded against a specific id, so rewriting it would lose provenance. Both records stay exactly as published and point at each other; deciding which one to show is yours. Relations are direct neighbours, not transitive clusters — exchange 712 shares an IPv4 LAN with 2402 and an IPv6 LAN with 2382, so it lists both while 2382 lists only 712.

Query parameters
ParameterDefaultDescription
limit100Maximum member addresses. Clamped to 1000 — the largest exchange has over 6,000. member_count is unaffected, so truncation is always detectable.
membersSet to 0 to skip the member query and return the exchange and its LANs only. members comes back as [] rather than being dropped, so the response shape never changes.
Response fields
FieldTypeDescription
idnumberPacket Clearing House exchange id.
namestringExchange name, e.g. "AMS-IX Amsterdam".
city / country / regionstring|nullLocation. country is a name, not a code — the upstream publishes no code.
iatastring|nullNearest airport code, present for ~25% of exchanges.
lat / lonnumber|nullExchange coordinates.
websitestring|nullThe exchange's own site.
statusstringActive, Planned, Unknown, Deprecated, Defunct or Not an exchange. Every directory entry is served so an id always resolves to something explicable.
portsnumberConnected ports as recorded upstream.
traffic_bpsnumberReported peak traffic in bits per second. 0 where unreported, which is common.
updatedstring|nullWhen the upstream record was last touched, YYYY-MM-DD.
pch_urlstringDeep link to the upstream page for this exchange.
member_countnumberTotal member addresses, before limit.
prefixesarrayPeering LANs, IPv4 first. Each has prefix, af (4 or 6), status and participants.
membersarrayNetworks on those LANs, most-connected first. Each has as_number, name, ip, rdns and peering_policy.
relatedarrayOther directory entries advertising an identical active LAN — the same fabric under another id. [] for all but 31 of the 1,327 exchanges. Always present, and unaffected by ?members=0.
related[].pch_idnumberThe other exchange's id. Named pch_id, not id, because it identifies a different record from the one you asked for.
related[].name / city / country / status / portsThe other exchange's own directory entry, as published.
related[].member_countnumberMember addresses on that record. Uncapped, so the size gap against this exchange's member_count tells you which entry is the fuller one.
related[].shared_prefixesarrayThe peering LANs both entries advertise as active. Sorted, so the array is stable between requests.
related[].pch_urlstringDeep link to the other exchange upstream.
snapshotobjectProvenance block, same shape as /api/v2/ix/snapshot.
Example requests
curl https://atlas.ipinfo.app/api/v2/ix/160 curl "https://atlas.ipinfo.app/api/v2/ix/160?limit=10" curl "https://atlas.ipinfo.app/api/v2/ix/160?members=0" # the duplicate-entry case, from either side curl "https://atlas.ipinfo.app/api/v2/ix/373?members=0" curl "https://atlas.ipinfo.app/api/v2/ix/2450?members=0"
Example response — 200 OK
{ "id": 160, "name": "AMS-IX Amsterdam", "city": "Amsterdam", "country": "Netherlands", "region": "Europe", "iata": "AMS", "lat": 52.3, "lon": 4.77, "website": "https://www.ams-ix.net/ams", "status": "Active", "ports": 893, "pch_url": "https://www.pch.net/ixp/details/160", "member_count": 2206, "prefixes": [ { "prefix": "80.249.208.0/21", "af": 4, "status": "Active", "participants": 893 }, { "prefix": "195.69.144.0/22", "af": 4, "status": "Deprecated", "participants": 0 }, { "prefix": "2001:7f8:1::/64", "af": 6, "status": "Active", "participants": 892 } ], // no other directory entry advertises these LANs "related": [], "members": [ { "as_number": 6939, "name": "Hurricane Electric LLC", "ip": "80.249.209.150", "rdns": "amsix-400gbps.core1.ams1.he.net", "peering_policy": "Open" }, { "as_number": 15169, "name": "Google LLC", "ip": "80.249.209.100", "rdns": "core2.ams.net.google.com", "peering_policy": "Open" } ], "snapshot": { /* ... */ } }
Example response — 200 OK (one fabric, two ids)
# GET /api/v2/ix/373?members=0 { "id": 373, "name": "Any2 California", "city": "Los Angeles", "status": "Active", "ports": 322, // 2009 — the record predates the CoreSite rebrand "updated": "2009-06-22", "member_count": 718, "prefixes": [ { "prefix": "206.72.210.0/23", "af": 4, "status": "Active", "participants": 322 }, // history only this record carries — one reason we do not merge { "prefix": "206.223.143.0/24", "af": 4, "status": "Deprecated", "participants": 449 }, { "prefix": "2001:504:13::/64", "af": 6, "status": "Active", "participants": 301 } ], "related": [ { "pch_id": 2450, "name": "Coresite - Any2 West", "city": "Los Angeles", "country": "United States", "status": "Active", "ports": 344, "member_count": 586, "shared_prefixes": ["2001:504:13::/64", "206.72.210.0/23"], "pch_url": "https://www.pch.net/ixp/details/2450" } ], "members": [], "snapshot": { /* ... */ } } # and symmetrically, GET /api/v2/ix/2450 → related[0].pch_id == 373
Response — 404 Not Found (unknown exchange)
{ "error": "unknown exchange", "id": 999999, "snapshot": { /* ... */ } }
Error response — 400 Bad Request
{ "error": "invalid exchange id" }
GET /api/v2/rdap/snapshotnew

Provenance for the registry-allocation dataset this instance is serving. Every other /api/v2/rdap/* response embeds the same block. Fetch this once to decide whether the registry endpoints are worth calling at all: 404 means no registry data is loaded here, which is a normal state on a database image built before these tables existed, and not an error. Cached for 24 hours.

Nothing here speaks RDAP. These endpoints answer the subset of questions an RDAP lookup is normally used for, out of a local table built from the five RIRs' daily delegated-extended statistics files. The point is that a consumer can stop making five third-party requests per page view.

Response fields
FieldTypeDescription
collected_atstringISO 8601 timestamp of the collection run, not of the image build.
sourcestringUpstream dataset description.
source_urlsobjectThe five files, keyed by registry. There is no single licence covering all of them, so the per-registry URL is the provenance — check each registry's own conditions of use before redistributing.
registriesobjectPer-registry serial, generation date and row counts. This is how you tell a fresh answer from one built on a registry that quietly stopped republishing.
alloc_rowsnumberAddress allocations loaded, both families.
asn_rowsnumberAS-number allocation ranges loaded.
Example request
curl https://atlas.ipinfo.app/api/v2/rdap/snapshot
GET /api/v2/rdap/ip/:ipnew

Which registry holds this address, in which country, since when, and under what status. IPv4 or IPv6. Cached for 24 hours.

Read the fields_absent array before rendering. The delegated files carry no network name, organisation name or abuse contact, so those fields are not merely null here — they are not in the dataset. Rendering their absence as a fact about the network ("this network has no abuse contact") would be false. The array names them explicitly on every response so there is no need to guess.

status: "available" or "reserved" is a real answer, meaning the registries positively record that nobody holds this space. That is different from found: false, which means the address falls outside what the RIRs publish at all — IANA special-purpose space, roughly 14% of IPv4 (multicast 224/4, reserved 240/4, 0/8, 127/8 and friends). Both are 200s; a 404 is reserved for "no dataset loaded".

Response fields
FieldTypeDescription
querystringThe address as parsed.
foundbooleanWhether any delegated-statistics record covers it.
allocation.prefixstringThe covering CIDR as the registry publishes it.
allocation.rirstringarin | ripencc | apnic | lacnic | afrinic.
allocation.ccstringISO alpha-2, or null when not recorded.
allocation.statusstringallocated | assigned | available | reserved.
allocation.allocatedstringDelegation date (YYYY-MM-DD), or null.
allocation.opaque_idstringHolder id. Unique within its own registry only — always pair it with rir.
allocation.delegatedbooleanTrue for allocated/assigned; false for space nobody holds.
fields_absentarrayRDAP fields this dataset does not carry. See above.
sourceobjectThe snapshot block.
Example request
curl https://atlas.ipinfo.app/api/v2/rdap/ip/8.8.8.8
Example response — 200 OK
{ "query": "8.8.8.8", "found": true, "allocation": { "prefix": "8.8.8.0/24", "rir": "arin", "cc": "US", "status": "allocated", "allocated": "2023-12-28", /* ... */ } }
Error response — 400 Bad Request
{ "error": "invalid IP address" }
GET /api/v2/rdap/prefix/:cidrnew

The registry record covering an entire CIDR block. Same response shape as /api/v2/rdap/ip/:ip. Cached for 24 hours.

This is not the same question as asking about the block's first address, and the difference bites. A block can span several allocations and be covered by none of them: 8.8.0.0/16 contains 12 separate registry records, so looking up 8.8.0.0 answers 8.8.8.0/22 with every appearance of confidence. This endpoint requires containment of the whole block and returns found: false when no single record covers it, which is the truthful answer.

Host bits are tolerated: 8.8.8.8/24 is read as the /24 containing that address.

Example requests
curl https://atlas.ipinfo.app/api/v2/rdap/prefix/8.8.8.0/24 curl https://atlas.ipinfo.app/api/v2/rdap/prefix/2606:4700::/32
Error response — 400 Bad Request
{ "error": "invalid CIDR prefix" }
GET /api/v2/rdap/asn/:as_numbernew

The registry record covering an AS number. Accepts 15169 or AS15169. Cached for 24 hours.

Allocations are stored as closed ranges, so the response carries as_start and as_end rather than a single number — registries hand out 32-bit AS numbers in blocks, and the range is the record that actually exists. A single assignment simply has both ends equal.

Example request
curl https://atlas.ipinfo.app/api/v2/rdap/asn/AS15169
Example response — 200 OK
{ "query": 15169, "found": true, "allocation": { "as_start": 15169, "as_end": 15169, "rir": "arin", "cc": "US", "allocated": "2000-03-30", /* ... */ } }
GET /api/v2/rdap/holder/:rir/:opaque_idnew

Every other prefix and AS number the same holder has, taken from the opaque_id on any allocation response. This is the one field the delegated files give that RDAP cannot cheaply be asked for in bulk: it groups a registrant's resources without a name ever being involved. ?limit= caps each list (default 100, max 1000); the counts stay uncapped. Cached for 24 hours.

The registry is part of the key, not decoration. An opaque id is unique within one registry only — LACNIC publishes small integers, ARIN hex digests, RIPE UUIDs — and nothing coordinates them, so two registries can emit the same string for unrelated organisations. An endpoint keyed on the id alone would silently merge them, which is why this one will not accept it.

Response fields
FieldTypeDescription
prefixesarrayAddress allocations held, up to limit.
asnsarrayAS-number ranges held, up to limit.
prefix_countnumberTotal held, uncapped.
asn_countnumberTotal held, uncapped.
Example request
curl https://atlas.ipinfo.app/api/v2/rdap/holder/arin/9d99e3f7d38d1b8026f2ebbea4017c9f
Error response — 400 Bad Request
{ "error": "invalid holder id" }
// API v1 — Plain text responses
GET /api/v1/country/:ip_or_hostname

Returns the full country name as a plain text string. Ideal for shell pipelines. Accepts IPv4, IPv6, or any resolvable hostname. Cached for 7 days.

Example
curl https://atlas.ipinfo.app/api/v1/country/1.1.1.1 Australia
GET /api/v1/asn/:ip_or_hostname

Returns the bare ASN number as plain text — no "AS" prefix.

Example
curl https://atlas.ipinfo.app/api/v1/asn/1.1.1.1 13335
GET /api/v1/continent/:ip_or_hostname

Returns the full continent name as plain text.

Example
curl https://atlas.ipinfo.app/api/v1/continent/1.1.1.1 Oceania
// Support This Project

Atlas is free and unmetered. If you're using it in something cool, consider supporting continued development.

♥ Donate