MaxMind GeoIP2 API for IP geolocation using web services and databases
—
Comprehensive exception hierarchy for handling various error conditions that can occur during web service requests and database lookups. The exceptions provide detailed information about failures including network errors, authentication problems, and data availability issues.
import ipaddress
from typing import Optional, UnionThe root exception class for all GeoIP2-specific errors.
class GeoIP2Error(RuntimeError):
"""
Base exception for all GeoIP2 errors.
This class represents a generic error and extends RuntimeError
without adding additional attributes.
"""Exceptions related to IP address lookup failures.
class AddressNotFoundError(GeoIP2Error):
"""
The IP address was not found in the database or web service.
For database lookups, this provides the network information
for efficient subnet enumeration.
"""
def __init__(self, message: str, ip_address: Optional[str] = None,
prefix_len: Optional[int] = None): ...
ip_address: Optional[str] # IP address used in lookup (database only)
@property
def network(self) -> Optional[Union[ipaddress.IPv4Network, ipaddress.IPv6Network]]:
"""
The network associated with the error.
For database lookups, this is the largest network where no address
would be found. Useful for efficient subnet enumeration.
Returns:
Network object or None if not available
"""Exceptions for credential and permission issues with web services.
class AuthenticationError(GeoIP2Error):
"""
Authentication failed due to invalid credentials.
This occurs when the account ID or license key is invalid,
missing, or unauthorized for the requested service.
"""
class PermissionRequiredError(GeoIP2Error):
"""
The account does not have permission to access the requested service.
This typically occurs when trying to access Insights endpoint
without an Insights subscription.
"""
class OutOfQueriesError(GeoIP2Error):
"""
The account has exceeded its query limit or is out of funds.
Check your MaxMind account for remaining queries or billing status.
"""Exceptions for HTTP transport and network communication issues.
class HTTPError(GeoIP2Error):
"""
An HTTP transport error occurred during the web service request.
This represents network-level failures, server errors, or
unexpected HTTP responses.
"""
def __init__(self, message: str, http_status: Optional[int] = None,
uri: Optional[str] = None, decoded_content: Optional[str] = None): ...
http_status: Optional[int] # HTTP status code returned
uri: Optional[str] # URI that was queried
decoded_content: Optional[str] # Response body content
class InvalidRequestError(GeoIP2Error):
"""
The request was invalid or malformed.
This can occur with invalid IP addresses, unsupported parameters,
or other client-side request errors.
"""import geoip2.webservice
from geoip2.errors import (
AddressNotFoundError,
AuthenticationError,
OutOfQueriesError,
HTTPError
)
try:
with geoip2.webservice.Client(42, 'license_key') as client:
response = client.city('127.0.0.1') # Private IP
print(response.country.name)
except AddressNotFoundError:
print("IP address not found in database")
except AuthenticationError:
print("Invalid account ID or license key")
except OutOfQueriesError:
print("Account is out of queries")
except HTTPError as e:
print(f"HTTP error {e.http_status}: {e.decoded_content}")
except GeoIP2Error as e:
print(f"Other GeoIP2 error: {e}")import geoip2.database
from geoip2.errors import AddressNotFoundError
try:
with geoip2.database.Reader('/path/to/GeoLite2-City.mmdb') as reader:
response = reader.city('10.0.0.1') # Private IP
except AddressNotFoundError as e:
print(f"Address {e.ip_address} not found")
if e.network:
print(f"No data available for entire network: {e.network}")
except FileNotFoundError:
print("Database file not found")
except PermissionError:
print("Permission denied accessing database file")import geoip2.webservice
from geoip2.errors import (
AddressNotFoundError,
AuthenticationError,
PermissionRequiredError,
OutOfQueriesError,
InvalidRequestError,
HTTPError,
GeoIP2Error
)
def lookup_ip_with_error_handling(ip_address):
try:
with geoip2.webservice.Client(42, 'license_key') as client:
# Try Insights first (most comprehensive data)
try:
return client.insights(ip_address)
except PermissionRequiredError:
# Fall back to City if no Insights permission
return client.city(ip_address)
except AddressNotFoundError:
print(f"IP {ip_address} not found in database")
return None
except AuthenticationError:
print("Authentication failed - check account ID and license key")
return None
except OutOfQueriesError:
print("Account out of queries - check billing status")
return None
except InvalidRequestError as e:
print(f"Invalid request: {e}")
return None
except HTTPError as e:
if e.http_status == 500:
print("Server error - try again later")
elif e.http_status == 503:
print("Service unavailable - try again later")
else:
print(f"HTTP error {e.http_status}: {e.decoded_content}")
return None
except GeoIP2Error as e:
print(f"Unexpected GeoIP2 error: {e}")
return Noneimport geoip2.database
import geoip2.errors
import ipaddress
def enumerate_subnet_with_error_handling(database_path, subnet):
"""
Efficiently enumerate a subnet using AddressNotFoundError.network
to skip entire ranges with no data.
"""
try:
with geoip2.database.Reader(database_path) as reader:
network = ipaddress.ip_network(subnet)
ip_address = network[0]
while ip_address in network:
try:
response = reader.asn(ip_address)
response_network = response.network
print(f"{response_network}: ASN {response.autonomous_system_number}")
except geoip2.errors.AddressNotFoundError as e:
response_network = e.network
if response_network:
print(f"{response_network}: No data")
else:
# Single IP with no data
response_network = ipaddress.ip_network(f"{ip_address}/32", strict=False)
print(f"{ip_address}: No data")
# Move to next subnet
ip_address = response_network[-1] + 1
except FileNotFoundError:
print(f"Database file not found: {database_path}")
except PermissionError:
print(f"Permission denied: {database_path}")
except Exception as e:
print(f"Unexpected error: {e}")import time
import geoip2.webservice
from geoip2.errors import HTTPError, GeoIP2Error
def lookup_with_retry(ip_address, max_retries=3, retry_delay=1.0):
"""
Lookup IP with retry logic for transient errors.
"""
for attempt in range(max_retries):
try:
with geoip2.webservice.Client(42, 'license_key') as client:
return client.city(ip_address)
except HTTPError as e:
# Retry on server errors (5xx) and rate limiting (429)
if e.http_status and (e.http_status >= 500 or e.http_status == 429):
if attempt < max_retries - 1:
print(f"Server error {e.http_status}, retrying in {retry_delay}s...")
time.sleep(retry_delay)
retry_delay *= 2 # Exponential backoff
continue
raise
except GeoIP2Error:
# Don't retry on client errors
raise
# If we get here, all retries failed
raise HTTPError("Max retries exceeded")import logging
import geoip2.webservice
from geoip2.errors import GeoIP2Error, HTTPError
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def lookup_with_logging(ip_address):
try:
with geoip2.webservice.Client(42, 'license_key') as client:
response = client.city(ip_address)
logger.info(f"Successfully looked up {ip_address}: {response.country.name}")
return response
except HTTPError as e:
logger.error(f"HTTP error for {ip_address}: {e.http_status} {e.uri}")
logger.debug(f"Response content: {e.decoded_content}")
raise
except GeoIP2Error as e:
logger.error(f"GeoIP2 error for {ip_address}: {type(e).__name__}: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error for {ip_address}: {type(e).__name__}: {e}")
raiseInstall with Tessl CLI
npx tessl i tessl/pypi-geoip2