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

PlanDailyMonthlyNotes
Anonymous60 req/min per IP
Free2,00060,000Card on file required; non-commercial only
Developer2,000,000$19/mo
Startup10,000,000$79/mo
Business50,000,000$249/mo
EnterpriseCustom500M+/moContact sales

Every successful response includes advisory headers:

HeaderMeaning
X-RateLimit-Limit-DayDaily quota (only when enforced — Free tier)
X-RateLimit-Remaining-DayRemaining requests today
X-RateLimit-Limit-MonthMonthly quota
X-RateLimit-Remaining-MonthRemaining requests this calendar month
X-Overage-Charged-CentsUSD cents charged on this request (0 if within quota)
X-Spend-Cap-Reachedtrue 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:

PlanOverage
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:

Errors

All errors return JSON of the form {"error": "<message>"}.

StatusCondition
400Malformed request (bad JSON, invalid IP, missing fields)
401Missing or revoked API key on endpoints that require one
402Spend cap reached — set a higher cap or disable it in the dashboard
404Unknown Stripe customer on /portal
405Method not allowed (e.g. GET on /v1/batch)
410Endpoint removed (e.g. legacy /signup — use /checkout)
429Quota exceeded, per-IP rate limit hit, or spend cap reached — see Retry-After
500Server error — transient, retry with backoff
502Upstream (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

FieldTypeDescription
ipstringThe queried IP
countrystringISO 3166-1 alpha-2 country code
country_namestringFull English country name
registrystringRIR: arin, ripe, apnic, lacnic, afrinic (empty string for IPv6 addresses with no RIR mapping)
asnintegerAutonomous System Number
orgstringOrganization registered with the ASN
is_datacenterbooleanIP belongs to a known cloud / datacenter provider
is_vpnbooleanIP is associated with a known VPN service
regionstringState/province name
citystringCity name
latitudenumberLatitude (city-level precision)
longitudenumberLongitude (city-level precision)
postalstringPostal/ZIP code (may be empty)
timezonestringIANA timezone (may be empty)
is_proxybooleanTrue if IP appears to be a proxy
abuse_contactstringAbuse 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

Data is hot-swapped daily without a service restart (atomic directory swap + SIGHUP). City data is refreshed monthly with DB-IP Lite.