eosdk
Python SDK and CLI (eo) for Earth Observation services: unified catalogue search
(STAC / OData), EOData downloads (HTTP / S3), and fully managed authentication
(Keycloak JWT, S3 key lifecycle) behind one coherent interface — search, then download.
Highlights
- Three steps to start your EO adventure: register, install, explore. Sign up for a
platform account, install the
eoCLI, and you're searching the catalogue. - Every EO service API in one place. Catalogue, downloads, S3 keys, and auth sit behind a single interface — you never juggle individual service APIs. Need more? Configuration resolves per field, from a single platform root down to individually pinned endpoints.
- Two modes, one eosdk. No API knowledge needed: human-readable calls —
eo auth login,eo search,eo download— cover the whole workflow, and the typed Python library mirrors them one-to-one (client.auth.login(),client.search(),client.download()) on the same session, config, and endpoints. On top of that, raw queries are available for the most demanding users. - Any EO platform from one URL. Point the SDK at a platform's discovery root —
for example CDSE's
https://discover.dataspace.copernicus.eu— and every endpoint is discovered from/.well-known/eo-services.json; nothing is hard-coded. - Search, then download, as one pipeline.
eo search --format json | eo download -is the whole workflow; JSON Lines interoperate cleanly withjqand the Unix toolbox. - Auth you never think about. Log in once; the session is cached per profile and reused by every later CLI and library call. S3 keys are minted automatically when needed.
- Backend details stay out of your way. HTTP vs. S3, STAC vs. OData — pick one flag; the SDK owns the protocol, retries, concurrency, checksums, resume, and credentials.
- Typed and strict. Pydantic models,
py.typed, and a strict-mypy codebase.
Quickstart (the eo CLI)
Examples use the Copernicus Data Space Ecosystem (CDSE) — one EO platform among those eosdk can talk to; any platform that publishes a discovery document works the same way, just with its own root URL and account. Search is anonymous; downloads require a login.
1 — Register for a free account at https://dataspace.copernicus.eu/ and confirm your email.
2 — Install the eo CLI from PyPI (Python 3.10+; see
Installation for pip, the library, and installing from source):
uv tool install eosdk # or: pipx install eosdk — makes `eo` available everywhere
eo --help
3 — Connect to the platform. One command discovers every endpoint from the platform root and saves them as your default profile:
eo config init --platform https://discover.dataspace.copernicus.eu
eo config show # every endpoint, with the source that provided it
4 — Log in with username/password (the password is prompted, so it never lands in your shell history):
eo auth login --username you@example.com
eo auth status
The bare
eo auth login(OAuth device flow) is the default when a realm supports it, but CDSE's public client currently has that grant disabled — use--usernameon CDSE. For CI, pipe the password in with--password-stdin.
5 — Search. A human-readable table by default:
eo search \
--collection sentinel-2-l2a \
--bbox 22.5,52.9,24.0,53.5 \
--from 2026-06-01 --to 2026-06-30 \
--filter "cloudCover=<20" \
--sort "-datetime" \
--limit 10
Unbounded searches are refused — give at least one of
--collection/--bbox/--from+--to/--filter. Collection ids are backend vocabulary: STAC (the default) uses product-level ids likesentinel-2-l2a; mission-level names likeSENTINEL-1belong to OData (--protocol odata). List a catalogue's collection ids witheo collections, and a collection's filterable attributes witheo queryables <collection>.
6 — Download. Pipe search --format json (one product per line) straight into
download:
eo search --collection sentinel-2-l2a \
--bbox 22.5,52.9,24.0,53.5 \
--from 2026-06-01 --to 2026-06-30 \
--filter "cloudCover=<10" \
--limit 2 --format json \
| eo download - --via http --output ./data --concurrency 4
Ctrl+C stops the batch; re-running the same command picks it up again — --via s3
resumes partial files, --via http restarts them.
The Python library
The library reuses the session cached by eo auth login, so most programs never log in
themselves. Add it to a project with uv add eosdk (or pip install eosdk).
import getpass
from eosdk import Client
client = Client() # your default profile (eo config init); or Client(platform=...) / Client(profile=...)
if not client.auth.status().logged_in:
client.auth.login("you@example.com", getpass.getpass()) # cached under ~/.config/eosdk/
products = client.search(
collection="sentinel-2-l2a", # protocol="odata" for mission-level ids
bbox=(22.5, 52.9, 24.0, 53.5),
datetime="2026-06-01/2026-06-30",
filters={"cloudCover": "<20"},
limit=50,
)
client.download(products, target="./data", via="http", concurrency=4)
See examples/python/ for ranged reads, S3 keys, error handling, raw-query escape hatches, and plugin backends.
Setting up the platform
A "platform" is a deployment of EO services (catalogue, download, keys, Keycloak). The SDK ships no built-in endpoints — you tell it which platform to talk to, once, and endpoints resolve per field, most specific source wins:
Client kwargs > EOSDK_* env > ./eosdk.toml > user profile > discovery
You rarely set every endpoint — point the SDK at a platform root and the rest are
discovered from {platform}/.well-known/eo-services.json. Inspect the result at any
time with eo config show (or client.config.resolved()), which prints every endpoint
with the source that provided it.
Named profiles (recommended) — create one from a root URL; discovery fills in the
rest. Profiles live in ~/.config/eosdk/config.toml:
eo config init --name prod --platform https://platform.example.eu
eo config use prod # make it the default
eo --profile staging search ... # or select per-invocation
eo config set profiles.staging.eodata_http https://canary.example.eu # pin beats discovery
Equivalent hand-written config:
default_profile = "prod"
[profiles.prod]
platform = "https://platform.example.eu" # everything discovered from the root
[profiles.staging]
platform = "https://staging.example.eu"
eodata_http = "https://canary.example.eu" # pinned; rest discovered
[profiles.local] # fully manual, no discovery
catalogue_stac = "http://localhost:8081/stac"
catalogue_odata = "http://localhost:8081/odata"
eodata_http = "http://localhost:8082"
s3_endpoint = "http://localhost:9000"
s3_credentials = "http://localhost:8083/api"
keycloak = "http://localhost:8180"
keycloak_realm = "eodata"
Project-local ./eosdk.toml — the same [profiles.*] syntax in the working
directory beats the user profile (and loses to env vars); handy for pinning a repo to
one deployment.
Environment variables — good for CI and one-off overrides. Every endpoint has an
EOSDK_*_URL variable (full map in
src/eosdk/config/defaults.py):
export EOSDK_PLATFORM=https://platform.example.eu # platform root; endpoints discovered
export EOSDK_PROFILE=staging # select a profile
export EOSDK_EODATA_HTTP_URL=http://localhost:8082 # override one endpoint only
In code — Client(...) kwargs win over everything:
Client(platform=...), Client(profile="prod"),
Client(endpoints={"eodata_http": ...}).
Where the discovery document lives — a dedicated subdomain (recommended, servable
from a static bucket/CDN), the main domain root, or any URL via discovery_url /
EOSDK_DISCOVERY_URL.
CDSE's discovery document is live at
https://discover.dataspace.copernicus.eu—eo config init --platform https://discover.dataspace.copernicus.eubootstraps a ready-to-use profile from it.
Installation
eosdk is published on PyPI as eosdk and requires
Python 3.10 or newer. One package ships both the eo CLI and the Python library.
The eo CLI — install it as a standalone tool in its own isolated environment,
available on your PATH everywhere:
uv tool install eosdk # or: pipx install eosdk
uv tool upgrade eosdk # later, to update (pipx: pipx upgrade eosdk)
To try it without installing anything, uvx --from eosdk eo --help runs it from a
throwaway environment.
The Python library — add it to your project's dependencies; the eo command is
installed into that environment too:
uv add eosdk # or: pip install eosdk
Pre-1.0, a minor release may contain breaking changes (always called out in the
changelog), so pin the
minor series in projects: uv add "eosdk~=0.5.0" (that is, >=0.5.0, <0.6).
From source — for development, or to run unreleased changes. From a clone, either install the CLI globally:
git clone https://github.com/CloudFerro/eosdk && cd eosdk
uv tool install --editable . # `--editable` picks up local code changes
or work inside the project virtualenv:
uv sync
uv run eo --help # or: source .venv/bin/activate && eo --help
Examples
examples/ contains
runnable, commented examples for both surfaces:
examples/python/
covers search, downloads, ranged reads, auth and S3 keys, configuration, error
handling, raw queries, and plugin backends;
examples/cli/ are
annotated eo walkthroughs.
Local discovery endpoint (Docker)
docker/discovery/ serves a sample discovery document at the well-known path — useful for developing against discovery without a real platform (run from a clone):
docker build -t eosdk-discovery docker/discovery
docker run --rm -p 8080:80 eosdk-discovery
# then: Client(platform="http://localhost:8080")
Mount your own document over
/usr/share/nginx/html/.well-known/eo-services.json to replace the baked-in sample.
Development
uv sync # create venv + install all dependency groups
uv run pytest # tests
uv run ruff check . # lint
uv run mypy # type check (strict)
Smoke tests hit real services and need credentials for the authenticated ones:
EOSDK_SMOKE=1 \
EOSDK_SMOKE_USERNAME=you@example.com \
EOSDK_SMOKE_PASSWORD=... \
uv run pytest tests/smoke/
Attention: some smoke tests create S3 credentials (with teardown); don't run them frequently, as S3 credentials are not meant to be temporary.
Documentation
- docs/ — user guide
built with MkDocs:
quickstart,
configuration,
CLI reference,
errors, and the
Python API reference.
Build and preview locally with
uv run mkdocs serve. - CHANGELOG.md — notable changes per release, following Keep a Changelog; the project uses Semantic Versioning.
Acknowledgements
eosdk stands on a small set of excellent open-source libraries — httpx for HTTP, boto3 for S3, pydantic for typed models, typer and rich for the CLI, and tenacity for retries — and interoperates with the wider STAC and OData ecosystems. Thank you to their maintainers.
Authors
eosdk is developed and maintained by CloudFerro.
License
eosdk is licensed under the Apache License, Version 2.0. See the LICENSE file for the full text.
Metadata
Release files for eosdk 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| eosdk-0.5.0.tar.gz | 410.2 kB | Without provenance | Details for eosdk-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| eosdk-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for eosdk-0.5.0-py3-none-any.whl |
Total release size: 513.4 kB
Release files / eosdk-0.5.0.tar.gz
| Download URL | eosdk-0.5.0.tar.gz |
|---|---|
| Size | 410.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83380df3d0f058f8a4c32a298cb34ae79af16011948b06e565126fa67ed368cc
|
|
BLAKE2b-256 checksum How to use checksums |
26171a472f1cf3b9ef96c8554823d53fc4f4bb85939410e498ee9da11bccc4ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / eosdk-0.5.0-py3-none-any.whl
| Download URL | eosdk-0.5.0-py3-none-any.whl |
|---|---|
| Size | 103.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7ccb461b2b8d521b507e8af72bfae5bb8353dbf0a4f0001249d0fb160322d21a
|
|
BLAKE2b-256 checksum How to use checksums |
07819847d3ca26c133aecc86bf3e54eaac194220ada4f4232bcb4b557dfc3e7a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for eosdk 0.5.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.