Python SDK

ip-api.io Python SDK
pip install ip-api-io

ip-api-io is the official, zero-dependency client for Python — pure standard library, no requests, no extras. One client wraps IP geolocation, VPN/proxy/Tor detection, email validation, and fraud risk scoring, with typed exceptions and batch helpers, so you write the call, not the plumbing.

Location
Mountain View, US
Threat signals
No VPN or proxy
Dependencies
Zero — stdlib only
client.lookup("8.8.8.8")
{
"ip": "8.8.8.8",
"isp": "Google LLC",
"location": {
"country": "United States",
"city": "Mountain View",
"timezone": "America/Los_Angeles"
},
"suspicious_factors": {
"is_vpn": false,
"is_datacenter": true
}
}

What you'll build

  • Install and initialize the zero-dependency client
  • Look up any IP — or the caller's IP — with one call
  • Detect VPN, proxy, Tor, and threats; batch up to 100 IPs
  • Handle rate limits and errors the production-ready way
Trusted by thousands of businesses
Fast JSON API responses
Real-time validation
Simple integration, SDKs & examples

Prerequisites

  • Python 3.8 or higher
  • An ip-api.io API key — get one free
  • No other dependencies — the client is pure standard library
1

Install the package

Add the official ip-api-io package to your project. It has zero dependencies — nothing to pull in beyond the standard library. Installing into a virtual environment keeps your project isolated.

python -m venv .venv && source .venv/bin/activate
pip install ip-api-io
2

Initialize the client

Import IpApiClient and create one instance with your API key. Read the key from an environment variable — never hardcode it. The API rejects keyless requests with 401.

import os
from ipapi_io import IpApiClient

client = IpApiClient(api_key=os.environ["IP_API_IO_KEY"])

Reuse a single client across your application — it is safe to keep as a module-level singleton.

3

Look up an IP address

Call lookup(ip) for any IPv4 or IPv6 address, or lookup() with no argument to resolve the caller's own IP. Every response is a plain dict with a location object and a suspicious_factors object.

info = client.lookup("8.8.8.8")

print(info["ip"])                       # "8.8.8.8"
print(info["isp"])                      # "Google LLC"
print(info["location"]["country"])      # "United States"
print(info["location"]["city"])         # "Mountain View"
print(info["location"]["latitude"],
      info["location"]["longitude"])    # 37.4056 -122.0775
print(info["location"]["timezone"])     # "America/Los_Angeles"

# Resolve the caller's own IP (no argument):
me = client.lookup()
print(me["ip"], me["location"]["country"])
Autocompletion: methods return plain dicts, but ipapi_io.types ships TypedDict definitions such as IpInfo — annotate with info: IpInfo = client.lookup(...) and your editor autocompletes info["location"]["city"].
4

Detect VPN, proxy, Tor & batch lookups

The suspicious_factors dict carries seven boolean flags on every lookup — this is what sets ip-api.io apart from plain geolocation APIs. You get security intelligence in the same call, at no extra cost.

info = client.lookup("185.220.101.45")  # example Tor exit node
sf = info["suspicious_factors"]

if sf["is_vpn"] or sf["is_proxy"] or sf["is_tor_node"]:
    print("Anonymized traffic — consider a CAPTCHA or block")
if sf["is_datacenter"]:
    print("Cloud/datacenter IP — likely automated")
if sf["is_threat"]:
    print("Active threat signal — block this request")

is_risky = sf["is_vpn"] or sf["is_proxy"] or sf["is_tor_node"] or sf["is_threat"]

Need to check many IPs at once? lookup_batch takes up to 100 addresses in a single request (it raises ValueError if the list is empty or longer than 100):

batch = client.lookup_batch(["8.8.8.8", "1.1.1.1", "9.9.9.9"])

print(batch["total_processed"], batch["successful_lookups"])

for ip, result in batch["results"].items():
    print(ip, result["location"]["country"], result["suspicious_factors"]["is_vpn"])
Tip: want a single block / review / allow decision instead of raw flags? Use client.risk_score(ip) for a combined fraud risk score from 0 to 100.
5

