Typed Python SDK for the IPGeolocation.io IP location API, with sync and async clients.
Project description
IPGeolocation Python SDK
Typed Python SDK for the IPGeolocation.io IP Location API.
- Single lookup (
/v3/ipgeo) and bulk lookup (/v3/ipgeo-bulk) - Sync client built on
requestsand async client built onhttpx - Typed response models plus raw JSON/XML methods
- Python 3.8+
Contents
- Install
- Quick Start
- Authentication
- Notes
- Plan Behavior
- Examples
- Request Options
- Response Metadata
- Errors
- Links
- Development
Install
pip install ipgeolocationio
PyPI package: ipgeolocationio
Package page: https://pypi.org/project/ipgeolocationio/
Import: ipgeolocation
Quick Start
from ipgeolocation import (
IpGeolocationClient,
IpGeolocationClientConfig,
LookupIpGeolocationRequest,
)
config = IpGeolocationClientConfig(api_key="YOUR_API_KEY")
with IpGeolocationClient(config) as client:
response = client.lookup_ip_geolocation(
LookupIpGeolocationRequest(ip="8.8.8.8")
)
print(response.data.ip)
if response.data.location is not None:
print(response.data.location.country_name)
print(response.data.location.city)
if response.data.time_zone is not None:
print(response.data.time_zone.name)
Async
import asyncio
from ipgeolocation import (
AsyncIpGeolocationClient,
IpGeolocationClientConfig,
LookupIpGeolocationRequest,
)
async def main() -> None:
config = IpGeolocationClientConfig(api_key="YOUR_API_KEY")
async with AsyncIpGeolocationClient(config) as client:
response = await client.lookup_ip_geolocation(
LookupIpGeolocationRequest(ip="8.8.8.8")
)
print(response.data.ip)
if response.data.location is not None:
print(response.data.location.country_name)
asyncio.run(main())
The async client exposes the same methods as coroutines. Use async with for cleanup, or call await client.aclose() manually.
Authentication
API key: Works on all plans for single lookup and bulk lookup.
config = IpGeolocationClientConfig(api_key="YOUR_API_KEY")
Request-origin auth: Paid plans only, and only for single lookup. If your origin is allowlisted in the IPGeolocation dashboard, the SDK sends it in the Origin header.
config = IpGeolocationClientConfig(request_origin="https://app.example.com")
request_origin must be an absolute http or https URL with no path, query string, or fragment. Bulk lookups always require api_key, even if request_origin is set. You can set both on the same config. Single lookups require at least one of them.
Client Config
| Field | Use |
|---|---|
api_key |
API key auth for single lookup and bulk lookup |
request_origin |
Paid request-origin auth for single lookup |
base_url |
Override the API base URL |
connect_timeout |
Time to wait for the connection in seconds |
read_timeout |
Time to wait for the response body in seconds |
Notes
- Typed methods require JSON. For XML output, use the
_rawmethods withoutput=ResponseFormat.XML. - Optional modules need
include. Fields likesecurity,abuse,user_agent,hostname,geo_accuracy, anddma_codeonly appear in the response when you pass the matchingincludevalue. Without it, those fields will beNone. fieldsandexcludesonly filter the response. They do not enable optional modules or unlock paid data.- Domain lookup is paid only. Passing a domain as
ip="google.com"works on paid plans. Free plans get a 401. - The SDK does not retry. Timeouts, server errors, and rate limits raise exceptions directly. Implement your own retry logic if you need it.
- Do not reuse a closed client. After
close(),aclose(), or leaving awithorasync withblock, further requests raiseValidationException.
Plan Behavior
Responses vary by plan.
Capabilities
| Capability | Free | Paid |
|---|---|---|
| Single IPv4/IPv6 lookup | Yes | Yes |
| Domain lookup | No | Yes |
Bulk lookup (/v3/ipgeo-bulk) |
No | Yes |
Non-English lang |
No | Yes |
| Request-origin auth | No | Yes |
Optional modules via include |
No | Yes |
include=["*"] |
Base response only | All plan-available modules |
Default Response Sections
Default single-lookup response:
| Section | Free | Paid |
|---|---|---|
location |
Yes | Yes |
country_metadata |
Yes | Yes |
currency |
Yes | Yes |
asn (basic: as_number, organization, country) |
Yes | Yes |
asn (full: adds type, domain, date_allocated, rir) |
No | Yes |
time_zone |
Yes | Yes |
network |
No | Yes |
company |
No | Yes |
Examples
The examples below assume you already have a configured client in scope. See Quick Start for setup.
Caller IP
Omit the ip parameter to look up the IP of the machine making the request:
response = client.lookup_ip_geolocation(LookupIpGeolocationRequest())
print(response.data.ip) # your public IP
Domain Lookup (Paid)
When you look up a domain, the response includes the resolved IP and the original domain name:
response = client.lookup_ip_geolocation(
LookupIpGeolocationRequest(ip="google.com")
)
print(response.data.ip) # resolved IP address
print(response.data.domain) # "google.com"
Security and Abuse
response = client.lookup_ip_geolocation(
LookupIpGeolocationRequest(
ip="9.9.9.9",
include=["security", "abuse"],
)
)
if response.data.security is not None:
print(response.data.security.threat_score)
if response.data.abuse is not None and response.data.abuse.emails:
print(response.data.abuse.emails[0])
User-Agent Parsing
To parse a visitor's user-agent string, pass include=["user_agent"] and send the visitor string in the request User-Agent header:
visitor_ua = (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) "
"AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9"
)
response = client.lookup_ip_geolocation(
LookupIpGeolocationRequest(
ip="115.240.90.163",
include=["user_agent"],
headers={"User-Agent": visitor_ua},
)
)
if response.data.user_agent is not None:
print(response.data.user_agent.name)
print(response.data.user_agent.operating_system)
Note: The
user_agentfield on the request model is different. It overrides the outboundUser-Agentheader for the HTTP request itself and takes precedence overheaders["User-Agent"]. If you set both, the API parses theuser_agentvalue.
Filtered Response
Use fields to keep specific fields, or excludes to remove them:
response = client.lookup_ip_geolocation(
LookupIpGeolocationRequest(
ip="8.8.8.8",
include=["security"],
fields=["location.country_name", "security.threat_score", "security.is_vpn"],
excludes=["currency"],
)
)
if response.data.location is not None:
print(response.data.location.country_name)
if response.data.security is not None:
print(response.data.security.threat_score)
print(response.data.security.is_vpn)
Raw XML
Typed methods only support JSON. For XML, use a raw method:
from ipgeolocation import ResponseFormat
response = client.lookup_ip_geolocation_raw(
LookupIpGeolocationRequest(ip="8.8.8.8", output=ResponseFormat.XML)
)
print(response.data) # raw XML string
Bulk Lookup (Paid)
Bulk lookup uses POST and accepts up to 50,000 IPs or domains. Each item in the response is either BulkLookupSuccess or BulkLookupError:
from ipgeolocation import (
BulkLookupError,
BulkLookupIpGeolocationRequest,
BulkLookupSuccess,
)
response = client.bulk_lookup_ip_geolocation(
BulkLookupIpGeolocationRequest(
ips=["8.8.8.8", "invalid-ip", "1.1.1.1"],
include=["security"],
)
)
for result in response.data:
if isinstance(result, BulkLookupSuccess):
print(result.data.ip, result.data.security) # 8.8.8.8 Security(...)
elif isinstance(result, BulkLookupError):
print(result.error.message) # 'invalid-ip' is not a valid IP address.
Request Options
Single Lookup
| Field | Type | Notes |
|---|---|---|
ip |
str or None |
IPv4, IPv6, or domain (domain is paid only). Omit for caller IP lookup. |
lang |
Language, str, or None |
Response language. Non-English requires a paid plan. |
include |
sequence of strings | Optional modules to enable. See include values. |
fields |
sequence of strings | Response field filter. Does not unlock data. |
excludes |
sequence of strings | Response field filter. Does not unlock data. |
output |
ResponseFormat or str |
"json" (default) or "xml". Typed methods require JSON. |
user_agent |
str or None |
Overrides the outbound User-Agent header. Takes precedence over headers["User-Agent"]. |
headers |
mapping of strings | Custom request headers. Accept is always SDK-managed. Sequence values are sent as one comma-joined header line. |
Bulk Lookup
Bulk lookup accepts the same fields as single lookup, plus:
| Field | Type | Notes |
|---|---|---|
ips |
sequence of strings | Required. Up to 50,000 IPs or domains. |
Differences from single lookup: Content-Type: application/json is always set by the SDK. Bulk lookup always requires api_key in the config, even if request_origin is also set.
include Values
| Value | What it adds |
|---|---|
security |
security object (threat score, VPN/proxy/Tor detection, bot detection) |
abuse |
abuse object (abuse contact info, emails, phone numbers) |
user_agent |
user_agent object (browser, device, OS parsed from the request User-Agent header) |
hostname |
hostname field (reverse DNS lookup) |
liveHostname |
hostname field (live DNS) |
hostnameFallbackLive |
hostname field (fallback to live DNS) |
geo_accuracy |
location.locality, location.accuracy_radius, location.confidence |
dma_code |
location.dma_code |
* |
All optional modules available on your plan |
Supported lang values: en, de, ru, ja, fr, cn, es, cs, it, ko, fa, pt
Response Metadata
Every SDK method returns ApiResponse(data=..., metadata=...).
- Typed single lookup returns
IpGeolocationResponse - Typed bulk lookup returns a list of
BulkLookupResult - Raw methods return the response body as a string
| Field | Type | Description |
|---|---|---|
status_code |
int |
HTTP status code |
duration_ms |
int |
Wall-clock request time in milliseconds, measured by the SDK |
credits_charged |
int or None |
Parsed from the X-Credits-Charged response header |
successful_records |
int or None |
Parsed from the X-Successful-Record response header |
raw_headers |
read-only mapping | All response headers. Values are tuples of strings. |
Header access helpers:
metadata.header_values("Header-Name") # all values as a tuple
metadata.first_header_value("Header-Name") # first value or None
JSON Serialization
Use to_json() or to_pretty_json() to serialize SDK objects:
from ipgeolocation import JsonOutputMode, to_pretty_json
# Compact mode (default): omits None fields
print(to_pretty_json(response.data))
# Full mode: includes None fields as null
print(to_pretty_json(response.data, mode=JsonOutputMode.FULL))
Errors
All exceptions inherit from IpGeolocationException. The SDK does not retry failed requests.
Before the request is sent:
ValidationExceptionfor bad config, invalid request parameters, or calling a closed client
Transport problems:
RequestTimeoutExceptionfor timeoutsTransportExceptionfor connection or other HTTP-level failures
API errors (non-2xx responses):
| Exception | HTTP Status |
|---|---|
BadRequestException |
400 |
UnauthorizedException |
401 |
NotFoundException |
404 |
MethodNotAllowedException |
405 |
PayloadTooLargeException |
413 |
UnsupportedMediaTypeException |
415 |
LockedException |
423 |
RateLimitException |
429 |
ClientClosedRequestException |
499 |
ServerErrorException |
5xx |
ApiException |
Any other non-2xx |
All API exceptions have .status_code and .api_message attributes.
Links
- PyPI package
- Python SDK documentation
- API documentation
- Authentication
- Response formats
- Credits and usage
- GitHub repository
Development
Clone and run local checks
git clone https://github.com/IPGeolocation/ip-geolocation-api-python-sdk.git
cd ip-geolocation-api-python-sdk
python -m venv .venv
./.venv/bin/pip install -e .[dev]
./.venv/bin/pytest
./.venv/bin/ruff check
./.venv/bin/mypy src
./.venv/bin/pyright
IPGEO_RUN_LIVE_TESTS=true \
IPGEO_FREE_KEY=YOUR_FREE_KEY \
IPGEO_PAID_KEY=YOUR_PAID_KEY \
./.venv/bin/pytest tests/test_live_integration.py tests/test_live_async_integration.py
IPGEO_RUN_LIVE_HARDENING=true \
IPGEO_PAID_KEY=YOUR_PAID_KEY \
./.venv/bin/pytest tests/test_live_field_parity.py tests/test_live_async_field_parity.py
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 ipgeolocationio-2.0.0.tar.gz.
File metadata
- Download URL: ipgeolocationio-2.0.0.tar.gz
- Upload date:
- Size: 41.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
708a9da1aed3fd24e1cb47fdc183c6090b820638654817f069a85c1dc8b8c1a8
|
|
| MD5 |
306ad1018b14d89bc3e230f4aabab0ab
|
|
| BLAKE2b-256 |
3e3c3d27e100647c9efdd0eeb1703a2c4e806634f1f26dd06f0bc6c9be95bf36
|
File details
Details for the file ipgeolocationio-2.0.0-py3-none-any.whl.
File metadata
- Download URL: ipgeolocationio-2.0.0-py3-none-any.whl
- Upload date:
- Size: 26.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48938b3d79dc63ee3d7e699b2be134bf8f7cf9e1d127aa5e1f11f64881e09978
|
|
| MD5 |
2af729e1d178620b5a2b4051e36f47c3
|
|
| BLAKE2b-256 |
87558b533c5ef36b4dbc5563d12c1e81b26ff52fdbbab1abc86dafa7e2f77c1a
|