API Documentation
IP-Atlas gives you IP geolocation, ASN data, and datacenter detection through one REST endpoint. Every field is included on every plan — you pay for throughput, not fields.
Base URL: https://api.ip-atlas.io
Response format: JSON (Content-Type: application/json)
Source IP handling: CF-Connecting-IP, then X-Real-IP, then the first entry of X-Forwarded-For, then the TCP source address.
Authentication
Pass your API key in the X-API-Key header:
# Every paid endpoint; all /json/* also accept this. curl https://api.ip-atlas.io/json/8.8.8.8 \ -H "X-API-Key: ipa_live_YOUR_API_KEY_HERE"
The /json/* endpoints also accept unauthenticated requests (free anonymous tier, 60 req/min per IP). Endpoints under /v1/* always require a key.
For browser demos the key may also be passed as ?key=… — supported so interactive demos work without CORS preflights, but never use it in production; keys leak into logs and Referer headers.
Rate limits
| Plan | Daily | Monthly | Notes |
|---|---|---|---|
| Anonymous | — | — | 60 req/min per IP |
| Free | 2,000 | 60,000 | Card on file required; non-commercial only |
| Developer | — | 2,000,000 | $19/mo |
| Startup | — | 10,000,000 | $79/mo |
| Business | — | 50,000,000 | $249/mo |
| Enterprise | Custom | 500M+/mo | Contact sales |
Every successful response includes advisory headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit-Day | Daily quota (only when enforced — Free tier) |
X-RateLimit-Remaining-Day | Remaining requests today |
X-RateLimit-Limit-Month | Monthly quota |
X-RateLimit-Remaining-Month | Remaining requests this calendar month |
X-Overage-Charged-Cents | USD cents charged on this request (0 if within quota) |
X-Spend-Cap-Reached | true when a 429 is caused by your spend cap |
On 429 Too Many Requests the response also includes Retry-After with the seconds until the window resets.
Overage billing
Every plan (including Free) has Automatic Overage Billing enabled by default. Once you pass your monthly quota, requests keep succeeding and are charged at your plan's overage rate:
| Plan | Overage |
|---|---|
| Free | $10.00 per 1,000,000 requests (i.e. $0.01 / 1,000) |
| Developer | $10.00 per 1,000,000 |
| Startup | $8.00 per 1,000,000 |
| Business | $6.00 per 1,000,000 |
Accrued overage is billed on the 1st of the following month via a Stripe Invoice Item. You'll receive warning emails at 80% and 95% of your monthly quota. To limit exposure:
- Turn off Automatic Overage Billing on your dashboard — requests will return
429at quota instead; - Set a spend cap (in USD cents) — once the cap is reached, requests return
429withX-Spend-Cap-Reached: true.
Errors
All errors return JSON of the form {"error": "<message>"}.
| Status | Condition |
|---|---|
| 400 | Malformed request (bad JSON, invalid IP, missing fields) |
| 401 | Missing or revoked API key on endpoints that require one |
| 402 | Spend cap reached — set a higher cap or disable it in the dashboard |
| 404 | Unknown Stripe customer on /portal |
| 405 | Method not allowed (e.g. GET on /v1/batch) |
| 410 | Endpoint removed (e.g. legacy /signup — use /checkout) |
| 429 | Quota exceeded, per-IP rate limit hit, or spend cap reached — see Retry-After |
| 500 | Server error — transient, retry with backoff |
| 502 | Upstream (Stripe) failure |
GET/json/{ip}
Look up geolocation, ASN, organisation, datacenter and VPN detection for a specific IPv4 or IPv6 address.
curl https://api.ip-atlas.io/json/8.8.8.8
{
"ip": "8.8.8.8",
"country": "US",
"country_name": "United States",
"registry": "arin",
"asn": 15169,
"org": "GOOGLE",
"is_datacenter": true,
"is_vpn": false
}
GET/json
Look up the caller's own IP. Same response shape as /json/{ip}.
curl https://api.ip-atlas.io/json
POST/v1/batch
Look up up to 100 IPs in a single request. Requires an API key; each IP counts as one request against your quota.
curl -X POST https://api.ip-atlas.io/v1/batch \ -H "X-API-Key: ipa_live_..." \ -H "Content-Type: application/json" \ -d '{"ips":["8.8.8.8","1.1.1.1","not-an-ip"]}'
{
"results": [
{"ip":"8.8.8.8", "country":"US", ...},
{"ip":"1.1.1.1", "country":"AU", ...},
{"ip":"not-an-ip", "error":"invalid IP address"}
]
}
Results are returned in the same order as the input list. Each element is either a full lookup result or a {"ip": "...", "error": "..."} record for per-IP failures. Request-level failures (invalid key, over-quota, spend cap) return a normal 4xx/5xx, not inside the results array.
The entire batch is preflighted against your quota before any lookup runs.
GET/v1/account
Returns plan metadata, live usage, and billing settings for the key presented. Does not count against your quota.
curl https://api.ip-atlas.io/v1/account -H "X-API-Key: ipa_live_..."
{
"plan": "developer",
"key_prefix": "ipa_live_abc1",
"email": "[email protected]",
"status": "active",
"created_at": "2026-04-22T18:14:03Z",
"daily_used": 0, "daily_limit": 0,
"monthly_used": 14823, "monthly_limit": 2000000,
"stripe_customer_id": "cus_...", "has_subscription": true,
"overage_enabled": true,
"overage_rate_cents_per_million": 1000,
"overage_accrued_cents": 0,
"spend_cap_cents": null
}
POST/v1/account/rotate
Revokes the current key and issues a replacement with the same plan, email, and Stripe linkage. The new plaintext key is returned once.
curl -X POST https://api.ip-atlas.io/v1/account/rotate -H "X-API-Key: ipa_live_..."
{"api_key": "ipa_live_9f8e...", "key_prefix": "ipa_live_9f8e"}
POST/v1/account/settings
Update billing settings for your key. Fields not present are unchanged.
curl -X POST https://api.ip-atlas.io/v1/account/settings \ -H "X-API-Key: ipa_live_..." \ -H "Content-Type: application/json" \ -d '{"overage_enabled": false, "spend_cap_cents": 2500}'
Pass "clear_spend_cap": true to remove an existing cap.
GET/health
Unauthenticated liveness check. No rate limit.
curl https://api.ip-atlas.io/health
{"ok": true, "ips_loaded": 256046, "asns_loaded": 520536}
POST/checkout
Creates a Stripe Checkout session. Use plan: "free" for a Setup-mode checkout (card authorised, no charge). Paid plans run in Subscription mode. Most users hit this through the pricing page.
curl -X POST https://api.ip-atlas.io/checkout \ -H "Content-Type: application/json" \ -d '{"plan":"developer","email":"[email protected]"}'
POST/portal
Returns a URL into the Stripe billing portal — update card, view invoices, or cancel.
curl -X POST https://api.ip-atlas.io/portal \ -H "Content-Type: application/json" \ -d '{"customer_id":"cus_..."}'
Node.js SDK
The official Node.js SDK is available on npm as @trellisdigitalservices/ip-atlas.
npm install @trellisdigitalservices/ip-atlas
import { IPAtlas } from '@trellisdigitalservices/ip-atlas'; const client = new IPAtlas({ apiKey: 'ipa_live_...' }); const result = await client.lookup('8.8.8.8'); console.log(result.country, result.org);
Python SDK
Install the official Python SDK via pip:
pip install ipatlas
Usage:
from ipatlas import IPAtlas
client = IPAtlas(api_key="ipa_live_...")
result = client.lookup("8.8.8.8")
print(result["country"]) # "US"
curl
All you need is your API key in the X-API-Key header.
# Single lookup (anonymous) curl https://api.ip-atlas.io/json/8.8.8.8 # Single lookup with auth curl https://api.ip-atlas.io/json/8.8.8.8 \ -H "X-API-Key: ipa_live_..." # Batch with auth curl -X POST https://api.ip-atlas.io/v1/batch \ -H "X-API-Key: ipa_live_..." \ -H "Content-Type: application/json" \ -d '{"ips":["8.8.8.8","1.1.1.1"]}'
JavaScript / fetch
// Single lookup const res = await fetch('https://api.ip-atlas.io/json/8.8.8.8', { headers: { 'X-API-Key': 'ipa_live_...' } }); const data = await res.json(); // Batch lookup const batch = await fetch('https://api.ip-atlas.io/v1/batch', { method: 'POST', headers: { 'X-API-Key': 'ipa_live_...', 'Content-Type': 'application/json' }, body: JSON.stringify({ ips: ['8.8.8.8', '1.1.1.1'] }) }).then(r => r.json());
Python / requests
import requests KEY = "ipa_live_..." HEADERS = {"X-API-Key": KEY} # Single lookup data = requests.get("https://api.ip-atlas.io/json/8.8.8.8", headers=HEADERS).json() # Batch lookup batch = requests.post( "https://api.ip-atlas.io/v1/batch", headers=HEADERS, json={"ips": ["8.8.8.8", "1.1.1.1"]} ).json()
Go
req, _ := http.NewRequest("GET", "https://api.ip-atlas.io/json/8.8.8.8", nil) req.Header.Set("X-API-Key", "ipa_live_...") resp, err := http.DefaultClient.Do(req) defer resp.Body.Close() var result map[string]any json.NewDecoder(resp.Body).Decode(&result)
PHP
$ch = curl_init('https://api.ip-atlas.io/json/8.8.8.8'); curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ipa_live_...']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $data = json_decode(curl_exec($ch), true);
Response fields
| Field | Type | Description |
|---|---|---|
ip | string | The queried IP |
country | string | ISO 3166-1 alpha-2 country code |
country_name | string | Full English country name |
registry | string | RIR: arin, ripe, apnic, lacnic, afrinic (empty string for IPv6 addresses with no RIR mapping) |
asn | integer | Autonomous System Number |
org | string | Organization registered with the ASN |
is_datacenter | boolean | IP belongs to a known cloud / datacenter provider |
is_vpn | boolean | IP is associated with a known VPN service |
region | string | State/province name |
city | string | City name |
latitude | number | Latitude (city-level precision) |
longitude | number | Longitude (city-level precision) |
postal | string | Postal/ZIP code (may be empty) |
timezone | string | IANA timezone (may be empty) |
is_proxy | boolean | True if IP appears to be a proxy |
abuse_contact | string | Abuse contact email for the network |
All fields are included on every tier. No per-field upsells.
Note: postal and timezone return empty string when city-level data is unavailable. registry returns empty string for IPv6 addresses.
CORS
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, X-API-Key
Preflight (OPTIONS) returns 204 No Content.
Data sources
- RIR delegation files — ARIN, RIPE NCC, APNIC, LACNIC, AFRINIC
delegated-extended, refreshed daily at 06:00 UTC. - ASN registry — All five RIR databases cross-referenced for ASN-to-organisation mapping (520k+ ASNs). IPv6 ASN lookup uses the same ip2asn-combined dataset.
- City-level geolocation — DB-IP IP-to-City Lite (CC-BY-SA 4.0). Covers
region,city,postal,latitude,longitude, andtimezonefor IPv4 and IPv6. Updated monthly. Attribution: IP geolocation by DB-IP. - Datacenter detection — Known IP ranges for AWS, Google Cloud, Azure, Cloudflare, DigitalOcean, OVH, Hetzner, Vultr, Linode/Akamai and other major clouds.
- VPN detection (
is_vpn) — Conservative ASN-based heuristic. A curated list of ASNs operated by or exclusively leased to VPN providers (M247, Mullvad, Surfshark, PIA, CyberGhost, and others). Coverage improves over time; not exhaustive. - Proxy detection (
is_proxy) — Heuristic:truewhenis_vpn=true, or whenis_datacenter=trueand the ASN is not on the trusted-cloud allowlist (major public clouds are excluded). Not a real-time proxy database — false-negative rate is higher than commercial proxy detection products. - Abuse contacts (
abuse_contact) — Sourced from RIR RDAP for the most-trafficked ASNs. Unknown ASNs fall back to the RIR's generic abuse address ([email protected],[email protected], etc.).
Data is hot-swapped daily without a service restart (atomic directory swap + SIGHUP). City data is refreshed monthly with DB-IP Lite.