GeoInfer Python SDK
The official Python client library for the GeoInfer API — AI-powered image geolocation without GPS, EXIF, or metadata.
What is GeoInfer?
GeoInfer determines where a photo was taken by analysing its visual content alone — no GPS signal, no EXIF data, no metadata required. Results are delivered in under a second, with global coverage including remote regions, conflict zones, and GPS-denied environments.
The API exposes two model families:
| Model type | Output | Accuracy |
|---|---|---|
| Global | Geographic clusters with coordinates and radius | 1 km – 100 km, worldwide |
| Accuracy (regional) | Ranked coordinate predictions with confidence scores | Metre-level, within a specific region |
Note: Accuracy models are region-specific and generally whitelisted — they are not available to the general public. Contact geoinfer.com to enquire about access.
Requirements
- Python 3.9 or later
- A GeoInfer API key — get one at geoinfer.com
Installation
pip install geoinfer
Quick start
from geoinfer import GeoInfer
client = GeoInfer(api_key="geo_...")
# List models your API key has access to
for m in client.predictions.models():
print(m.model_id, m.model_type, f"{m.credits_per_use} credit(s)")
# Global model — returns geographic clusters
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
top = result.prediction.clusters[0]
print(top.location.name, top.center.latitude, top.center.longitude)
# Accuracy / regional model — returns ranked predictions
result = client.predictions.predict("photo.jpg", model_id="pais_vasco_v0_1")
top = result.prediction.top_prediction
if top:
print(top.latitude, top.longitude, f"{top.confidence:.1%} confidence")
Authentication
Pass your API key at client construction. It is sent as the X-GeoInfer-Key header on every request.
client = GeoInfer(api_key="geo_...")
API keys can be managed from your dashboard at geoinfer.com.
Model access
Available models are determined by your API key — some models are public and some are whitelisted per account. models() returns only the models your key has access to:
models = client.predictions.models()
for m in models:
print(m.model_id, m.model_type, m.credits_per_use)
The model list is fetched on first use and cached for 5 minutes. Passing a model_id that is unknown to your account or disabled raises InvalidModelError immediately — before any prediction request is made.
Async support
AsyncGeoInfer mirrors the synchronous client and is safe to use in any asyncio application:
import asyncio
from geoinfer import AsyncGeoInfer
async def main():
async with AsyncGeoInfer(api_key="geo_...") as client:
result = await client.predictions.predict("photo.jpg", model_id="global_v0_1")
top = result.prediction.clusters[0]
print(top.location.name, top.center.latitude, top.center.longitude)
asyncio.run(main())
File input
predict() accepts five input forms:
from pathlib import Path
# URL — the SDK fetches the image and uploads it automatically
client.predictions.predict("https://example.com/photo.jpg", model_id="global_v0_1")
# Path string
client.predictions.predict("photo.jpg", model_id="global_v0_1")
# pathlib.Path
client.predictions.predict(Path("photo.jpg"), model_id="global_v0_1")
# Raw bytes (already loaded into memory)
with open("photo.jpg", "rb") as f:
client.predictions.predict(f.read(), model_id="global_v0_1")
# Binary file-like object (streamed)
with open("photo.jpg", "rb") as f:
client.predictions.predict(f, model_id="global_v0_1")
URL inputs are fetched client-side and uploaded as bytes — the image is not stored or cached. Any
http://orhttps://string is treated as a URL. File objects must be opened in binary mode ("rb"); passing a text-mode object raisesTypeError.
Maximum file size: 10 MB.
Credits
Each prediction consumes credits according to the model used. Check your balance at any time:
summary = client.credits.summary()
print(summary.summary.total_available) # total credits available now
print(summary.subscription.remaining) # subscription allowance remaining
print(summary.summary.topup_credits) # one-time top-up balance
Credits consumed are reported on every PredictionResponse:
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
print(f"Used {result.credits_consumed} credit(s). ID: {result.prediction_id}")
Rate limits
Rate-limit metadata is attached to every PredictionResponse:
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
rl = result.rate_limit
print(f"{rl.remaining} requests remaining, window resets in {rl.reset}s")
When the limit is exceeded RateLimitError is raised with a retry_after attribute:
import time
from geoinfer import RateLimitError
try:
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
except RateLimitError as e:
if e.retry_after:
time.sleep(e.retry_after)
Error handling
All SDK exceptions inherit from GeoInferError.
| Exception | HTTP status | When raised |
|---|---|---|
AuthenticationError |
401 | Missing or invalid API key |
InsufficientCreditsError |
402 | Account has insufficient credits |
ForbiddenError |
403 | Access denied to this resource |
FileTooLargeError |
413 | Image exceeds the 10 MB limit |
InvalidFileTypeError |
422 | File format is not supported |
RateLimitError |
429 | Request rate limit exceeded |
InvalidModelError |
— | model_id is not in your account's model list or is disabled |
APIError |
other | Unexpected non-2xx response |
from geoinfer import (
GeoInferError,
AuthenticationError,
InsufficientCreditsError,
InvalidModelError,
RateLimitError,
)
try:
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
except InvalidModelError as e:
print(e.message) # includes a list of your available model IDs
except RateLimitError as e:
if e.retry_after:
time.sleep(e.retry_after)
except InsufficientCreditsError:
print("Top up credits at geoinfer.com")
except AuthenticationError:
print("Check your API key at geoinfer.com")
except GeoInferError as e:
print(e.message_code, e.message)
Context managers
Both clients support context managers for deterministic connection cleanup:
# Sync
with GeoInfer(api_key="geo_...") as client:
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
# Async
async with AsyncGeoInfer(api_key="geo_...") as client:
result = await client.predictions.predict("photo.jpg", model_id="global_v0_1")
Configuration
| Parameter | Default | Description |
|---|---|---|
api_key |
(required) | Your GeoInfer API key |
base_url |
https://api.geoinfer.com |
Override the API base URL |
timeout |
60 |
HTTP request timeout in seconds |
model_cache_ttl |
300 |
Seconds to cache the model list before re-fetching |
Type safety
All response objects are immutable frozen dataclasses. Narrow on result.prediction.result_type to get the specific prediction type:
from geoinfer.types import CoordinatePredictionResult, AccuracyPredictionResult
result = client.predictions.predict("photo.jpg", model_id="global_v0_1")
if isinstance(result.prediction, CoordinatePredictionResult):
print(result.prediction.clusters[0].location.name)
elif isinstance(result.prediction, AccuracyPredictionResult):
print(result.prediction.top_prediction.confidence)
License
Apache 2.0 — see LICENSE.
Metadata
Release files for geoinfer 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| geoinfer-0.0.1.tar.gz | 62.0 kB | Details |
Release files / geoinfer-0.0.1.tar.gz
| Download URL | geoinfer-0.0.1.tar.gz |
|---|---|
| Size | 62.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e354414243ebbec5adc07a8302b18d8cd48d32f11197cdf471a6bb8ae49ba08
|
|
BLAKE2b-256 checksum How to use checksums |
3e78face23860eecff2c26e73ba7426826d8b559a7aef5e93370a77188f2681e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 24, 2026.
Transparency log