Guide

How to Detect Jurisdiction & Compliance Context from an IP

Resolve country, state/province and EU membership from one lookup — to drive GDPR notices, VAT, sales tax and regional policy before the page renders.

You can resolve a visitor's jurisdiction context — country, state or province, and EU membership — from a single IP lookup, and use it to pre-apply the right GDPR notice, VAT or sales tax, and regional policy before the page renders. The response carries countryCode, regionCode and is_in_eu directly. These are signals, not legal determinations — always pair IP with stated billing or residency for anything binding.

Python

The examples here are Python, standard library only — but the same logic maps directly onto our official SDKs for TypeScript, Go, Ruby, Java and 7 more. Field paths and the worked outputs below are all verified against the live API.

1Get a key and call the API securely

Create a free account here — 2,000 lookups/day, no card. Send the key as an Authorization: Bearer header from an environment variable, keeping it out of URLs and logs:

Python
import os, urllib.request, json

API_KEY=***  # export it; never hardcode

def lookup(ip):
    req = urllib.request.Request(
        f"https://api.ipwho.org/ip/{ip}",
        headers={"Authorization": f"Bearer {API_KEY}", "User-Agent": "MyApp/1.0"},
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        return json.loads(resp.read())["data"]

2Read country, state/province and EU status:

The response carries geoLocation.countryCode, geoLocation.regionCode (state or province), and geoLocation.is_in_eu. Country and EU status cover GDPR and country-level VAT — but a lot of law is sub-national: US sales tax, Australian state rules, Canadian provincial tax all turn on the region. Read regionCode too, and remember it can be null on some IPs, so keep a country-level fallback:

Python
data = lookup("76.102.11.203")
geo = data.get("geoLocation") or {}

country = geo.get("countryCode")     # "US"
region  = geo.get("regionCode")      # "CA"  -> state/province (can be None)
in_eu   = bool(geo.get("is_in_eu"))  # False
print(country, region, in_eu)        # US CA False

3Map jurisdiction to policy — signal, not verdict:

Use country + region + EU status to pre-apply a GDPR banner, the right VAT or state sales tax, and regional rules. Treat IP as a hint: for anything binding — tax invoices, KYC, age-gating — confirm against the user's stated billing or residency, because IP location is approximate, region can be missing, and VPNs exist.

Python
#!/usr/bin/env python3
"""Detect jurisdiction/compliance context from an IP (IPWho IP geolocation API).
Signals only — not legal advice; confirm with billing/residency for binding use."""
import os, sys, urllib.request, json

API_KEY=*** EU_VAT = {"DE": 0.19, "ES": 0.21, "FR": 0.20}        # illustrative country VAT
US_STATE_TAX = {"CA": 0.0725, "NY": 0.04}             # illustrative state sales tax

def lookup(ip):
    req = urllib.request.Request(
        f"https://api.ipwho.org/ip/{ip}",
        headers={"Authorization": f"Bearer {API_KEY}", "User-Agent": "IPWho-Compliance/1.0"},
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        return json.loads(resp.read())["data"]

def compliance_context(data):
    geo = data.get("geoLocation") or {}
    cc = geo.get("countryCode")
    region = geo.get("regionCode")   # state / province — many laws are sub-national
    in_eu = bool(geo.get("is_in_eu"))
    return {
        "country": cc,
        "region": region,
        "gdpr_applies": in_eu,
        "vat_rate": EU_VAT.get(cc),
        "us_state_tax": US_STATE_TAX.get(region) if cc == "US" else None,
    }

if __name__ == "__main__":
    ip = sys.argv[1] if len(sys.argv) > 1 else "76.102.11.203"
    print(f"{ip}: {compliance_context(lookup(ip))}")
Output
$ python3 compliance.py 91.0.0.1       # Germany (EU)
91.0.0.1: {'country': 'DE', 'region': 'NW', 'gdpr_applies': True, 'vat_rate': 0.19, 'us_state_tax': None}

$ python3 compliance.py 76.102.11.203  # California, USA
76.102.11.203: {'country': 'US', 'region': 'CA', 'gdpr_applies': False, 'vat_rate': None, 'us_state_tax': 0.0725}

$ python3 compliance.py 24.48.0.1      # Quebec, Canada
24.48.0.1: {'country': 'CA', 'region': 'QC', 'gdpr_applies': False, 'vat_rate': None, 'us_state_tax': None}

Start with a free API key

Free tier: 2,000 lookups/day, no card. Paid plans from $2.50/month. Full field reference here, or see the compliance use case here.