How to use the Compatibility API¶
The /compatibility endpoint helps you understand how to call TiTiler-CMR for a collection. It returns the sampled granule_ur and accepts granule_ur when you want to inspect a specific granule. For hierarchical HDF5 and NetCDF datasets it can return a lightweight list of candidate group paths from the root request, then let you inspect variables by making a follow-up request with group=....
This notebook demonstrates that workflow for the NISAR Beta Geocoded Polarimetric Covariance (GCOV) collection, which is a known example of a hierarchical HDF5 dataset that needs a group parameter for xarray requests.
Setup¶
import json
import os
import earthaccess
import httpx2 as httpx
titiler_endpoint = os.getenv(
"TITILER_CMR_ENDPOINT", "https://staging.openveda.cloud/api/titiler-cmr"
)
Identify the dataset¶
You can find the collection with earthaccess.search_datasets.
datasets = earthaccess.search_datasets(concept_id="C3622214170-ASF")
ds = datasets[0]
collection_concept_id = ds["meta"]["concept-id"]
print("CollectionConcept-Id:", collection_concept_id)
print("Short name:", ds["umm"]["ShortName"])
print("Version:", ds["umm"]["Version"])
CollectionConcept-Id: C3622214170-ASF Short name: NISAR_L2_GCOV_BETA_V1 Version: 1
Explore the collection using /compatibility¶
compatibility_response = httpx.get(
f"{titiler_endpoint}/compatibility",
params={"collection_concept_id": collection_concept_id},
timeout=None,
).json()
print(json.dumps(compatibility_response, indent=2))
{
"concept_id": "C3622214170-ASF",
"backend": "xarray",
"datetime": [
{
"RangeDateTimes": [
{
"BeginningDateTime": "2025-10-01T00:00:00.000Z"
}
],
"EndsAtPresentFlag": true
}
],
"variables": {},
"dimensions": {},
"coordinates": {},
"compatible_groups": [
"science/LSAR/GCOV/grids/frequencyA",
"science/LSAR/GCOV/grids/frequencyB"
],
"tileable_variables": [],
"incompatible_variables": {},
"granule_ur": "NISAR_L2_PR_GCOV_002_109_D_063_4005_DHDH_A_20251012T182508_20251012T182531_X05010_N_P_J_001",
"example_assets": "s3://sds-n-cumulus-prod-nisar-products/NISAR_L2_GCOV_BETA_V1/NISAR_L2_PR_GCOV_002_109_D_063_4005_DHDH_A_20251012T182508_20251012T182531_X05010_N_P_J_001/NISAR_L2_PR_GCOV_002_109_D_063_4005_DHDH_A_20251012T182508_20251012T182531_X05010_N_P_J_001.h5",
"sample_asset_raster_info": null,
"links": []
}
Interpret the response¶
For hierarchical datasets, a root /compatibility request stays lightweight. The most important fields are:
granule_ur: the sampled granule used for this compatibility checkcompatible_groups: candidate group paths to try in a follow-up request
At this stage the response may not include variable metadata or xarray links yet, because TiTiler-CMR has not inspected a nested group.
compatible_groups = compatibility_response.get("compatible_groups", [])
variables = list((compatibility_response.get("variables") or {}).keys())
tilejson_link = next(
(
link["href"]
for link in compatibility_response.get("links", [])
if link["rel"] == "tilejson"
),
None,
)
print("compatible_groups:")
for group in compatible_groups:
print(" -", group)
compatible_groups: - science/LSAR/GCOV/grids/frequencyA - science/LSAR/GCOV/grids/frequencyB
Inspect a specific group¶
Once you choose a group from compatible_groups, make a follow-up /compatibility request with group=... to inspect variables and get xarray links.
selected_group = compatible_groups[1]
grouped_compatibility_response = httpx.get(
f"{titiler_endpoint}/compatibility",
params={
"collection_concept_id": collection_concept_id,
"group": selected_group,
},
timeout=30,
).json()
print(json.dumps(grouped_compatibility_response, indent=2))
{
"concept_id": "C3622214170-ASF",
"backend": "xarray",
"datetime": [
{
"RangeDateTimes": [
{
"BeginningDateTime": "2025-10-01T00:00:00.000Z"
}
],
"EndsAtPresentFlag": true
}
],
"variables": {
"HHHH": {
"shape": [
4590,
4635
],
"dtype": "float32",
"min": 1.0176728166763627e-15,
"max": 161.939453125,
"mean": 0.24553754925727844,
"p01": 4.8098032718116305e-15,
"p05": 1.2278231816681419e-14,
"p95": 0.5810504913330078,
"p99": 1.084585723876953
},
"HVHV": {
"shape": [
4590,
4635
],
"dtype": "float32",
"min": 3.7021099619054254e-16,
"max": 27.40234375,
"mean": 0.053082797676324844,
"p01": 1.0660350098327942e-15,
"p05": 2.527668287351237e-15,
"p95": 0.12816238403320312,
"p99": 0.1995861053466802
},
"listOfCovarianceTerms": {
"shape": [
2
],
"dtype": "|S4",
"min": null,
"max": null,
"mean": null,
"p01": null,
"p05": null,
"p95": null,
"p99": null
},
"listOfPolarizations": {
"shape": [
2
],
"dtype": "|S2",
"min": null,
"max": null,
"mean": null,
"p01": null,
"p05": null,
"p95": null,
"p99": null
},
"mask": {
"shape": [
4590,
4635
],
"dtype": "float32",
"min": 0.0,
"max": 3.0,
"mean": 1.7087618112564087,
"p01": 0.0,
"p05": 0.0,
"p95": 3.0,
"p99": 3.0
},
"numberOfLooks": {
"shape": [
4590,
4635
],
"dtype": "float32",
"min": 0.09790978580713272,
"max": 72.7900619506836,
"mean": 36.88620376586914,
"p01": 20.620860862731934,
"p05": 27.69395923614502,
"p95": 45.419249534606934,
"p99": 50.936746597290046
},
"numberOfSubSwaths": {
"shape": [],
"dtype": "uint8",
"min": 3.0,
"max": 3.0,
"mean": 3.0,
"p01": 3.0,
"p05": 3.0,
"p95": 3.0,
"p99": 3.0
},
"rtcGammaToSigmaFactor": {
"shape": [
4590,
4635
],
"dtype": "float32",
"min": 0.030899126082658768,
"max": 0.9858595132827759,
"mean": 0.744823694229126,
"p01": 0.513236579298973,
"p05": 0.6084782421588898,
"p95": 0.8582949250936508,
"p99": 0.9093189173936843
},
"xCoordinateSpacing": {
"shape": [],
"dtype": "float64",
"min": 80.0,
"max": 80.0,
"mean": 80.0,
"p01": 80.0,
"p05": 80.0,
"p95": 80.0,
"p99": 80.0
},
"yCoordinateSpacing": {
"shape": [],
"dtype": "float64",
"min": -80.0,
"max": -80.0,
"mean": -80.0,
"p01": -80.0,
"p05": -80.0,
"p95": -80.0,
"p99": -80.0
}
},
"dimensions": {
"yCoordinates": 4590,
"xCoordinates": 4635,
"phony_dim_5": 2
},
"coordinates": {
"projection": {
"size": 1,
"dtype": "uint32",
"min": 32633.0,
"max": 32633.0
},
"xCoordinates": {
"size": 4635,
"dtype": "float64",
"min": 148360.0,
"max": 519080.0
},
"yCoordinates": {
"size": 4590,
"dtype": "float64",
"min": 5392840.0,
"max": 5759960.0
}
},
"compatible_groups": null,
"tileable_variables": [
"HHHH",
"HVHV",
"mask",
"numberOfLooks",
"rtcGammaToSigmaFactor"
],
"incompatible_variables": {
"listOfCovarianceTerms": {
"dims": [
"phony_dim_5"
],
"reason": "missing recognized x/y dimensions"
},
"listOfPolarizations": {
"dims": [
"phony_dim_5"
],
"reason": "missing recognized x/y dimensions"
},
"numberOfSubSwaths": {
"dims": [],
"reason": "missing recognized x/y dimensions"
},
"xCoordinateSpacing": {
"dims": [],
"reason": "missing recognized x/y dimensions"
},
"yCoordinateSpacing": {
"dims": [],
"reason": "missing recognized x/y dimensions"
}
},
"granule_ur": "NISAR_L2_PR_GCOV_002_109_D_063_4005_DHDH_A_20251012T182508_20251012T182531_X05010_N_P_J_001",
"example_assets": "s3://sds-n-cumulus-prod-nisar-products/NISAR_L2_GCOV_BETA_V1/NISAR_L2_PR_GCOV_002_109_D_063_4005_DHDH_A_20251012T182508_20251012T182531_X05010_N_P_J_001/NISAR_L2_PR_GCOV_002_109_D_063_4005_DHDH_A_20251012T182508_20251012T182531_X05010_N_P_J_001.h5",
"sample_asset_raster_info": null,
"links": [
{
"rel": "tilejson",
"href": "https://v4jec6i5c0.execute-api.us-west-2.amazonaws.com/xarray/WebMercatorQuad/tilejson.json?collection_concept_id=C3622214170-ASF&variables=HHHH&group=science/LSAR/GCOV/grids/frequencyB&temporal={temporal}",
"title": "TileJSON",
"type": "application/json"
},
{
"rel": "map",
"href": "https://v4jec6i5c0.execute-api.us-west-2.amazonaws.com/xarray/WebMercatorQuad/map.html?collection_concept_id=C3622214170-ASF&variables=HHHH&group=science/LSAR/GCOV/grids/frequencyB&temporal={temporal}",
"title": "Map viewer",
"type": "text/html"
},
{
"rel": "tile",
"href": "https://v4jec6i5c0.execute-api.us-west-2.amazonaws.com/xarray/tiles/WebMercatorQuad/{z}/{x}/{y}?collection_concept_id=C3622214170-ASF&variables=HHHH&group=science/LSAR/GCOV/grids/frequencyB&temporal={temporal}",
"title": "Map tile",
"type": "image/png"
}
]
}
group_variables = list((grouped_compatibility_response.get("variables") or {}).keys())
group_tilejson_link = next(
link["href"]
for link in grouped_compatibility_response.get("links", [])
if link["rel"] == "tilejson"
)
print("selected group:", selected_group)
print("variables in selected group (first 10):", group_variables[:10])
print("tilejson link for selected group:", group_tilejson_link)
selected group: science/LSAR/GCOV/grids/frequencyB
variables in selected group (first 10): ['HHHH', 'HVHV', 'listOfCovarianceTerms', 'listOfPolarizations', 'mask', 'numberOfLooks', 'numberOfSubSwaths', 'rtcGammaToSigmaFactor', 'xCoordinateSpacing', 'yCoordinateSpacing']
tilejson link for selected group: https://v4jec6i5c0.execute-api.us-west-2.amazonaws.com/xarray/WebMercatorQuad/tilejson.json?collection_concept_id=C3622214170-ASF&variables=HHHH&group=science/LSAR/GCOV/grids/frequencyB&temporal={temporal}
For a grouped dataset like NISAR GCOV, the workflow is:
- Call
/compatibilitywithoutgroupto get candidatecompatible_groups. - Choose one of those group paths.
- Call
/compatibilityagain withgroup=...to inspect variables and dimensions for that group. - Pick a variable name from
variables. - Reuse the returned xarray links from the grouped response.
That keeps the root compatibility response lightweight while still giving you a practical path to the next xarray request.