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
Release files for ipgeolocationio 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ipgeolocationio-2.0.0.tar.gz | 41.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ipgeolocationio-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.1 kB
Release files / ipgeolocationio-2.0.0.tar.gz
| Download URL | ipgeolocationio-2.0.0.tar.gz |
|---|---|
| Size | 41.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
708a9da1aed3fd24e1cb47fdc183c6090b820638654817f069a85c1dc8b8c1a8
|
|
BLAKE2b-256 checksum How to use checksums |
3e3c3d27e100647c9efdd0eeb1703a2c4e806634f1f26dd06f0bc6c9be95bf36
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ipgeolocationio-2.0.0-py3-none-any.whl
| Download URL | ipgeolocationio-2.0.0-py3-none-any.whl |
|---|---|
| Size | 26.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
48938b3d79dc63ee3d7e699b2be134bf8f7cf9e1d127aa5e1f11f64881e09978
|
|
BLAKE2b-256 checksum How to use checksums |
87558b533c5ef36b4dbc5563d12c1e81b26ff52fdbbab1abc86dafa7e2f77c1a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|