Production-ready error handling

The client raises typed exceptions so you can react to each failure precisely. It never retries on its own — on a rate limit, RateLimitError tells you exactly when to try again.

from ipapi_io import (
    IpApiClient,
    IpApiError,
    RateLimitError,
    AuthenticationError,
    InvalidRequestError,
    ServerError,
)

client = IpApiClient(api_key=os.environ["IP_API_IO_KEY"])

try:
    info = client.lookup("8.8.8.8")
    # ... use info
except RateLimitError as e:
    # HTTP 429 — quota exhausted
    print(f"Rate limited. Remaining: {e.remaining}. Resets at {e.reset}")
except AuthenticationError:
    print("Invalid or missing API key — get one free at https://ip-api.io")
except InvalidRequestError as e:
    print("Bad request:", e)
except ServerError:
    print("ip-api.io is having issues — try again later")
except IpApiError as e:
    print(f"error {e.status_code}: {e}")
No automatic retries: wrap calls in your own retry/backoff logic and use RateLimitError.reset (a Unix timestamp) to schedule the next attempt instead of hammering the API. Transport failures (DNS, timeout) surface as urllib.error.URLError / socket.timeout, not an IpApiError.

In a Flask app, run the lookup server-side and pass the real client IP from the X-Forwarded-For header:

import os
from flask import Flask, request, jsonify
from ipapi_io import IpApiClient, IpApiError

app = Flask(__name__)
client = IpApiClient(api_key=os.environ["IP_API_IO_KEY"])

@app.get("/check")
def check():
    xff = request.headers.get("X-Forwarded-For", "")
    client_ip = xff.split(",")[0].strip() or request.remote_addr

    try:
        info = client.lookup(client_ip)
        return jsonify(
            ip=info["ip"],
            country=info["location"]["country"],
            is_vpn=info["suspicious_factors"]["is_vpn"],
            is_threat=info["suspicious_factors"]["is_threat"],
        )
    except IpApiError:
        # Treat ip-api.io as a non-critical dependency
        return jsonify(ip=client_ip, country=None), 200
Reference

Response & method reference

lookup() returns a plain dict (typed as IpInfo in ipapi_io.types) with three parts: the top-level ip/isp/asn, a suspicious_factors dict, and a location dict. Location fields are None for private ranges or unrecognized addresses.

client .lookup("78.55.53.58")
{
  "ip": "78.55.53.58",
  "isp": "Deutsche Telekom AG",
  "asn": "AS3320",
  "suspicious_factors": {
    "is_proxy":      false,
    "is_tor_node":   false,
    "is_spam":       false,
    "is_crawler":    false,
    "is_datacenter": false,
    "is_vpn":        false,
    "is_threat":     false
  },
  "location": {
    "country":           "Germany",
    "country_code":      "DE",
    "city":              "Berlin",
    "latitude":          52.5694,
    "longitude":         13.3753,
    "zip":               "13409",
    "timezone":          "Europe/Berlin",
    "local_time":        "2024-05-20T22:16:52+02:00",
    "local_time_unix":   1716236212,
    "is_daylight_savings": true
  }
}

suspicious_factors — security signals (all boolean)

FieldDescription
is_vpnVPN service, corporate gateway, or self-hosted VPN detected
is_proxyHTTP, HTTPS, or SOCKS proxy (~99.5% accuracy)
is_tor_nodeTor exit node, relay, or bridge (updated in real time)
is_datacenterCloud provider (AWS, GCP, Azure), VPS, or hosting facility
is_threatActive security threat — malware C&C, botnet, or DDoS source
is_spamAssociated with spam, phishing, or malware email campaigns
is_crawlerKnown web crawler, scraper, or bot

location — geographic and timezone data

FieldTypeDescription
countrystrFull country name in English (ISO 3166-1)
country_codestrISO 3166-1 alpha-2 code (e.g. "DE", "US")
citystrCity or municipality name (85–95% accuracy)
latitudefloatDecimal degrees, WGS84 — ~50 km median accuracy radius
longitudefloatDecimal degrees, WGS84 — ~50 km median accuracy radius
zipstrPostal or ZIP code in country-specific format
timezonestrIANA timezone identifier (e.g. "America/Los_Angeles")
local_timestrCurrent local time in ISO 8601 with UTC offset
local_time_unixintUnix timestamp (seconds) in local timezone
is_daylight_savingsboolTrue if currently observing DST; None if not applicable

