PySTAC-Client Introduction#
This notebook shows basic use of pystac-client to open an API, iterate through Collections and Items, and perform simple spatio-temporal searches.
[1]:
# set pystac_client logger to DEBUG to see API calls
import logging
from pystac_client import Client
logging.basicConfig()
logger = logging.getLogger("pystac_client")
logger.setLevel(logging.DEBUG)
Client#
We first connect to an API by retrieving the root catalog, or landing page, of the API with the Client.open function.
[2]:
# STAC API root URL
URL = "https://planetarycomputer.microsoft.com/api/stac/v1"
# custom headers
headers = []
cat = Client.open(URL, headers=headers)
cat
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1 Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
[2]:
CollectionClient#
As with a static catalog the get_collections function will iterate through the Collections in the Catalog. Notice that because this is an API it can get all the Collections through a single call, rather than having to fetch each one individually.
[3]:
for collection in cat.get_collections():
print(collection)
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/ Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
<CollectionClient id=daymet-annual-pr>
<CollectionClient id=daymet-daily-hi>
<CollectionClient id=3dep-seamless>
<CollectionClient id=3dep-lidar-dsm>
<CollectionClient id=fia>
<CollectionClient id=gridmet>
<CollectionClient id=daymet-annual-na>
<CollectionClient id=daymet-monthly-na>
<CollectionClient id=daymet-annual-hi>
<CollectionClient id=daymet-monthly-hi>
<CollectionClient id=daymet-monthly-pr>
<CollectionClient id=gnatsgo-tables>
<CollectionClient id=hgb>
<CollectionClient id=cop-dem-glo-30>
<CollectionClient id=cop-dem-glo-90>
<CollectionClient id=terraclimate>
<CollectionClient id=nasa-nex-gddp-cmip6>
<CollectionClient id=gpm-imerg-hhr>
<CollectionClient id=gnatsgo-rasters>
<CollectionClient id=3dep-lidar-hag>
<CollectionClient id=io-lulc-annual-v02>
<CollectionClient id=goes-cmi>
<CollectionClient id=conus404>
<CollectionClient id=sentinel-1-rtc>
<CollectionClient id=3dep-lidar-intensity>
<CollectionClient id=3dep-lidar-pointsourceid>
<CollectionClient id=mtbs>
<CollectionClient id=noaa-c-cap>
<CollectionClient id=3dep-lidar-copc>
<CollectionClient id=modis-64A1-061>
<CollectionClient id=alos-fnf-mosaic>
<CollectionClient id=3dep-lidar-returns>
<CollectionClient id=mobi>
<CollectionClient id=landsat-c2-l2>
<CollectionClient id=chloris-biomass>
<CollectionClient id=kaza-hydroforecast>
<CollectionClient id=planet-nicfi-analytic>
<CollectionClient id=modis-17A2H-061>
<CollectionClient id=modis-11A2-061>
<CollectionClient id=daymet-daily-pr>
<CollectionClient id=predicted-damage-venezuela-2026>
<CollectionClient id=3dep-lidar-dtm-native>
<CollectionClient id=3dep-lidar-classification>
<CollectionClient id=3dep-lidar-dtm>
<CollectionClient id=gap>
<CollectionClient id=modis-17A2HGF-061>
<CollectionClient id=planet-nicfi-visual>
<CollectionClient id=gbif>
<CollectionClient id=modis-17A3HGF-061>
<CollectionClient id=modis-09A1-061>
<CollectionClient id=alos-dem>
<CollectionClient id=alos-palsar-mosaic>
<CollectionClient id=deltares-water-availability>
<CollectionClient id=modis-16A3GF-061>
<CollectionClient id=modis-21A2-061>
<CollectionClient id=us-census>
<CollectionClient id=jrc-gsw>
<CollectionClient id=deltares-floods>
<CollectionClient id=modis-43A4-061>
<CollectionClient id=modis-09Q1-061>
<CollectionClient id=modis-14A1-061>
<CollectionClient id=hrea>
<CollectionClient id=modis-13Q1-061>
<CollectionClient id=modis-14A2-061>
<CollectionClient id=sentinel-2-l2a>
<CollectionClient id=modis-15A2H-061>
<CollectionClient id=modis-11A1-061>
<CollectionClient id=modis-15A3H-061>
<CollectionClient id=modis-13A1-061>
<CollectionClient id=daymet-daily-na>
<CollectionClient id=nrcan-landcover>
<CollectionClient id=modis-10A2-061>
<CollectionClient id=ecmwf-forecast>
<CollectionClient id=noaa-mrms-qpe-24h-pass2>
<CollectionClient id=nasadem>
<CollectionClient id=io-lulc>
<CollectionClient id=landsat-c2-l1>
<CollectionClient id=drcog-lulc>
<CollectionClient id=chesapeake-lc-7>
<CollectionClient id=chesapeake-lc-13>
<CollectionClient id=chesapeake-lu>
<CollectionClient id=noaa-mrms-qpe-1h-pass1>
<CollectionClient id=noaa-mrms-qpe-1h-pass2>
<CollectionClient id=noaa-nclimgrid-monthly>
<CollectionClient id=usda-cdl>
<CollectionClient id=eclipse>
<CollectionClient id=esa-cci-lc>
<CollectionClient id=esa-cci-lc-netcdf>
<CollectionClient id=fws-nwi>
<CollectionClient id=usgs-lcmap-conus-v13>
<CollectionClient id=usgs-lcmap-hawaii-v10>
<CollectionClient id=noaa-climate-normals-tabular>
<CollectionClient id=noaa-climate-normals-netcdf>
<CollectionClient id=goes-glm>
<CollectionClient id=sentinel-1-grd>
<CollectionClient id=noaa-climate-normals-gridded>
<CollectionClient id=aster-l1t>
<CollectionClient id=cil-gdpcir-cc-by-sa>
<CollectionClient id=naip>
<CollectionClient id=io-lulc-9-class>
<CollectionClient id=io-biodiversity>
<CollectionClient id=noaa-cdr-sea-surface-temperature-whoi>
<CollectionClient id=noaa-cdr-ocean-heat-content>
<CollectionClient id=cil-gdpcir-cc0>
<CollectionClient id=cil-gdpcir-cc-by>
<CollectionClient id=noaa-cdr-sea-surface-temperature-whoi-netcdf>
<CollectionClient id=noaa-cdr-sea-surface-temperature-optimum-interpolation>
<CollectionClient id=modis-10A1-061>
<CollectionClient id=sentinel-5p-l2-netcdf>
<CollectionClient id=sentinel-3-olci-wfr-l2-netcdf>
<CollectionClient id=noaa-cdr-ocean-heat-content-netcdf>
<CollectionClient id=met-office-uk-deterministic-pressure>
<CollectionClient id=sentinel-3-synergy-aod-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-v10-l2-netcdf>
<CollectionClient id=sentinel-3-olci-lfr-l2-netcdf>
<CollectionClient id=sentinel-3-sral-lan-l2-netcdf>
<CollectionClient id=sentinel-3-slstr-lst-l2-netcdf>
<CollectionClient id=sentinel-3-slstr-wst-l2-netcdf>
<CollectionClient id=sentinel-3-sral-wat-l2-netcdf>
<CollectionClient id=hls2-s30>
<CollectionClient id=ms-buildings>
<CollectionClient id=sentinel-3-slstr-frp-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-syn-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-vgp-l2-netcdf>
<CollectionClient id=sentinel-3-synergy-vg1-l2-netcdf>
<CollectionClient id=esa-worldcover>
<CollectionClient id=met-office-uk-deterministic-near-surface>
<CollectionClient id=met-office-global-deterministic-near-surface>
<CollectionClient id=hls2-l30>
<CollectionClient id=met-office-global-deterministic-height>
<CollectionClient id=met-office-global-deterministic-pressure>
<CollectionClient id=met-office-uk-deterministic-height>
<CollectionClient id=met-office-uk-deterministic-whole-atmosphere>
<CollectionClient id=era5-pds>
<CollectionClient id=met-office-global-deterministic-whole-atmosphere>
Collection Search#
Sometimes, it can be challenging to identify which collection you want to work with. The collection_search method allows you to discover collections by applying search filters that will help you find the specific collection(s) you need. Since many STAC APIs have not implemented the collection search extension, pystac-client will perform a limited client-side filter if the API does not conform to the collection search spec.
[4]:
collection_search = cat.collection_search(
q="ASTER",
)
/home/docs/checkouts/readthedocs.org/user_builds/pystac-client/checkouts/latest/pystac_client/collection_search.py:306: DoesNotConformTo: Server does not conform to COLLECTION_SEARCH, Filtering will be performed client-side where only bbox, datetime, and q arguments are supported
warnings.warn(
The collections method lets you iterate through the results of the search so you can inspect the details of matching collections.
[5]:
for result in collection_search.collections():
print(result.id, f"{collection.description}", sep="\n")
print("\n")
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections?q=ASTER Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
modis-21A2-061
This collection offers 14 whole-atmosphere parameters from the Met Office global deterministic 10km forecast. This is a numerical weather prediction forecast for the whole globe, with a resolution of approximately 0.09 degrees i.e. 10km (2,560 x 1,920 grid points).
The data is available as NetCDF files. It's offered on a free, unsupported basis, so we don't recommend using it for any critical business purposes.
## Timesteps
The following timesteps are available:
* every hour from 0 to 54 hours (for most parameters, see parameter table for exceptions)
* every 3 hours from 57 to 144 hours
* every 6 hours from 150 to 168 hours
## Update Frequency
The model is run four times each day, with forecast reference times of 00:00, 06:00, 12:00 and 18:00 (UTC).
The runs at 00:00 and 12:00 provide data for the next 168 hours. The runs at 06:00 and 18:00 provide data for the next 67 hours.
The forecast reference time represents the nominal data time or start time of a model forecast run, rather than the time when the data is available.
## Archive length and latency
As of December 2025, the archive contains data from December 2023 onwards. Forecasts will continue to be available for at least two years from their data date.
The data is typically available 6 hours after the model run time.
## Technical specs
The data is available as NetCDF files. NetCDF (Network Common Data Form) is an interface for array-orientated data access and a library that supports the interface. It is composed of 3 components:
* Variables store the data
* Dimensions give relevant dimension information for the variables
* Attributes provide auxiliary information about the variables or dataset itself
NetCDF is used within the atmospheric and oceanic science communities and is network transparent, allowing for it to be accessed by computers that store integers, characters and floating-point numbers.
Iris supports NetCDF files through reading, writing and handling. Iris implements a model based on the CF conventions, giving a format-agnostic interface for working with data.
[Find further support on using Iris with NetCDF files.](https://scitools-iris.readthedocs.io/en/stable/)
## Help us improve the data services we offer
[Join the Met Office research panel](https://forms.office.com/Pages/ResponsePage.aspx?id=YYHxF9cgRkeH_VD-PjtmGdxioYGoFbFIkZuB_q8Fb3VUQkoxRVQzTFdUMzNMVzczWVM5VTc3QTY3MC4u) to help us understand how people interact with weather and climate data, uncover challenges and explore opportunities.
## How to cite
Met Office global deterministic 10km forecast was accessed on DATE from the Microsoft Planetary Computer (https://zenodo.org/records/7261897).
## License
British Crown copyright 2023-2025, the Met Office, is licensed under [CC BY-SA](https://creativecommons.org/licenses/by-sa/4.0/deed.en).
## Providers
[Met Office](https://www.metoffice.gov.uk/)
See all datasets managed by [Met Office.](https://planetarycomputer.microsoft.com/catalog?filter=met+office)
## Contact
[servicedesk@metoffice.gov.uk](mailto:servicedesk@metoffice.gov.uk). Service desk is only available Mon – Fri, 09:00 until 17:00 UTC (-1 hour during BST).
As a non-operational service we aim to respond to any service support enquiries within 3-5 business days.
aster-l1t
This collection offers 14 whole-atmosphere parameters from the Met Office global deterministic 10km forecast. This is a numerical weather prediction forecast for the whole globe, with a resolution of approximately 0.09 degrees i.e. 10km (2,560 x 1,920 grid points).
The data is available as NetCDF files. It's offered on a free, unsupported basis, so we don't recommend using it for any critical business purposes.
## Timesteps
The following timesteps are available:
* every hour from 0 to 54 hours (for most parameters, see parameter table for exceptions)
* every 3 hours from 57 to 144 hours
* every 6 hours from 150 to 168 hours
## Update Frequency
The model is run four times each day, with forecast reference times of 00:00, 06:00, 12:00 and 18:00 (UTC).
The runs at 00:00 and 12:00 provide data for the next 168 hours. The runs at 06:00 and 18:00 provide data for the next 67 hours.
The forecast reference time represents the nominal data time or start time of a model forecast run, rather than the time when the data is available.
## Archive length and latency
As of December 2025, the archive contains data from December 2023 onwards. Forecasts will continue to be available for at least two years from their data date.
The data is typically available 6 hours after the model run time.
## Technical specs
The data is available as NetCDF files. NetCDF (Network Common Data Form) is an interface for array-orientated data access and a library that supports the interface. It is composed of 3 components:
* Variables store the data
* Dimensions give relevant dimension information for the variables
* Attributes provide auxiliary information about the variables or dataset itself
NetCDF is used within the atmospheric and oceanic science communities and is network transparent, allowing for it to be accessed by computers that store integers, characters and floating-point numbers.
Iris supports NetCDF files through reading, writing and handling. Iris implements a model based on the CF conventions, giving a format-agnostic interface for working with data.
[Find further support on using Iris with NetCDF files.](https://scitools-iris.readthedocs.io/en/stable/)
## Help us improve the data services we offer
[Join the Met Office research panel](https://forms.office.com/Pages/ResponsePage.aspx?id=YYHxF9cgRkeH_VD-PjtmGdxioYGoFbFIkZuB_q8Fb3VUQkoxRVQzTFdUMzNMVzczWVM5VTc3QTY3MC4u) to help us understand how people interact with weather and climate data, uncover challenges and explore opportunities.
## How to cite
Met Office global deterministic 10km forecast was accessed on DATE from the Microsoft Planetary Computer (https://zenodo.org/records/7261897).
## License
British Crown copyright 2023-2025, the Met Office, is licensed under [CC BY-SA](https://creativecommons.org/licenses/by-sa/4.0/deed.en).
## Providers
[Met Office](https://www.metoffice.gov.uk/)
See all datasets managed by [Met Office.](https://planetarycomputer.microsoft.com/catalog?filter=met+office)
## Contact
[servicedesk@metoffice.gov.uk](mailto:servicedesk@metoffice.gov.uk). Service desk is only available Mon – Fri, 09:00 until 17:00 UTC (-1 hour during BST).
As a non-operational service we aim to respond to any service support enquiries within 3-5 business days.
[6]:
collection = cat.get_collection("aster-l1t")
collection
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections/aster-l1t Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
[6]:
Items#
The main functions for getting items return iterators, where pystac-client will handle retrieval of additional pages when needed. Note that one request is made for the first ten items, then a second request for the next ten.
[7]:
items = collection.get_items()
# flush stdout so we can see the exact order that things happen
def get_ten_items(items):
for i, item in enumerate(items):
print(f"{i}: {item}", flush=True)
if i == 9:
return
print("First page", flush=True)
get_ten_items(items)
print("Second page", flush=True)
get_ten_items(items)
First page
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections/aster-l1t/items?collections=aster-l1t Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
0: <Item id=AST_L1T_00312272006020322_20150518201805>
1: <Item id=AST_L1T_00312272006020313_20150518201753>
2: <Item id=AST_L1T_00312272006020304_20150518201753>
3: <Item id=AST_L1T_00312272006020256_20150518201805>
4: <Item id=AST_L1T_00312272006013731_20150518201743>
5: <Item id=AST_L1T_00312272006013722_20150518201738>
6: <Item id=AST_L1T_00312272006013714_20150518201738>
7: <Item id=AST_L1T_00312272006013705_20150517144401>
8: <Item id=AST_L1T_00312272006013656_20150517144401>
9: <Item id=AST_L1T_00312272006013647_20150517144406>
Second page
DEBUG:pystac_client.stac_api_io:GET https://planetarycomputer.microsoft.com/api/stac/v1/collections/aster-l1t/items?collections=aster-l1t&token=next:aster-l1t:AST_L1T_00312272006013647_20150517144406 Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
0: <Item id=AST_L1T_00312272006013638_20150517144356>
1: <Item id=AST_L1T_00312272006013629_20150517144344>
2: <Item id=AST_L1T_00312272006013620_20150517144344>
3: <Item id=AST_L1T_00312272006013612_20150517144400>
4: <Item id=AST_L1T_00312272006013603_20150517144346>
5: <Item id=AST_L1T_00312272006013554_20150517144346>
6: <Item id=AST_L1T_00312272006013545_20150517144346>
7: <Item id=AST_L1T_00312272006013536_20150517144340>
8: <Item id=AST_L1T_00312272006013527_20150517144346>
9: <Item id=AST_L1T_00312272006013519_20150517144336>
API Search#
If the Catalog is an API, we have the ability to search for items based on spatio-temporal properties.
[8]:
# AOI around Delfzijl, in northern Netherlands
geom = {
"type": "Polygon",
"coordinates": [
[
[6.42425537109375, 53.174765470134616],
[7.344360351562499, 53.174765470134616],
[7.344360351562499, 53.67393435835391],
[6.42425537109375, 53.67393435835391],
[6.42425537109375, 53.174765470134616],
]
],
}
# limit sets the # of items per page so we can see multiple pages getting fetched
search = cat.search(
max_items=15,
limit=5,
collections="aster-l1t",
intersects=geom,
datetime="2000-01-01/2010-12-31",
)
items = list(search.items())
len(items)
DEBUG:pystac_client.stac_api_io:POST https://planetarycomputer.microsoft.com/api/stac/v1/search Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '353', 'Content-Type': 'application/json'} Payload: {"limit": 5, "datetime": "2000-01-01T00:00:00Z/2010-12-31T23:59:59Z", "collections": ["aster-l1t"], "intersects": {"type": "Polygon", "coordinates": [[[6.42425537109375, 53.174765470134616], [7.344360351562499, 53.174765470134616], [7.344360351562499, 53.67393435835391], [6.42425537109375, 53.67393435835391], [6.42425537109375, 53.174765470134616]]]}}
DEBUG:pystac_client.stac_api_io:POST https://planetarycomputer.microsoft.com/api/stac/v1/search Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '421', 'Content-Type': 'application/json'} Payload: {"limit": 5, "datetime": "2000-01-01T00:00:00Z/2010-12-31T23:59:59Z", "collections": ["aster-l1t"], "intersects": {"type": "Polygon", "coordinates": [[[6.42425537109375, 53.174765470134616], [7.344360351562499, 53.174765470134616], [7.344360351562499, 53.67393435835391], [6.42425537109375, 53.67393435835391], [6.42425537109375, 53.174765470134616]]]}, "token": "next:aster-l1t:AST_L1T_00307032006103813_20150515031748"}
DEBUG:pystac_client.stac_api_io:POST https://planetarycomputer.microsoft.com/api/stac/v1/search Headers: {'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '421', 'Content-Type': 'application/json'} Payload: {"limit": 5, "datetime": "2000-01-01T00:00:00Z/2010-12-31T23:59:59Z", "collections": ["aster-l1t"], "intersects": {"type": "Polygon", "coordinates": [[[6.42425537109375, 53.174765470134616], [7.344360351562499, 53.174765470134616], [7.344360351562499, 53.67393435835391], [6.42425537109375, 53.67393435835391], [6.42425537109375, 53.174765470134616]]]}, "token": "next:aster-l1t:AST_L1T_00305302006105006_20150514150004"}
[8]:
15
[9]:
# note that this will work in JupyterLab, but not in a Jupyter Notebook
import IPython.display
IPython.display.JSON([i.to_dict() for i in items])
[9]:
<IPython.core.display.JSON object>
[10]:
# this cell can be used in Jupyter Notebook. Use above if using JupyterLab
import json
import uuid
from IPython.display import display_html, display_javascript
class RenderJSON(object):
def __init__(self, json_data):
if isinstance(json_data, dict) or isinstance(json_data, list):
self.json_str = json.dumps(json_data)
else:
self.json_str = json_data
self.uuid = str(uuid.uuid4())
def _ipython_display_(self):
display_html(
'<div id="{}" style="height: 600px; width:100%;font: 12px/18px monospace '
'!important;"></div>'.format(self.uuid),
raw=True,
)
display_javascript(
"""
require(["https://rawgit.com/caldwell/renderjson/master/renderjson.js"],
function() {
renderjson.set_show_to_level(2);
document.getElementById('%s').appendChild(renderjson(%s))
});
"""
% (self.uuid, self.json_str),
raw=True,
)
RenderJSON([i.to_dict() for i in items])