open-sar-triadAPI reference
← Map

API reference

Every scene in the console is also a plain JSON file on a CDN. There is no server, no key, no account and no rate limit, because there is nothing to authenticate against. Fetch the files, or use the Python client.

Live, fetched from this API
Loading current catalog figures…

Base URL

https://www.pmuguda.com/open-sar-triad/api/v1

The catalog is rebuilt weekly from the providers' own open-data STAC records. Responses are static files, so they cache well and work offline once fetched. Every file carries its own license, attribution and disclaimer fields, so the terms travel with the data.

CORS is open. You can call these endpoints directly from a browser, a notebook or a static site, with no proxy.

01 Endpoints

{provider} is one of iceye, umbra or capella.

GET /stats.json Counts by provider, mode, year and format, plus the temporal extent. ~2 KB. Start here to see what exists before pulling anything larger.
GET /index.json Compact search index over every scene. ~2.5 MB. One array per scene rather than one object, to keep it small.
GET /catalog.json STAC root catalog, linking to the three collections. The entry point for pystac and other STAC clients.
GET /collections/{provider}.json STAC Collection: spatial and temporal extent, licence, provider metadata.
GET /items/{provider}.json STAC ItemCollection with every scene for that provider. One file rather than 14,000 Items, which would churn the repository every week.
GET /scenes/{provider}.geojson Raw GeoJSON FeatureCollection, for dropping straight into QGIS or geopandas.

Try it

curl -s https://www.pmuguda.com/open-sar-triad/api/v1/stats.json | jq '.total, .by_provider'

03 STAC

The catalog is STAC 1.0.0. Point any STAC client at catalog.json:

import pystac

cat = pystac.Catalog.from_file(
    "https://www.pmuguda.com/open-sar-triad/api/v1/catalog.json")

for collection in cat.get_children():
    print(collection.id, collection.extent.temporal.intervals)

Items carry the sar, sat and view extensions: sar:instrument_mode, sar:polarizations, sar:resolution_range, sat:orbit_state, sar:observation_direction and view:incidence_angle. sar:polarizations is an array, as the spec types it. Two fields are ours: ost:formats lists the formats a scene ships in, and ost:first_seen records when this catalog first saw it.

Each asset key is a product format, and every one is paired with a _metadata sibling, so SICD has SICD_metadata next to it.

04 Python client

pip install open-sar-triad          # core: no dependencies at all
pip install "open-sar-triad[plot]"  # adds matplotlib for the maps
from opensartriad import Catalog

cat = Catalog()                      # no key, no config
print(cat.stats()["total"])

scenes = cat.search(
    bbox=(-118.7, 33.7, -117.6, 34.3),   # Los Angeles
    start="2024-01-01",
    mode="spotlight",
    orbit="ascending",
    polarization="VV",                   # or ["VV", "HH"]
    landuse="airport",                   # or ["port", "urban"]
)
print(len(scenes))

s = scenes[0]
print(s.id, s.provider, s.date, s.formats)
print(s.url("SICD"), s.metadata_url("SICD"))

The first search downloads the index once and caches it, so reuse a single Catalog and every later search is local and instant. Filters combine with AND, and mode is case-insensitive.

Product families

The three providers name the same product differently, so you can ask for a family and let the client resolve it per scene. Umbra's complex product is SICD; ICEYE's is SLC.

s.resolve("detected")   # GRD | GEC | GEO | SIDD
s.resolve("complex")    # SLC | SICD
s.resolve("phase")      # CPHD, or None if the scene has none
s.resolve("visual")     # CSI | VID

05 Land use

Every scene carries a landuse list describing what a map says is at its location. Nothing here looks at a pixel: the tags come from intersecting each footprint against published geographic vectors. A scene tagged airport covers an airport; whether anything is visible in the data is a separate question this catalog has no opinion on.

Tags are a list because a footprint is several kilometres across and routinely spans more than one thing. A scene over Rotterdam is a port and urban and partly water, all at once.

airport port urban industrial military Built infrastructure. A point feature counts when it falls within its own half-extent of the footprint — 2 km for an airport, 1 km for a port — so a city near an airport is not tagged as one.
agriculture forest Vegetated land use. From OpenStreetMap, so present only where that enrichment has run.
mountain hilly Measured terrain relief in a ~10 km window: 500 m and 200 m. From AWS Terrain Tiles, public domain. Not from being inside a named mountain range — the Andes polygon covers Santiago, which is flat ground.
desert wetland ice Land cover.
water offshore Inland water covering a third of the footprint, or sea covering most of it.
cat.search(landuse="port")              # any scene over a port
cat.search(landuse=["airport", "urban"])  # either one
cat.search(landuse="port", providers="umbra", start="2026-01-01")

stats.json carries a by_landuse block with the catalog-wide counts, and STAC Items carry the same list as ost:landuse.

Infrastructure and land-cover tags come from Natural Earth and terrain from AWS Terrain Tiles, both public domain. agriculture, forest, industrial and military come from OpenStreetMap, which is ODbL.

06 Downloads

Asset URLs point at the providers' own S3 buckets. This catalog never proxies or re-hosts imagery, so a download is between you and the provider.

picks = cat.search(bbox=(-118.7, 33.7, -117.6, 34.3), limit=3)

picks.download("data/", family="complex", dry_run=True)   # print, fetch nothing
paths = picks.download("data/", family="complex")         # actually fetch

picks.save_geojson("selection.geojson")
ic = picks.to_stac()                                      # STAC ItemCollection
df = picks.to_dataframe()                                 # needs pandas

Every download brings the scene's metadata sidecar alongside the data file, because a SAR product without its metadata is not much use.

07 Plotting

With the [plot] extra installed, result sets draw themselves against a country basemap.

scenes.plot_coverage()     # footprints on a world map, coloured by provider
scenes.plot_timeline()     # acquisitions over time, stacked by provider
scenes.plot_providers()    # scene counts per provider
scene.plot_footprint()     # one scene, with surrounding geography

Each returns a matplotlib Axes and accepts ax=, so they compose into your own figures. A SAR footprint is thinner than a pixel on a world view, so plot_coverage draws scenes either as geometry or as markers, never as a mix of the two: markers merge per target and scale by how many scenes sit behind them, and zooming in far enough that every scene is visible brings the polygons back at true scale.

08 Licence and attribution

Scene metadata is © ICEYE, Umbra and Capella, published under their open data programmes and licensed CC-BY 4.0. This catalog is assembled from their published STAC records and is an independent third-party index, not endorsed by any of them.

If you redistribute this data or publish derived work, credit the originating provider. Every endpoint repeats these terms in its own body, so the notice travels with whatever you fetch.

CC-BY 4.0 STAC 1.0.0 No API key Weekly refresh