Official Python client for the ipwhois.io IP Geolocation API. Simple, dependency-free, supports single and bulk IP lookups.
Project description
ipwhois-python
Official, dependency-free Python client for the ipwhois.io IP Geolocation API.
- ✅ Single and bulk IP lookups (IPv4 and IPv6)
- ✅ Works with both the Free and Paid plans
- ✅ HTTPS by default
- ✅ Localisation, field selection, threat detection, rate info
- ✅ Never raises — all errors returned as
success: Falsedicts - ✅ No external dependencies — only the Python standard library
- ✅ Python 3.8+
Installation
pip install ipwhois-python
Free vs Paid plan
The same IPWhois class is used for both plans. The only difference is whether
you pass an API key:
- Free plan — create the client without arguments. No API key, no signup required. Suitable for low-traffic and non-commercial use.
- Paid plan — create the client with your API key from https://ipwhois.io. Higher limits, plus access to bulk lookups and threat-detection data.
from ipwhois import IPWhois
free = IPWhois() # Free plan — no API key
paid = IPWhois("YOUR_API_KEY") # Paid plan — with API key
Everything else (lookup(), options, error handling) is identical.
Quick start — Free plan (no API key)
from ipwhois import IPWhois
ipwhois = IPWhois() # no API key
info = ipwhois.lookup("8.8.8.8")
print(info["country"], info["flag"]["emoji"])
# → United States 🇺🇸
print(f"{info['city']}, {info['region']}")
# → Mountain View, California
Quick start — Paid plan (with API key)
Get an API key at https://ipwhois.io and pass it to the constructor:
from ipwhois import IPWhois
ipwhois = IPWhois("YOUR_API_KEY") # with API key
info = ipwhois.lookup("8.8.8.8")
print(info["country"], info["flag"]["emoji"])
# → United States 🇺🇸
print(f"{info['city']}, {info['region']}")
# → Mountain View, California
ℹ️ Pass nothing to look up your own public IP:
ipwhois.lookup()— works on both plans.
Lookup options
Every option below can be passed per call as a keyword argument, or set once on the client as a default.
| Option | Type | Plans needed | Description |
|---|---|---|---|
lang |
str | Free + Paid | One of: en, ru, de, es, pt-BR, fr, zh-CN, ja |
fields |
list | Free + Paid | Restrict the response to specific fields (e.g. ["country", "city"]) |
output |
str | Free + Paid | json (default), xml, csv |
rate |
bool | Basic and above | Include the rate block (limit, remaining) |
security |
bool | Business and above | Include the security block (proxy/vpn/tor/hosting) |
Setting defaults once
If you make many calls with the same options, set them once and forget:
# Free plan
ipwhois = (
IPWhois()
.set_language("en")
.set_fields(["country", "city", "flag.emoji"])
.set_timeout(8)
)
ipwhois.lookup("8.8.8.8") # uses all of the above
ipwhois.lookup("1.1.1.1", lang="de") # per-call options override defaults
# Paid plan
ipwhois = (
IPWhois("YOUR_API_KEY")
.set_language("en")
.set_fields(["country", "city", "flag.emoji"])
.set_timeout(8)
)
ipwhois.lookup("8.8.8.8") # uses all of the above
ipwhois.lookup("1.1.1.1", lang="de") # per-call options override defaults
ℹ️ Paid plans additionally support
set_security(True)(Business+) andset_rate(True)(Basic+). See the table above for what's available where.
HTTPS encryption
By default, all requests are sent over HTTPS. If you need to disable it (for
example, in environments without an up-to-date CA bundle), pass ssl=False
to the constructor:
from ipwhois import IPWhois
# Free plan
ipwhois = IPWhois(ssl=False)
from ipwhois import IPWhois
# Paid plan
ipwhois = IPWhois("YOUR_API_KEY", ssl=False)
ℹ️ HTTPS is strongly recommended for production traffic — your API key is sent in the query string and would otherwise travel in clear text.
Bulk lookup (Paid plan only)
The bulk endpoint sends up to 100 IPs in a single GET request. Each address counts as one credit. Available on the Business and Unlimited plans.
from ipwhois import IPWhois
ipwhois = IPWhois("YOUR_API_KEY")
results = ipwhois.bulk_lookup([
"8.8.8.8",
"1.1.1.1",
"208.67.222.222",
"2c0f:fb50:4003::", # IPv6 is fine — mix freely
])
for row in results:
if row.get("success") is False:
# Per-IP errors (e.g. "Invalid IP address") are returned inline,
# they do NOT raise — the rest of the batch is still usable.
print(f"skip {row['ip']}: {row['message']}")
continue
print(f"{row['ip']} → {row['country']}")
ℹ️ Bulk requires an API key. Calling
bulk_lookup()without one will fail at the API level.
Error handling
The library never raises. Every failure — invalid IP, bad API key, rate
limit, network outage, bad options — comes back inside the response dict
with success set to False and a message. Just check
info["success"] after every call:
info = ipwhois.lookup("8.8.8.8")
if not info["success"]:
print(f"Lookup failed: {info['message']}")
return
print(info["country"])
This means an outage of the ipwhois.io API (or of your machine's DNS, connection, etc.) will never surface as an unhandled exception in your application — you decide how to react.
Error response fields
Every error response contains success: False and a message. Some errors
include extra fields you can branch on:
| Field | When it's present |
|---|---|
error_type |
'network' or 'invalid_argument' — for non-API errors |
http_status |
On HTTP 4xx / 5xx responses |
retry_after |
On HTTP 429 if the API sent a Retry-After header |
import time
info = ipwhois.lookup("8.8.8.8")
if not info["success"]:
if info.get("http_status") == 429:
time.sleep(info.get("retry_after", 60))
# ...retry
if info.get("error_type") == "network":
# DNS failure, connection refused, timeout, ...
pass
print(f"Error: {info['message']}")
Response shape
A successful response includes (depending on your plan and selected options):
{
"ip": "8.8.4.4",
"success": true,
"type": "IPv4",
"continent": "North America",
"continent_code": "NA",
"country": "United States",
"country_code": "US",
"region": "California",
"region_code": "CA",
"city": "Mountain View",
"latitude": 37.3860517,
"longitude": -122.0838511,
"is_eu": false,
"postal": "94039",
"calling_code": "1",
"capital": "Washington D.C.",
"borders": "CA,MX",
"flag": {
"img": "https://cdn.ipwhois.io/flags/us.svg",
"emoji": "🇺🇸",
"emoji_unicode": "U+1F1FA U+1F1F8"
},
"connection": {
"asn": 15169,
"org": "Google LLC",
"isp": "Google LLC",
"domain": "google.com"
},
"timezone": {
"id": "America/Los_Angeles",
"abbr": "PDT",
"is_dst": true,
"offset": -25200,
"utc": "-07:00",
"current_time": "2026-05-08T14:31:48-07:00"
},
"currency": {
"name": "US Dollar",
"code": "USD",
"symbol": "$",
"plural": "US dollars",
"exchange_rate": 1
},
"security": {
"anonymous": false,
"proxy": false,
"vpn": false,
"tor": false,
"hosting": false
},
"rate": {
"limit": 250000,
"remaining": 50155
}
}
For the full field reference, see the official documentation.
An error response looks like:
{
"success": false,
"message": "Invalid IP address",
"http_status": 400 // present for HTTP 4xx / 5xx
// "retry_after": 60 // additionally present on HTTP 429 if the API sent a Retry-After header
// "error_type": "network" // present for non-API errors: 'network', 'invalid_argument'
}
Requirements
- Python 3.8 or newer
- No third-party dependencies — only the standard library (
urllib,json)
Contributing
Issues and pull requests are welcome on GitHub.
License
MIT © ipwhois.io
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ipwhois_python-1.0.1.tar.gz.
File metadata
- Download URL: ipwhois_python-1.0.1.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b70cd5d12fae69b0023ba015ee9ffd24cdb956bb6906eb5626c90e3efdecba0
|
|
| MD5 |
4a50cfc723d1012069941f1529520763
|
|
| BLAKE2b-256 |
b6de1e2e82e1ebb9d2f594ad3fde11b8e62eb55efd21ae397094b32982e83e9b
|
File details
Details for the file ipwhois_python-1.0.1-py3-none-any.whl.
File metadata
- Download URL: ipwhois_python-1.0.1-py3-none-any.whl
- Upload date:
- Size: 12.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be3dee7174d521ee38eb5bb6828145d045747fdbe2ecf1d3f364b459f07e70b4
|
|
| MD5 |
6944868b5156fd6c7e67be338425503a
|
|
| BLAKE2b-256 |
75e7a990753ecad7fac283df0e45b4c405a31bb91d076a6ff0556a5356790ec6
|