# Install
pip install ipscope# Quickstart: single, batch & prefix lookups
Pass your API key explicitly or set IPSCOPE_KEY in your environment. Every lookup returns the canonical response schema with geo, network, connection, security and security_extensions.
import asyncio
import os
from ipscope import AsyncIpScopeClient
async def main() -> None:
async with AsyncIpScopeClient(api_key=os.environ["IPSCOPE_KEY"]) as client:
# 1. Single IPv4 or IPv6 lookup (cached automatically in bounded LRU)
info = await client.lookup_ip("198.51.100.7")
print(info["ip"], info["geo"]["country_code"], info["network"]["asn"])
print("VPN:", info["security"]["is_vpn"], "Reason:", info["security_extensions"]["anonymizer_reason"])
# 2. Batch lookup (up to 100 IPs; deduplicates & reuses cached entries)
batch = await client.lookup_batch(["198.51.100.7", "1.1.1.1", "2606:4700:4700::1111"])
for record in batch["results"]:
print(record["ip"], "->", record["network"]["organization"])
# 3. Memory-bounded async streaming for 10,000+ IPs (constant O(chunk) RAM)
async for chunk in client.lookup_batch_stream(large_ip_list, chunk_size=100):
for record in chunk["results"]:
pass # Process each record with < 350 KB steady-state memory overhead
# 4. Network CIDR prefix lookup
prefix = await client.lookup_network("198.51.100.0/24")
print(prefix["queried_cidr"], prefix["record"]["asn"], prefix["record"]["organization"])
asyncio.run(main())1. Single IPv4/IPv6 lookup response (GET /v1/ip/{ip})
{
"ip": "198.51.100.7",
"ip_version": 4,
"matched_network": "198.51.100.0/24",
"geo": {
"country_code": "US",
"declared_country_code": "US",
"physical_country_code": "US",
"is_virtual_location": false,
"location_type": "physical",
"confidence": 0.94,
"subdivision": "Virginia",
"city": "Ashburn",
"postal_code": "20147",
"latitude": 39.0438,
"longitude": -77.4874,
"accuracy_radius_km": 10,
"timezone": "America/New_York"
},
"network": {
"asn": 64501,
"organization": "Example Cloud LLC"
},
"connection": {
"connection_type": "hosting",
"is_hosting": true,
"is_residential": false,
"is_mobile": false,
"is_business": false
},
"security": {
"is_vpn": true,
"is_hosting": true,
"is_tor_exit": false,
"is_proxy": false,
"anonymizer_type": "vpn",
"confidence": 0.96,
"reason_codes": [
"vpn_handshake_confirmed",
"datacenter_asn_range"
]
},
"security_extensions": {
"abuse_velocity_score": 18,
"is_known_scanner": false,
"observation_recency_hours": 12,
"anonymizer_reason": "vpn_handshake_confirmed",
"threat_level": "medium",
"abuse_velocity": "low",
"confidence": 0.96
},
"dataset_version": "2026-10-27T00:00:00Z.crawl-1793088000000",
"updated_at": "2026-10-27T00:00:00Z"
}4. Network CIDR prefix lookup response (GET /v1/networks/{prefix}/{length})
{
"queried_cidr": "198.51.100.0/24",
"family": 4,
"dataset_version": "2026-10-27T00:00:00Z.crawl-1793088000000",
"record": {
"prefix": "198.51.100.0/24",
"family": 4,
"asn": 64501,
"organization": "Example Cloud LLC",
"countryCode": "US",
"city": "Ashburn",
"locationType": "physical",
"isVpn": true,
"isHosting": true,
"isTorExit": false,
"isProxy": false,
"anonymizerType": "vpn"
}
}# Architecture & runtime guarantees
- Ships both
AsyncIpScopeClient(httpx.AsyncClientconnection pooling) andIpScopeClient(thread-safe sync wrapper). - Built-in O(1) bounded LRU cache (
max_cache_entries=2048,cache_ttl_seconds=60) with automatic batch deduplication. lookup_batch_stream()yields chunks of 100 IPs lazily viaAsyncIteratorso 100,000+ IP enrichments run in constant< 350 KBheap memory.- Full
TypedDictdefinitions (IpLookupResponse,GeoInfo,SecurityInfo,SecurityExtensionsInfo) for strictmypyand Pyright checking.
# Reading local MMDB files
If you subscribe to a downloadable database, our .mmdb snapshots open directly in standard MaxMind DB readers with sub-microsecond in-memory lookups:
import maxminddb
with maxminddb.open_database("/var/lib/ipscope/ipscope-complete.mmdb") as reader:
record = reader.get("198.51.100.7")
print(record["geo"]["country_code"], record["security"]["is_vpn"])# Plain HTTP fallback
Need to call the REST API without installing a package? Use Python’s standard HTTP client directly:
import os
import httpx
async with httpx.AsyncClient(timeout=5.0) as client:
res = await client.get(
"https://api.ipscope.io/v1/ip/198.51.100.7",
headers={"Authorization": f"Bearer {os.environ['IPSCOPE_KEY']}"},
)
res.raise_for_status()
info = res.json()