Client methods

MethodReturnsWhat it does
lookup(ip=None)IpInfoGeolocate one IP, or the caller's IP if omitted
lookup_batch(ips)BatchIpLookupResponseGeolocate up to 100 IPs in one call
risk_score(ip=None)RiskScoreFraud risk score (0–100) for an IP
tor_check(ip)TorDetectionCheck whether an IP is a Tor node
email_info(email)EmailInfoBasic email validation (syntax, MX, disposable)
validate_email(email)AdvancedEmailValidationFull validation incl. SMTP deliverability
asn(ip)AsnLookupAutonomous System Number lookup
whois(domain)WhoisWHOIS domain registration info
domain_age(domain)DomainAgeDomain registration date and age in days
rate_limit()RateLimitInfoRemaining quota and plan limits
Full surface: the client also exposes validate_email_batch, email_risk_score, ip_reputation, reverse_dns, forward_dns, mx_records, domain_age_batch, and usage_summary. See the README and per-feature guides.
FAQ

Frequently asked questions

Common questions about the ip-api.io Python SDK.

Is the ip-api-io PyPI package official?

Yes. ip-api-io is the official Python client for ip-api.io, maintained by the ip-api.io team and published to PyPI from github.com/ip-api-io/ipapi-python. It is MIT licensed and not affiliated with ip-api.com or ipapi.com.

What is the PyPI package name for the ip-api.io SDK?

The package is named ip-api-io; the import is ipapi_io. Install and import it like this:

# pip install ip-api-io
from ipapi_io import IpApiClient
client = IpApiClient(api_key=os.environ["IP_API_IO_KEY"])

It has zero dependencies — pure standard library — so it works on any Python 3.8+ runtime.

Does the SDK return typed objects or plain dicts?

Every method returns parsed JSON as plain dicts, so you read fields like info["location"]["country"]. For editor autocompletion, ipapi_io.types ships TypedDict definitions (e.g. IpInfo, RiskScore) you can use as type hints.

Can I use the SDK in a browser or mobile app?

No. Calls should run server-side so your API key stays private — never ship the key to a client. Use the SDK in your Python backend (Django, Flask, FastAPI, a worker, or a script); your frontend calls your own endpoint, which uses the SDK to reach ip-api.io.

How do I handle rate limits?

On HTTP 429 the SDK raises a RateLimitError exposing limit, remaining, and reset parsed from the response headers. The client never retries automatically — catch the error and schedule your retry after the reset timestamp.

What's the difference between the SDK and using requests directly?

Both call the same ip-api.io API. The SDK adds a single client object, batch helpers, typed exceptions, and automatic URL encoding so you write less boilerplate — with no third-party dependencies. If you'd rather call the API with requests, follow the Python IP geolocation tutorial.

Pricing

Pricing

Start using IP-API.io to make your website safer and more user-friendly. Keep out unwanted bots, show visitors content that's relevant to where they are, and spot risky IP addresses quickly. It's perfect for making online shopping more personal and keeping your site secure. Get started today with one of the plans!

Small
€10 /mo
100,000 geo ip requests
10,000 advanced email validation requests
Location data
Email validation
Risk score calculation
Currency data
Time zone data
Threat data
Unlimited support
HTTPS encryption
Create Account to Subscribe
Medium
€20 /mo
300,000 geo ip requests
25,000 advanced email validation requests
Location data
Email validation
Risk score calculation
Currency data
Time zone data
Threat data
Unlimited support
HTTPS encryption
Create Account to Subscribe

Need support?

Explore how IP-API.io can enhance your security, provide robust bot protection, and improve IP geolocation accuracy for your applications.

Contact Support

Found a bug in the SDK?

The ip-api-io package is open source. Open an issue or pull request on GitHub and we'll take a look.

Open on GitHub