Typed Python SDK for the IPGeolocation.io IP Geolocation 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.10+
- Install
- Quick Start
- Authentication
- Notes
- Plan Behavior
- Examples
- Request Options
- Response Metadata
- Errors
- Links
- Development
pip install ipgeolocationioPyPI package: ipgeolocationio
Package page: https://pypi.org/project/ipgeolocationio/
Import: ipgeolocation
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)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.
API key: API key authentication supports single lookups on all plans. Bulk lookup requires a paid plan.
config = IpGeolocationClientConfig(api_key="YOUR_API_KEY")| Field | Use |
|---|---|
api_key |
API key auth for single lookup and bulk lookup |
base_url |
Use the final address of your API or proxy. The standard client follows redirects only if the protocol, host and port stay the same. The async client stops at redirects by default. |
connect_timeout |
Time to wait for the connection in seconds |
read_timeout |
Time to wait for the response body in seconds |
- 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.
Responses vary by plan.
| 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 |
Optional modules via include |
No | Yes |
include=["*"] |
Base response only | All plan-available modules |
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 |
The examples below assume you already have a configured client in scope. See Quick Start for setup.
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 IPWhen 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"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])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.
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)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 stringBulk 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.| 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 accepts the same fields as single lookup, plus:
| Field | Type | Notes |
|---|---|---|
ips |
sequence of strings | Required. Up to 50,000 IPs or domains. |
Set api_key in your client settings for bulk lookups. The SDK sets Content-Type: application/json for you.
| 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, ar (Arabic)
Every SDK method returns ApiResponse(data=..., metadata=...).
- Typed single lookup returns
IpGeolocationResponse - A typed bulk lookup returns one
BulkLookupResultper input. If the API sends too few or too many results, the SDK raisesSerializationException. - 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 NoneUse to_json() or to_pretty_json() to print SDK objects as JSON. When you print your client settings, the SDK hides your API key.
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))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. If the error body is too large or the connection closes early, you still get the HTTP status. The SDK hides your API key in error messages, including errors for individual IPs in a typed bulk lookup.