Python

Python SDK

Install, configure, and authenticate the SateAIs Python client.

The SateAIs Python SDK (sateais) is a typed client for the SateAIs API. It resolves credentials automatically, exposes one method per detection type, and includes a sateais command-line tool.

Packagesateais (PyPI)
Python≥ 3.10
Dependencieshttpx

Installation

pip install sateais

Authentication

Create an API key in the SateAIs API Console (see Authentication).

Get your free API keyYou get 500 free API credits every month during the beta. No credit card required.

The client resolves it from the first available source, in order:

  1. The api_key argument to Client(...).
  2. The SATEAIS_API_KEY environment variable.
  3. The credentials file at ~/.sateais/credentials (written by sateais login).
from sateais import Client

# 1. Explicit argument
client = Client(api_key="sk_live_xxxxx")

# 2. From SATEAIS_API_KEY (recommended)
client = Client()

If no key can be resolved, the constructor raises CredentialsNotFoundError.

Keep your key secret

Never hard-code sk_live_ keys in source control. Prefer the SATEAIS_API_KEY environment variable or sateais login.

Creating a client

from sateais import Client

client = Client(
    api_key=None,        # falls back to env / credentials file
    base_url=None,       # defaults to https://api.spcsft.com/api/v1
    timeout=30.0,        # per-request timeout in seconds
)

The base URL falls back to the SATEAIS_BASE_URL environment variable, then to the production endpoint.

Releasing resources

Client holds an HTTP connection pool. Use it as a context manager, or call close() when you are done:

with Client() as client:
    job = client.analyze.ship(scene_id="S1A_IW_GRDH_...")
    result = client.jobs.wait(job.job_id)

# or manually
client = Client()
try:
    ...
finally:
    client.close()

Your first detection

from sateais import Client

client = Client()

# Submit a job
job = client.analyze.ship(scene_id="S1A_IW_GRDH_...")
print(job.job_id, job.status)  # -> "...", JobStatus.PENDING

# Poll until completion and fetch the GeoJSON
result = client.jobs.wait(job.job_id, poll_interval=30, on_poll=lambda j: print(j.status))
print(len(result["features"]), "ships detected")

Preview before submitting

client.preview.* mirrors client.analyze.* — the same five methods with the same arguments — but creates no job and spends no credits. It returns the area the request is expected to analyse and the credits it would cost, so you can widen the period, split the AOI, or top up before committing:

preview = client.preview.newbuilding(
    polygon="POLYGON((139.7 35.6, 139.8 35.6, 139.8 35.7, 139.7 35.7, 139.7 35.6))",
    date_start="2026-01-01",
    date_end="2026-05-01",
)

preview.area_sqkm            # area expected to be analysed (km²)
preview.credits.estimated    # expected cost — None means "not determined before submission"
preview.credits.balance      # current balance
preview.credits.sufficient   # whether the balance covers it — None when estimated is None
preview.credits.shortfall    # how much you are short — 0.0 when covered, None when unknown

if preview.coverage is not None:   # None means "cannot be predicted", not 100%
    preview.coverage.ratio         # fraction of the AOI that will be analysed (0.0–1.0)
    preview.coverage.polygon       # WKT of the area that will be analysed

for warning in preview.warnings:
    print(warning.code, warning.message)

A short balance does not raise — that is what credits.sufficient is for. InsufficientCreditsError is raised on submission only. Every other check runs the same validation as submission, in the same order, so a preview that succeeds means the submission passes validation too.

credits.estimated of None means the cost is not knowable before submission (a scene_id input, for example) — it does not mean the job is free. Likewise coverage of None means "cannot be predicted", not "100% covered". See Preview a request.

Handling errors

All SDK exceptions inherit from SateAIsError. API responses map to typed subclasses that mirror the API error codes:

from sateais import Client, RateLimitError, JobFailedError, InsufficientCreditsError

client = Client()

try:
    job = client.analyze.timeseries(
        polygon="POLYGON((...))",
        date_start="2026-01-01",
        date_end="2026-05-01",
    )
    result = client.jobs.wait(job.job_id, timeout=3600)
except InsufficientCreditsError:
    print("Top up credits in the Console")
except RateLimitError:
    print("Rate limited — back off and retry")
except JobFailedError as err:
    print("Job failed:", err.job.error_code, err.job.error_message)

See the Reference for the full exception hierarchy.

Command-line interface

Installing the package also provides a sateais CLI for quick, scriptable runs:

# Store your API key in ~/.sateais/credentials (prompts if --api-key is omitted)
sateais login --api-key sk_live_xxxxx

# Submit a job, wait for it, and save the result
sateais analyze ship --scene-id S1A_IW_GRDH_... --wait -o ships.geojson

# Polygon-period analyses
sateais analyze timeseries \
  --polygon "POLYGON((139.7 35.6, 139.8 35.6, 139.8 35.7, 139.7 35.7, 139.7 35.6))" \
  --date-start 2026-01-01 --date-end 2026-05-01 --wait -o result.geojson

# Check the analysed area and credit cost without submitting anything
sateais preview newbuilding \
  --polygon "POLYGON((139.7 35.6, 139.8 35.6, 139.8 35.7, 139.7 35.7, 139.7 35.6))" \
  --date-start 2026-01-01 --date-end 2026-05-01

# Inspect or fetch jobs
sateais jobs status <job_id>
sateais jobs result <job_id> -o result.geojson
sateais jobs wait <job_id> --poll-interval 30 -o result.geojson

# Decode a Sentinel-1 scene ID locally (no API call)
sateais scene S1A_IW_GRDH_...

Every analyze subcommand accepts --satellite-id, --wait, --poll-interval, --timeout, and -o/--output. ship and oilslick additionally take --scene-id, --polygon, --date, --date-direction, and --orbit-direction; the polygon-period types (newbuilding, disappearbuilding, timeseries) take --polygon, --date-start, --date-end, and --orbit-direction.

sateais preview <endpoint> takes the same analysis parameters as sateais analyze <endpoint> (minus the waiting flags) and prints the preview JSON. It exits 0 even when the balance is short, because the API answers 200 with "sufficient": false rather than an error.

Analysis parameters can also be passed as a single object with --json — a JSON string, @file.json, or - for stdin. Individual flags override the values in the JSON.

Next steps

On this page