Prerequisites
- Python 3.7 or higher
- The
requestslibrary (installed below) - An ip-api.io API key — get one free
Install requests
Install the requests library if you don't have it already. Add it to your
requirements.txt for reproducible installs.
pip install requests # requirements.txt
requests>=2.28 Quickstart: current user IP
Call GET /api/v1/ip/ with no IP in the path. The API resolves the IP address
from the incoming request and returns its geolocation data.
Store your API key in an environment variable — never hardcode it.
import os
import requests
API_KEY = os.environ["IP_API_IO_KEY"]
resp = requests.get(
"https://ip-api.io/api/v1/ip/",
params={"api_key": API_KEY},
timeout=5,
)
resp.raise_for_status()
data = resp.json()
print(data["ip"]) # "78.55.53.58"
print(data["location"]["country"]) # "Germany"
print(data["location"]["city"]) # "Berlin"
print(data["location"]["latitude"]) # 52.5694
print(data["location"]["longitude"]) # 13.3753
print(data["location"]["timezone"]) # "Europe/Berlin"
print(data["location"]["local_time"]) # "2024-05-20T22:16:52+02:00"
/api/v1/ip/ from the
browser — you'll call it server-side and pass the client's IP from the
X-Forwarded-For header. See step 5 for a Flask/Django example.
Look up a specific IP address
To look up any IPv4 or IPv6 address, append it to the path:
GET /api/v1/ip/{ip}.
import os
import requests
API_KEY = os.environ["IP_API_IO_KEY"]
def lookup_ip(ip: str) -> dict:
resp = requests.get(
f"https://ip-api.io/api/v1/ip/{ip}",
params={"api_key": API_KEY},
timeout=5,
)
resp.raise_for_status()
return resp.json()
data = lookup_ip("8.8.8.8")
loc = data["location"]
print(f"{loc['city']}, {loc['country']}") # "Mountain View, United States"
print(f"Timezone: {loc['timezone']}") # "America/Los_Angeles"
print(f"Coords: {loc['latitude']}, {loc['longitude']}") Detect VPN, proxy, Tor, and threats
Every response includes a suspicious_factors object with seven boolean flags.
This is what sets ip-api.io apart from basic geolocation APIs — you get security
intelligence in the same call, at no extra cost per lookup.
data = lookup_ip("185.220.101.45") # example Tor exit node
sf = data["suspicious_factors"]
if sf["is_vpn"] or sf["is_proxy"] or sf["is_tor_node"]:
print("Anonymous or masked traffic — consider blocking or CAPTCHA")
if sf["is_datacenter"]:
print("Cloud/datacenter IP — likely automated or bot traffic")
if sf["is_threat"]:
print("Active threat signal — block this request")
if sf["is_spam"]:
print("Associated with spam or phishing activity")
# Combine signals for a risk decision
is_risky = any([
sf["is_vpn"],
sf["is_proxy"],
sf["is_tor_node"],
sf["is_threat"],
])
print(f"Risk flag: {is_risky}") # True
block /
review / allow recommendation, use the
Risk Score API at
/api/v1/risk-score/{ip}.
Production-ready helper
A complete helper function with error handling, plus a utility to extract the real
client IP from X-Forwarded-For headers in Flask, Django, or any
WSGI/ASGI framework.
import os
import requests
API_KEY = os.environ["IP_API_IO_KEY"]
_BASE = "https://ip-api.io/api/v1/ip"
def get_ip_intel(ip: str | None = None) -> dict:
"""Fetch geolocation and security signals for an IP address.
Pass ip=None to resolve the caller's IP automatically.
Raises RuntimeError on timeout or non-2xx response.
"""
url = f"{_BASE}/{ip}" if ip else f"{_BASE}/"
try:
r = requests.get(url, params={"api_key": API_KEY}, timeout=5)
r.raise_for_status()
return r.json()
except requests.Timeout:
raise RuntimeError("ip-api.io request timed out")
except requests.HTTPError as exc:
raise RuntimeError(f"ip-api.io HTTP {exc.response.status_code}") from exc
except requests.RequestException as exc:
raise RuntimeError("Could not connect to ip-api.io") from exc
def get_client_ip(request) -> str:
"""Extract the real client IP from a Flask or Django request object.
Handles reverse proxies that set X-Forwarded-For.
"""
xff = request.headers.get("X-Forwarded-For")
return xff.split(",")[0].strip() if xff else request.remote_addr
Using the helper in a Flask route:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/check")
def check_ip():
client_ip = get_client_ip(request)
data = get_ip_intel(client_ip)
sf = data["suspicious_factors"]
return jsonify({
"ip": data["ip"],
"country": data["location"]["country"],
"is_vpn": sf["is_vpn"],
"is_proxy": sf["is_proxy"],
"is_tor": sf["is_tor_node"],
"is_threat": sf["is_threat"],
})
Prefer async Python? Use httpx as a drop-in replacement for
requests:
import httpx
import os
API_KEY = os.environ["IP_API_IO_KEY"]
async def get_ip_intel_async(ip: str | None = None) -> dict:
url = f"https://ip-api.io/api/v1/ip/{ip}" if ip else "https://ip-api.io/api/v1/ip/"
async with httpx.AsyncClient(timeout=5) as client:
r = await client.get(url, params={"api_key": API_KEY})
r.raise_for_status()
return r.json()