Guide

How to Detect VPN, Proxy & Tor Traffic with an IP Geolocation API

Flag anonymised and datacentre traffic from one lookup — VPN, Tor, and hosting networks — then challenge rather than hard-block.

You can flag anonymised traffic — VPN, Tor, and datacentre/hosting networks — from a single IP lookup. The response carries security.isVpn, security.isTor, a threat level, and the connection type. This guide combines them into a verdict you can act on — and the honest rule is to challenge, not hard-block, because plenty of legitimate users browse via VPN.

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

2Four fields reveal anonymised traffic:

Read security.isVpn and security.isTor (booleans), security.isThreat (a string level), and connection.connection_type — where Corporate flags cloud, hosting and datacentre networks (it also includes offices and corporate VPNs, so treat it as a hint, not a hard tell):

Python
data = lookup("185.220.101.1")   # a real Tor exit node
sec  = data.get("security") or {}
conn = data.get("connection") or {}

is_vpn = sec.get("isVpn")              # bool
is_tor = sec.get("isTor")              # bool  -> True here
threat = sec.get("isThreat")           # "low" | "medium" | "high"
ctype  = conn.get("connection_type")   # "Corporate" = cloud/hosting/business
print(is_vpn, is_tor, threat, ctype)   # False True medium Corporate

3Combine into a verdict — challenge, don't block:

Fold the flags into an action. Reserve a hard block for a known-bad combination (e.g. a high-threat Tor exit); otherwise challenge with MFA or a captcha. Legitimate customers use VPNs — let them prove themselves:

Python
#!/usr/bin/env python3
"""Detect VPN/proxy/Tor/datacentre traffic (IPWho IP geolocation API).
Challenge, don't hard-block — legitimate users use VPNs too."""
import os, sys, urllib.request, json

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

def anonymiser(data):
    sec  = data.get("security") or {}
    conn = data.get("connection") or {}
    flags = []
    if sec.get("isTor"): flags.append("tor")
    if sec.get("isVpn"): flags.append("vpn")
    if conn.get("connection_type") == "Corporate": flags.append("datacenter")

    threat = (sec.get("isThreat") or "low").lower()
    if "tor" in flags and threat == "high":
        verdict = "block"          # known-bad anonymiser
    elif flags:
        verdict = "challenge"      # step-up: MFA / captcha
    else:
        verdict = "allow"
    return {"flags": flags or ["clean"], "threat": threat, "verdict": verdict}

if __name__ == "__main__":
    ip = sys.argv[1] if len(sys.argv) > 1 else "185.220.101.1"
    print(f"{ip}: {anonymiser(lookup(ip))}")
Output
$ python3 anon.py 185.220.101.1    # Tor exit node
185.220.101.1: {'flags': ['tor', 'datacenter'], 'threat': 'medium', 'verdict': 'challenge'}

$ python3 anon.py 52.95.110.1      # AWS datacentre
52.95.110.1: {'flags': ['datacenter'], 'threat': 'low', 'verdict': 'challenge'}

$ python3 anon.py 76.102.11.203    # residential broadband
76.102.11.203: {'flags': ['clean'], 'threat': 'low', 'verdict': 'allow'}

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 threat-detection use case here.