Python SDK
The GeoLens Python SDK (geolens) is a typed client for the GeoLens API.
It ships typed attrs models, an
httpx sync/async client, and Bearer-token +
API-key auth helpers. It is Apache-2.0 licensed and auto-generated from
the OpenAPI contract, so every operation mirrors a real endpoint in the
API reference.
Install
Section titled “Install”pip install geolensThe package targets a modern Python (3.10+) and pulls in httpx and attrs.
Authenticate
Section titled “Authenticate”Create a GeolensClient pointed at your instance. The deployed API is served
under /api, so include that suffix in base_url:
from geolens import GeolensClient
client = GeolensClient( base_url="https://geolens.example.com/api", bearer_token="...", # a JWT from POST /api/auth/login)You get a bearer token the same way the CLI and any raw client
do: POST /api/auth/login returns a JWT. See
Authentication for the full token-acquisition flow.
The SDK supports the same two header auth modes as the rest of GeoLens: Bearer or API key, but not both:
# API-key auth instead of a bearer token (sent as X-Api-Key):client = GeolensClient( base_url="https://geolens.example.com/api", api_key="...",)Passing both bearer_token and api_key raises ValueError. With neither, the
client is anonymous and can only reach public endpoints. An API key the
instance cannot resolve returns 401 on every endpoint that reads credentials
(v1.14.0 and newer); older instances mostly discarded the bad key and answered
with the public subset, so a stale key could make a private dataset seem to
have vanished.
First call
Section titled “First call”Operations live under geolens.api.<tag> and each exposes sync,
sync_detailed, asyncio, and asyncio_detailed. Pass the underlying
client via client.client.
The health check is the simplest read: no parameters, no auth required:
from geolens import GeolensClientfrom geolens.api.health import health_health_get
client = GeolensClient(base_url="https://geolens.example.com/api")
# sync() returns the parsed model (HealthResponse) or None.health = health_health_get.sync(client=client.client)print(health.status) # e.g. "healthy"print(health.providers) # HealthResponseProviders, per-service statusNeed the status code and headers too? Use sync_detailed, which returns a
Response[HealthResponse] wrapper:
resp = health_health_get.sync_detailed(client=client.client)print(resp.status_code) # HTTPStatus.OKprint(resp.parsed.status) # the parsed HealthResponseEvery operation also has an asyncio variant for the httpx async client:
import asynciofrom geolens import GeolensClientfrom geolens.api.health import health_health_get
async def main() -> None: client = GeolensClient(base_url="https://geolens.example.com/api") health = await health_health_get.asyncio(client=client.client) print(health.status)
asyncio.run(main())Authenticated reads follow the same shape. For example, searching the catalog
uses geolens.api.search.search_datasets_endpoint_search_datasets_get and
returns a typed OGCFeatureCollectionResponse.
Search, schema, export
Section titled “Search, schema, export”Three calls take you from a question to a GeoDataFrame without assembling a
URL. The last step reads the export with GeoPandas, which pip install geolens
does not pull in, so add pip install geopandas first:
import iofrom uuid import UUID
import geopandas as gpdfrom geolens import GeolensClientfrom geolens.api.datasets import export_dataset_endpoint_datasets_dataset_id_export_get as export_datasetfrom geolens.api.datasets_metadata import list_attributes_endpoint_datasets_dataset_id_attributes_get as list_attributesfrom geolens.api.search import search_datasets_endpoint_search_datasets_get as search
client = GeolensClient(base_url="https://geolens.example.com/api")
# Catalog search returns OGC API Records; each feature id is a dataset id.hit = search.sync(client=client.client, q="meteorite landings", limit=1).features[0]dataset_id = UUID(hit.id)
# Column metadata the profiler inferred at ingest, beyond names and types.for attr in list_attributes.sync(client=client.client, dataset_id=dataset_id).attributes: print(attr.field_name, attr.data_type, attr.semantic_role, attr.units)
# The filter is SQL over the dataset's own columns, evaluated server-side.resp = export_dataset.sync_detailed( client=client.client, dataset_id=dataset_id, format_="geojson", where="mass_kg > 1000")gdf = gpd.read_file(io.BytesIO(resp.content))list_attributes returns a semantic_role (such as label, measure,
temporal, categorical) and units for each column, which is how you learn
that the filter belongs on mass_kg and that the values are kilograms.
export_dataset runs the where clause in PostGIS and returns only the
matching rows; format_ also takes gpkg, parquet, shp, and csv. Quoted
string literals are the one gap in the where validator: fall = 'Fell' is
rejected as Unknown column: Fell, so filter on numbers server-side and on
strings in pandas.
Errors arrive typed. A rejected filter is a 400 whose
sync_detailed(...).parsed is a ProblemDetail (RFC 9457), so the reason is
resp.parsed.detail rather than JSON you decode by hand.
The full script is
python/sdk-catalog.py
in the examples repo;
python/README.md
shows its output.
Next steps
Section titled “Next steps”- Authentication: how to obtain a bearer token or API key.
- API reference: the full operation surface; every
geolens.api.*function maps to an endpoint here. - CLI & Manifests: for terminal and CI workflows; the CLI wraps this SDK.
- TypeScript SDK: the same surface for Node.
python/analyze.py: the same catalog over plain OGC API Features withhttpxand GeoPandas, pinned and run against the live demo.