Use cases

Five integration patterns with real code and the fields you'll care about. Start with the one closest to yours — the API contract is the same for all of them.

Fraud detection

The majority of trial abuse, stuffed carts, and bot signups come from three IP types: commercial VPNs, residential proxies, and datacenter hosts. Every IP-Atlas response flags them — on every plan.

The three signals that pull the most weight

FieldWhy it matters
is_vpnTrue if the IP is on a known commercial VPN ASN or range. High-signal for "user hiding location".
is_datacenterTrue if the IP is on AWS/GCP/Azure/DO/Hetzner/etc. A human shouldn't be signing up from a VPS.
asnThe network. Combine with your own allow/deny list of known hosting and proxy networks.

Pattern 1 — Hard block at signup

Reject signups from datacenter or VPN IPs outright. One HTTP call per signup; adds ~20 ms to the critical path.

// Node — signup middleware
import { IPAtlas } from '@trellisdigitalservices/ip-atlas';
const ipa = new IPAtlas({ apiKey: process.env.IPATLAS_KEY });

app.post('/signup', async (req, res) => {
  const ip = req.headers['cf-connecting-ip'] || req.ip;
  const r  = await ipa.lookup(ip);
  if (r.is_datacenter || r.is_vpn) {
    return res.status(403).json({ error: 'Please sign up from your normal network.' });
  }
  // ... proceed
});

This is the strictest setting. About 0.3% of real users will be caught (corporate VPNs, travellers on mobile hotspots). For anything where false positives matter, use Pattern 2.

Pattern 2 — Risk score, hand-off to step-up auth

Don't block — score, then route suspicious traffic into captcha / SMS verification.

function riskScore(ipData, signup) {
  let score = 0;
  if (ipData.is_datacenter) score += 60;
  if (ipData.is_vpn)        score += 40;
  if (signup.email.endsWith('+test@gmail.com')) score += 50;
  if (signup.country !== ipData.country) score += 20;
  return score; // >70 = step-up, >100 = block
}

Pattern 3 — Batch-enrich your signup log daily

Don't gate the request path on a third-party API. Log the raw IP, and every night run the day's signups through /v1/batch to flag post-hoc.

# Python — nightly enrichment
from ipatlas import IPAtlas
c = IPAtlas(api_key=os.environ["IPATLAS_KEY"])

for chunk in chunked(todays_signup_ips, 100):
    batch = c.lookup_batch(chunk)
    for r in batch["results"]:
        if r.get("is_datacenter") or r.get("is_vpn"):
            mark_for_review(r["ip"])

Geo-targeting & localisation

Every visitor hits your origin with an IP. Resolve it once at the edge, cache it, and ship the right currency, language, and legal banners before your React bundle finishes parsing.

Fields you need

FieldUse
countryCurrency, legal banners (GDPR, CCPA), regional pricing
country_nameHuman-readable "shipping to Germany"
regionUS state for sales-tax routing; EU region for VAT
timezone"Delivery at 3pm your time" instead of "3pm UTC"

Cloudflare Worker pattern (recommended)

At the CDN edge, look up the visitor IP once per session and cache the result in a signed cookie. Your origin never pays the RTT.

// Cloudflare Worker — geo-cookie
export default {
  async fetch(req, env) {
    const cookie = getCookie(req, 'ipa_geo');
    if (cookie) return fetch(req, { cf: { cookie } });

    const ip = req.headers.get('cf-connecting-ip');
    const r  = await fetch(`https://api.ip-atlas.io/json/${ip}`, {
      headers: { 'X-API-Key': env.IPATLAS_KEY }
    }).then(r => r.json());

    const res = await fetch(req);
    res.headers.append('Set-Cookie',
      `ipa_geo=${r.country}; Max-Age=86400; Path=/; Secure`);
    return res;
  }
};

Next.js middleware pattern

// middleware.ts
export async function middleware(req) {
  const ip = req.ip;
  const r  = await fetch(`https://api.ip-atlas.io/json/${ip}`, {
    headers: { 'X-API-Key': process.env.IPATLAS_KEY! }
  }).then(r => r.json());
  const res = NextResponse.next();
  res.cookies.set('country', r.country, { maxAge: 86400 });
  res.cookies.set('tz', r.timezone, { maxAge: 86400 });
  return res;
}

What NOT to do

  • Don't hit the API on every render. One call per session is enough. Cache the country in a cookie or KV store.
  • Don't geo-lock by browser language. Users in Berlin who set English as their browser language shouldn't see German pricing.
  • Don't rely on IP for identity. Geo-IP is probabilistic; use it for UX, not KYC.

Compliance & sanctions

Financial services, crypto, gaming, and export-controlled SaaS all need a country determination on every request — and a paper trail for audits.

What IP-Atlas gives you

FieldUse
countryISO 3166-1 alpha-2, RIR-sourced. Check against OFAC SDN, EU sanctions, UK HMT lists.
registryWhich RIR allocated the block. Useful for regional enforcement audits.
is_vpn, is_datacenterFlag attempts to bypass geo blocks via VPN or cloud relays.
abuse_contactFor automated takedown notices when you detect abuse.

