How to Migrate from MaxMind GeoIP2 to IP Geo API in 2026: A Step-by-Step Drop-In Guide
7-minute read · 2026 code samples · honest rollback plan
This is the practical companion to the MaxMind alternative comparison → and the head-on review of MaxMind GeoIP2 vs IP Geo API →. Those two pages tell you whether to switch. This page tells you how — including the four steps no other migration guide is honest about.
TL;DR — most teams complete a
.mmdb→ REST migration in one engineering day. The hard work is not the code; it’s the field map, the cache layer, and a rollback path you actually trust.
Who this guide is for
You already use MaxMind GeoIP2 (paid) or GeoLite2 (free) via libmaxminddb or one of its language bindings, you’ve decided that the database-sync workflow costs more than it saves, and you want a REST API replacement that:
- Returns the same shape your code already consumes (no rewrite of downstream callers)
- Adds VPN / proxy / Tor flags without a second license
- Bills in EUR with a transparent tier ceiling
If those three boxes are unchecked — pause and read the vs comparison → first. The tradeoffs are real, especially around hot-path latency and air-gapped environments.
The 7-step migration checklist
- Inventory every call site that opens a
Reader/ queries an.mmdb. - Map your fields to the GeoIP2-compatibility response (
?format=geoip2). - Add a feature flag so you can switch any call site between MMDB and API.
- Wire a 60-second cache in front of the API client (in-memory or Redis).
- Deploy in shadow mode — read both, log differences, serve MMDB responses.
- Cut over gradually — 10% → 50% → 100% of traffic over 48 hours.
- Decommission the sync pipeline — delete the cron, remove the licence, archive the readers.
The rest of this post walks each step with copy-paste code.
Step 1 — Inventory call sites
Run this in the repo root before touching anything:
git grep -nE "GeoIP2|libmaxminddb|maxminddb|\.mmdb" -- ':!*.lock' ':!*.md'
Most teams find 3-12 call sites. Make a list. Note for each: language, library version, field paths consumed, and whether the result is cached.
Step 2 — Map the fields
IP Geo API ships a ?format=geoip2 compatibility shim that returns the most common GeoIP2 City fields under the same nested paths. The mapping for the fields ~95% of integrations rely on:
| Your old code | GeoIP2 path | IP Geo API ?format=geoip2 |
Native ?format=ipgeo |
|---|---|---|---|
| Country code | country.iso_code |
country.iso_code |
country |
| Country name | country.names.en |
country.names.en |
country_name |
| City | city.names.en |
city.names.en |
city |
| Subdivision | subdivisions[0].iso_code |
subdivisions[0].iso_code |
region |
| Postal | postal.code |
postal.code |
postal |
| Lat/lon | location.latitude / .longitude |
location.latitude / .longitude |
lat / lon |
| Time zone | location.time_zone |
location.time_zone |
timezone |
| ASN | traits.autonomous_system_number |
traits.autonomous_system_number |
asn.number |
| ASN org | traits.autonomous_system_organization |
traits.autonomous_system_organization |
asn.org |
| VPN / Tor / proxy | (Anonymous IP DB, separate licence) | traits.is_anonymous / is_tor_exit_node / is_anonymous_proxy |
threat.{vpn,tor,proxy} |
Fields the shim does not cover (documented gaps): registered_country distinction, ISP-only fields (e.g. traits.isp), confidence scores, and historical / point-in-time queries. If your code reads any of those, list them as blockers and decide per call site whether to drop the dependency or keep MaxMind for that path only (hybrid pattern — see the comparison page →).
Step 3 — Feature flag, then drop-in client
Python (was geoip2.database.Reader)
# before
import geoip2.database
reader = geoip2.database.Reader("/var/lib/geoip/GeoLite2-City.mmdb")
res = reader.city(ip)
country = res.country.iso_code
# after — drop-in via the geoip2-compatibility shim
import os, requests
from functools import lru_cache
API_KEY = os.environ["IPGEO_API_KEY"]
USE_IPGEO = os.environ.get("USE_IPGEO_API", "0") == "1" # feature flag
@lru_cache(maxsize=10_000)
def _lookup(ip: str) -> dict:
r = requests.get(
f"https://api.ipgeo.10b.app/v1/{ip}",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"format": "geoip2"},
timeout=2.0,
)
r.raise_for_status()
return r.json()
def lookup_country(ip: str) -> str:
if USE_IPGEO:
return _lookup(ip)["country"]["iso_code"]
return reader.city(ip).country.iso_code # legacy path
The lru_cache decorator gives you a free in-process cache; replace with Redis SETEX 60 for multi-pod deployments.
Node / TypeScript (was maxmind)
// before
import maxmind from "maxmind";
const lookup = await maxmind.open<CityResponse>("/var/lib/geoip/GeoLite2-City.mmdb");
const res = lookup.get(ip);
// after — drop-in
const cache = new Map<string, any>();
export async function geoLookup(ip: string) {
if (process.env.USE_IPGEO_API !== "1") return lookup.get(ip);
if (cache.has(ip)) return cache.get(ip);
const r = await fetch(`https://api.ipgeo.10b.app/v1/${ip}?format=geoip2`, {
headers: { Authorization: `Bearer ${process.env.IPGEO_API_KEY!}` },
});
if (!r.ok) throw new Error(`ipgeo ${r.status}`);
const j = await r.json();
cache.set(ip, j);
setTimeout(() => cache.delete(ip), 60_000); // 60-s TTL
return j;
}
Go (was github.com/oschwald/geoip2-golang)
// after — drop-in via the geoip2-compatibility shim
url := fmt.Sprintf("https://api.ipgeo.10b.app/v1/%s?format=geoip2", ip)
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("IPGEO_API_KEY"))
resp, err := httpClient.Do(req)
// ... unmarshal into your existing geoip2.City struct
Step 4 — Cache layer (the step everyone skips)
A naive 1-call-per-request integration will burn through paid-tier quotas in week one. Most production traffic is dominated by 1-5% of IPs (your bot crawler, your monitoring, your power users). A 60-second in-memory cache typically deflects 70-90% of calls at zero cost.
- Single-pod / serverless Lambda: Python
lru_cacheor aMapin Node, sized 10k entries. - Multi-pod web: Redis
SETEX <ip> 60 <json>or your existing distributed cache. - Edge / CDN: Cloudflare Workers KV with a 60-300 s TTL.
If you want strict cache-miss bounds, add a per-host concurrency limiter so only one in-flight call per IP is ever issued.
Step 5 — Shadow mode (the step that builds trust)
Before flipping any user-facing path: run both readers and compare.
def lookup_country(ip: str) -> str:
legacy = reader.city(ip).country.iso_code
if SHADOW_MODE:
try:
new = _lookup(ip)["country"]["iso_code"]
if new != legacy:
logger.warning("geoip-shadow-mismatch", extra={"ip": ip, "legacy": legacy, "new": new})
except Exception as e:
logger.error("geoip-shadow-error", extra={"ip": ip, "error": str(e)})
return legacy
Run shadow mode for 24-48 hours. The mismatch rate on country-level data is typically <0.5% (mostly recent IP-block reassignments where one source is fresher). City-level is 1-3%. ASN and threat flags are not directly comparable because MaxMind’s free GeoLite2 lacks threat data.
Step 6 — Gradual cutover
Once shadow logs are clean, flip a percentage of traffic via your feature-flag system (LaunchDarkly, Unleash, or a hashed-IP rollout):
import hashlib
def use_ipgeo(ip: str, percent: int) -> bool:
h = int(hashlib.md5(ip.encode()).hexdigest(), 16)
return (h % 100) < percent
Recommended schedule: 10% → 50% → 100% over 48 hours. Watch error rate, p99 latency, and downstream conversion / fraud-rate dashboards for any regression.
Step 7 — Decommission
After 7 days at 100% with no rollback events, you can:
- Delete the
geoipupdatecron (or whatever you used for sync). - Remove
libmaxminddb/ language reader libraries from build manifests. - Archive the licence key (do not delete — keep it dormant for at least 90 days as part of your rollback plan).
- Reclaim ~70-100 MB per pod / per build image.
That last bullet is often the surprise win: container builds get faster, cold starts shrink, CI cache hit-rates improve.
Rollback plan
Keep the legacy MaxMind reader path behind the same feature flag for a minimum of 30 days. If you see a regression at any percentage, you can flip back with one config change and zero deploy. After 30 days of stability, prune the dead code in a separate PR.
The 7 gotchas teams hit in week one
- No cache layer. Quota burn in 48 h. Add the cache before flipping the flag.
- Outbound HTTPS blocked. Production VPC egress rules deny
api.ipgeo.10b.app. Get firewall change scheduled before cutover. - Timezone parsing differences. GeoIP2 returns
Europe/Amsterdam; ensure your parser doesn’t fail on rarenulls. registered_countryconsumers. A handful of fraud rules read this distinct fromcountry; map both or document the gap.- Bot traffic spike. Your scrapers / synthetic monitoring suddenly count against paid quota. Allowlist them client-side or skip the lookup entirely.
- CI / test environments. Shared test IPs (
127.0.0.1,::1) return 422 from the API; mock them in test or short-circuit before the call. - GDPR DPIA refresh. Switching processor classes (US-jurisdictional MaxMind LLC → EU-only IP Geo API) usually triggers a one-page Article 30 update. Boring, but should be on the cutover checklist.
What you’ll see in week two
- 70-90% cache-hit rate if step 4 was done right.
- ~$50-200/yr / engineer-week saved in not maintaining the sync pipeline.
- VPN / Tor / proxy flags on every response — most teams find one new fraud rule worth shipping in the first month.
- Container images ~70 MB smaller per pod.
Pairing pages
- MaxMind alternative comparison → — full feature matrix, EU-residency claim, free-tier specs.
- IP Geo API vs MaxMind GeoIP2 in 2026 → — narrative companion: when MaxMind still wins.
- API reference → — endpoint, parameters, error codes (ships with paid tiers).
- Pricing → — EUR 0 / 29 / 99 tiers, no overage surprises.
FAQ
How long does a real migration take? For a single-stack web app with 3-6 call sites and a working CI: one engineering day end-to-end. Multi-stack monorepos or services with 12+ call sites: 2-4 days, mostly in shadow-mode tuning.
Will my MMDB-shaped tests still pass? Yes — if you’ve used dependency injection on the reader. The compatibility shim returns the same nested shape for the supported field set. For fields outside the shim, mock the new client path instead.
What about GeoLite2 (free) vs paid GeoIP2 callers? Same migration. The free tier of IP Geo API (1.000 req/day, no attribution required) is a cleaner replacement than GeoLite2 for low-volume internal tools.
What’s the rollback story if something goes wrong? The feature flag gives you a 1-second flip back to MaxMind. Keep the licence and the sync cron alive for at least 30 days post-cutover. Most teams keep them for 90 days for audit-trail reasons.
Can I migrate one service at a time? Yes — and it’s the recommended approach. Each call site is independent. Migrate the lowest-risk one first (often a dashboard analytics path), measure for a week, then move to the next. There is no all-or-nothing requirement.
Related migration & comparison reading
- How to Migrate from ipinfo.io to IP Geo API in 2026 — sibling migration guide for the dominant North-American REST incumbent
- How to Migrate from ipstack to IP Geo API in 2026 — sibling migration guide for the apilayer-hosted REST incumbent (HTTPS-on-free + bundled Security)
- How to Migrate from ipapi.co to IP Geo API in 2026 — sibling migration guide for the attribution-tied free-tier REST incumbent (
org-string concatenation + bundled threat flags) - How to Migrate from ipgeolocation.io to IP Geo API in 2026 — sibling migration guide for the bundled-endpoints REST incumbent (Security-API SKU consolidation,
apiKey-in-URL log-leak) - How to Migrate from IP2Location to IP Geo API in 2026 — sibling migration guide for the BIN/CSV/MMDB downloadable-database incumbent (IP2Proxy SKU consolidation, USD-annual-prepay-to-EUR-monthly billing migration)
- How to Migrate from DB-IP to IP Geo API in 2026 — sibling migration guide for the EU-headquartered (Brussels) MMDB-download incumbent (CC-BY-4.0 attribution scrub, IP-to-Threat / Anonymous / Datacenter SKU consolidation)
- IP Geo API vs MaxMind GeoIP2 in 2026 — narrative head-on with TCO math
- IP Geo API vs ipinfo.io in 2026 — head-on with the dominant North-American incumbent
- IP Geo API vs ipstack in 2026 — modern EU-hosted alternative
- IP Geo API vs ipapi.co in 2026 — pricing, throughput and threat-intel comparison
- IP Geo API vs ipgeolocation.io in 2026 — feature parity, GDPR posture, EUR billing
- IP Geo API vs IP2Location in 2026 — REST-first vs database-download
- IP Geo API vs DB-IP in 2026 — REST-first vs MMDB-download EU-vs-EU
Industry deep-dives
-
IP Geolocation for Fintech — KYC, Sanctions Screening, Fraud, and EU Residency → — fintech-specific deep-dive: the three IP-control surfaces (KYC country-of-origin, OFAC/EU sanctions, payment-fraud risk), EU-hosted GDPR posture, EUR billing, ASN-level hosting detection, and ≤40 ms median EU-edge latency for 800-1200 ms PSP authorisation budgets.
-
IP Geolocation for Ad-Tech — RTB Enrichment, SIVT/IVT Filtering, and Click-Fraud Attribution → — ad-tech-specific deep-dive: the three IP-control surfaces (RTB bid enrichment with ≤40 ms latency budget + OpenRTB 2.6 device.geo/device.ext, SIVT/IVT filtering with IAB-confirmed datacenter ASN block-list, click-fraud post-back attribution + risk scoring), EU-hosted GDPR + ePrivacy + IAB-TCF v2.2 posture, bundled threat fields, ASN-level granularity, and predictable EUR billing.
-
IP Geolocation for iGaming — Licence-Jurisdiction Enforcement, VPN-Circumvention Scoring, and Self-Exclusion Register Routing → — iGaming-specific deep-dive: the three IP-control surfaces (licence-jurisdiction enforcement with hard-fail-closed posture across MGA/UKGC/KSA/DGOJ/ANJ/ADM/DAS, anti-circumvention scoring with residential-proxy ASN block-list covering Bright Data + Oxylabs + Smartproxy + IPRoyal, self-exclusion register routing to GamStop/CRUKS/ROFUS/Spelpaus/OASIS by IP-country), EU-hosted GDPR + EGBA posture, bundled threat fields, ASN-level granularity, and predictable EUR billing.
-
IP Geolocation for SaaS Monetization — Geo-Pricing, EU-VAT/DAC7 Tax-Routing, Trial-Abuse Scoring, and OFAC/EAR Export-Controls → — SaaS-specific deep-dive: the four IP-control surfaces (PPP-anchored geo-pricing with ≤40 ms checkout-flow budget, EU-VAT-MOSS + OECD DAC7 tax-routing to the right Stripe/Adyen/Braintree/Paddle tax-id, trial-abuse detection with residential-proxy ASN block-list across Bright Data/Oxylabs/Smartproxy/IPRoyal, and OFAC SDN + EAR export-controls feature-gating), EU-hosted GDPR posture, bundled threat fields, ASN-level granularity, and predictable EUR billing.
-
IP Geolocation for Streaming Media — Content Licensing, VPN-Bypass Defence, CDN POP Steering, and SSAI Ad-Insertion → — Streaming-media-specific deep-dive: the four IP-control surfaces (per-territory licensing enforcement with hard-fail-closed HTTP 451 on ambiguous resolve, VPN/proxy/Tor circumvention defence with residential-proxy ASN block-list across Bright Data/Oxylabs/Smartproxy/IPRoyal, CDN POP steering and adaptive bitrate-ladder selection across Akamai/Cloudflare/Fastly/BunnyCDN/Lumen, and SSAI ad-insertion targeting with sports blackout windows via Haversine GPS-distance), ≤40 ms session-init budget on EU edges, studio-grade 24-month audit trail, threat fields on every plan, ASN-level granularity, and EU-hosted GDPR + AVMSD (Directive 2018/1808) posture.
-
IP Geolocation for E-commerce — Tax-Jurisdiction Routing, BIN-vs-IP Carding Defence, PPP-Adjusted Currency Display, and Shipping-Zone Fulfilment Routing → — E-commerce-specific deep-dive: the four IP-control surfaces (EU OSS distance-sales 27-rate map + UK VAT 20% + CH-VAT 7.7% + NO MVA 25% + US Wayfair 13-state nexus + CA GST/HST per-province + AU/SG/IN/BR/JP GST/ICMS/JCT with sanctions hard-stop on IR/KP/SY/CU/BY/RU/MM/VE at checkout; BIN-vs-IP carding + refund-fraud 6-factor weighted score at place-order with residential-proxy ASN block-list across Bright Data/Oxylabs/Smartproxy/IPRoyal/Tier3; PPP-adjusted 7-tier pricebook on first paint with VPN/proxy fall-back to BIN-billing-country; 9-warehouse fulfilment routing FRA/AMS/MAD/MIL/DOV/IAD/LAX/DEL/SIN with DDP/DDU duty pre-calc and lithium/aerosol/prescription destination-gates), ≤40 ms checkout-first-paint budget, DAC7/GDPR/EU OSS audit posture, bundled threat fields on every plan, ASN-level granularity, and EUR billing.
-
IP Geolocation for Healthcare — Cross-Border Telehealth Licensing, HIPAA PHI/EPHI Access Geofencing, EU Patient-Data Residency w/ Schrems II Routing, and Cross-Border Pharma + DEA Schedule Gating → — Healthcare-specific deep-dive: the four IP-control surfaces (cross-border telehealth licensure match at consult-init w/ US IMLC 41-state partial + CA/FL/NY/TX independent + EU MRPQ Directive 2005/36/EC + DE Bundesärztekammer + NL BIG + FR ONM + UK GMC + HTTP 451 hard-fail-closed on jurisdiction-mismatch + NO_RECIPROCITY hard-stop on IR/KP/SY/CU/BY/RU/MM/VE/AF/SO; HIPAA 45 CFR §164.308(a)(4) PHI/EPHI access geofencing w/ clinical-ASN allowlist Epic/Cerner/Allscripts/Mayo/MGH/Cleveland/Kaiser + residential-proxy ASN reject Bright Data/Oxylabs/Smartproxy/IPRoyal/Tier3 + home-office BAA-attested workstation allowlist + risk_score < 30 soft-allow; EU patient-data residency w/ GDPR Art. 9 special-category + EDPB Recommendations 01/2020 supplementary technical measures + Schrems II SCC flag for US-shard + routing to 6 EHR shards EU-FRA/EU-AMS/UK-LON/US-IAD/CA-YYZ/AU-SYD w/ VPN/proxy → fall-back to EU-FRA highest protection; cross-border pharma + controlled-substance gating w/ DEA Schedules I-V + Ryan Haight Act §3 in-person-eval requirement for telemed Rx + EU Falsified Medicines Directive 2011/62/EU originator-country audit + per-country bans for cannabis/CBD/psilocybin/MDMA/kratom), ≤40 ms consult-init budget, HIPAA/GDPR Art. 9/Schrems II/DEA/EU FMD audit posture, bundled threat fields on every plan, ASN-level granularity, and EUR billing.
-
IP Geolocation for Travel + Hospitality — Geo-Rate Enforcement + Dynamic-Pricing per Booking Origin, OTA Carding + ATO Defence, OFAC/EU CONSILIUM/UK OFSI Sanctions Screening at Booking-Init, and GDS + EU OSS / DAC7 Reporting → — Travel/hospitality-specific deep-dive: the four IP-control surfaces (geo-rate enforcement + dynamic-pricing per booking origin w/ 8-tier pricebook T1 EU-Lux 1.00x → T8 Africa 0.75x + VPN/proxy/Tor fall-back to T2_NA_LUX anti-arbitrage + SANCTIONS_HARDSTOP on IR/KP/SY/CU/BY/RU/MM/VE/AF/SO HTTP 451 at search-render + BIN-billing-country pin at checkout; OTA carding + ATO defence at booking checkout w/ corporate-travel-platform ASN allowlist AS-CWT/Amex GBT/BCD/FCM/Egencia/Navan/Amadeus/Sabre fast-lane + consumer-OTA reject on VPN/Tor/relay + residential-proxy ASN block Bright Data/Oxylabs/Smartproxy/IPRoyal/Tier3 + 6-factor carding score threshold ≥70; OFAC + EU CONSILIUM + UK OFSI sanctions screening at booking-init w/ sanctioned-origin hard-stop regardless of session residency + EU 6AMLD compelled-disclosure on VPN/proxy + US-Cuba 31 CFR §515 General License gate + luxury-segment AML thresholds yacht €10K / private jet €20K / villa €5K/night / heli €3K + PEP screen + source-of-funds eval; GDS + inventory routing + EU OSS / DAC7 reporting w/ Amadeus EU/UK + Sabre US/CA + Travelport APAC + 27 EU-MS destination-VAT rates DE 19% → HU 27% + NO 25% + CH 8.1% + UK 20% + DAC7 Directive 2021/514 reportable-platform-operator evidence-log 5-year retention + Jan-31 lead-MS annual report), ≤40 ms search-render budget, OFAC/EU CONSILIUM/UK OFSI/DAC7/EU OSS/HOTREC audit posture, bundled threat fields on every plan, ASN-level granularity, and EUR billing.
Last reviewed 2026-05-09 · IP Geo API team · Comments / corrections: hello@ipgeo.10b.app
Pairs with the full MaxMind alternative comparison page and the head-on IP Geo API vs MaxMind GeoIP2 review.
Get early access — 50% off for 12 months
First 100 signups lock in 50% off any paid plan for the first year. No credit card required — we’ll email you at launch.