Guide

How to Geo-Gate Content by IP (with Spoof Detection)

Enforce licensed regions and blackouts from one lookup — and catch VPN/Tor geo-spoofing in the same call.

You can enforce licensing regions and blackouts and catch the VPN/Tor spoofing used to evade them — from a single IP lookup. The response gives you country for the allow/deny decision and security.isVpn / isTor to flag an anonymiser pretending to be in-region. This guide combines both into one gate.

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"]

2Decide allow/deny by country, catch spoofing in the same call:

Compare geoLocation.countryCode against your licensed set, and reject the request if security.isVpn or security.isTor is set — an anonymiser claiming to be in-region is the classic way users evade a geo-block. This catches the common cases, not every one: a determined user on a residential proxy can still slip through, so pair this gate with account-level entitlements for high-value rights:

Python
LICENSED = {"US", "CA", "GB"}   # regions you hold rights for

def geo_gate(data):
    geo = data.get("geoLocation") or {}
    sec = data.get("security") or {}
    cc  = geo.get("countryCode")

    # An anonymiser in-region is the classic licensing-evasion move
    spoof = bool(sec.get("isVpn") or sec.get("isTor"))
    allowed = (cc in LICENSED) and not spoof
    if spoof:        reason = "vpn/tor geo-spoof"
    elif cc in LICENSED: reason = "licensed region"
    else:            reason = "outside licensed region"
    return {"country": cc, "allowed": allowed, "reason": reason}

print(geo_gate(lookup("76.102.11.203")))

3Enforce the gate — the full script:

Everything together: a region allow-list plus spoof detection in one lookup. Tune the LICENSED set to your rights, and remember IP geo is approximate — pair it with account-level entitlements for anything high-stakes:

Python
#!/usr/bin/env python3
"""Geo-gate content by IP with spoof detection (IPWho IP geolocation API)."""
import os, sys, urllib.request, json

API_KEY=*** LICENSED = {"US", "CA", "GB"}

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

def geo_gate(data):
    geo = data.get("geoLocation") or {}
    sec = data.get("security") or {}
    cc  = geo.get("countryCode")
    spoof = bool(sec.get("isVpn") or sec.get("isTor"))
    allowed = (cc in LICENSED) and not spoof
    if spoof:            reason = "vpn/tor geo-spoof"
    elif cc in LICENSED: reason = "licensed region"
    else:                reason = "outside licensed region"
    return {"country": cc, "allowed": allowed, "reason": reason}

if __name__ == "__main__":
    ip = sys.argv[1] if len(sys.argv) > 1 else "76.102.11.203"
    print(f"{ip}: {geo_gate(lookup(ip))}")
Output
$ python3 geogate.py 76.102.11.203   # USA, residential -> allowed
76.102.11.203: {'country': 'US', 'allowed': True, 'reason': 'licensed region'}

$ python3 geogate.py 91.0.0.1        # Germany -> outside licensed region
91.0.0.1: {'country': 'DE', 'allowed': False, 'reason': 'outside licensed region'}

$ python3 geogate.py 185.220.101.1   # Tor exit -> spoof blocked
185.220.101.1: {'country': 'DE', 'allowed': False, 'reason': 'vpn/tor geo-spoof'}

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 content geo-gating use case here.