OFAC-style block pattern

// Current US-OFAC comprehensive-sanctions list (2026)
const BLOCKED = ['IR', 'KP', 'SY', 'CU'];

app.use(async (req, res, next) => {
  const r = await ipa.lookup(req.ip);
  if (BLOCKED.includes(r.country)) {
    audit.log({ event: 'sanctions-block', ip: req.ip, country: r.country, ts: Date.now() });
    return res.status(451).send('Service not available in your jurisdiction.');
  }
  if (r.is_vpn) {
    // extra scrutiny: challenge with KYC step-up
  }
  next();
});

Audit considerations

  • Log every block. Store the IP, country, ASN, timestamp, and IP-Atlas response version. Auditors will ask.
  • Don't delete the log. Sanctions enforcement audits typically reach back 5 years.
  • Document your lists. OFAC changes designations; your block list should be source-controlled with clear changelogs.
  • Assume VPN bypass. A motivated user can use a commercial VPN. Combine IP with KYC for high-stakes decisions.
This page is engineering guidance, not legal advice. Your compliance team owns the list. We give you a reliable country determination and the signals to catch bypass attempts.

Analytics & attribution

Two options: batch-enrich via the API, or drop a monthly MMDB into your log pipeline and keep raw IPs out of your analyst's hands entirely.

Pattern A — Batch enrichment at ingest

Every hour, pull the last hour's IPs from your log ingest, chunk into 100-item batches, call /v1/batch, and join the results back onto the log line.

// Python — hourly enrichment
from ipatlas import IPAtlas
c = IPAtlas(api_key=os.environ["IPATLAS_KEY"])

for chunk in chunked(unique_ips_this_hour, 100):
    r = c.lookup_batch(chunk)
    for row in r["results"]:
        warehouse.upsert("ip_enrichment", row)

Volume: 1M unique IPs/day = 10,000 batch calls = ~30M requests/month. That exceeds the Pro plan's 150k/mo included quota — overage at $0.40/1k applies, or consider a dedicated pipeline with MMDB (no per-request cost).

Pattern B — MMDB at the log shipper

Pro subscribers get monthly MMDB snapshots. Ship them with your log shipper (Vector, Fluent Bit, Filebeat) and enrich inline. No outbound HTTP, no latency, no analyst ever touches the raw IP.

# Vector config — MMDB enrichment on every log event
[transforms.geo]
type    = "geoip"
inputs  = ["logs"]
database = "/etc/vector/ipatlas-2026-04.mmdb"
source   = "ip"
target   = "geo"

What enrichment unlocks

  • Country slicing. "MRR by country, monthly" without a country list.
  • ASN attribution. Detect corporate traffic ("all our traffic from AS8068 is Microsoft internal").
  • Bot cohorts. Segment analytics by is_datacenter to keep real user trends clean.
  • CDN-miss analysis. Correlate region with p99 latency to see where you need another PoP.

Privacy posture

GDPR, CCPA, and most state privacy laws treat IP addresses as personal data. The MMDB pattern lets you enrich once at the log shipper and strip the raw IP before the data reaches your warehouse. The country / ASN stays; the identifier doesn't.

Content delivery & edge routing

When you run multiple origins — US-East, EU-West, APAC — the client's country and ASN tell you which one to hit. IP-Atlas MMDB makes that lookup a memory read, not a network round trip.

Signals you'll use

FieldUse
countryContinental routing — US → us-east, CN → apac, etc.
asnShortcut for edge-network peerings — e.g. prefer GCP origin for Google ASN clients.
is_datacenterDon't treat a server-to-server call as a human session; route to API-origin, not CDN-origin.
latitude, longitudeNearest-origin selection when you have many PoPs.

Edge Worker routing pattern

// Cloudflare Worker — pick origin by country
const ORIGIN = {
  US: 'https://us-east.app.example.com',
  GB: 'https://eu-west.app.example.com',
  JP: 'https://apac.app.example.com',
  DEFAULT: 'https://us-east.app.example.com'
};

export default {
  async fetch(req, env) {
    const ip = req.headers.get('cf-connecting-ip');
    const r  = await env.IPATLAS.lookup(ip);  // KV-cached wrapper
    const target = ORIGIN[r.country] || ORIGIN.DEFAULT;
    return fetch(target + new URL(req.url).pathname, req);
  }
};

MMDB at your own Nginx

# nginx.conf — ngx_http_geoip2_module
load_module modules/ngx_http_geoip2_module.so;
geoip2 /etc/nginx/ipatlas-2026-04.mmdb {
  $geo_country country;
  $geo_asn     asn;
}
server {
  location / {
    proxy_pass https://origin-$geo_country.internal;
  }
}

Memory-mapped MMDB lookup is ~800 ns. On a box handling 50K RPS that's less than 0.1% CPU.

When NOT to geo-route

  • Stateful sessions. If the user's session lives on US-East, don't send them to EU-West just because they flew to Paris.
  • Data-residency requirements. GDPR may require EU data stays in the EU regardless of the user's current country.
  • Low-volume services. One origin is simpler. Don't add complexity unless latency demands it.

Start with a key. Upgrade as you need.