USGS Landsat meta data is available in 3 formats: CSV, XML, Parquet. None of these are spatially indexed, which means you can't quickly get a sub-dataset or bounding box of the metadata. The following shows how to convert the Parquet data into a spatially indexed FlatBeoBuf (FGB) vector format for fast BBox retrieval, either locally or over the internet, no API required. As of this writing, over 2.3 million metadata entries exist.
In the example below we'll be working with the U.S. Landsat Analysis Ready Data (ARD) metadata (1982 to present). It's basically 3 steps:
It will be ready for querying by bbox and other columns, either locally or across the internet. Let's start by processing the parquet into FGB:
# Note, if you're copy/pasting this, remove all lines with a '#' sign.
# Download ARD Parquet
wget -q https://landsat.usgs.gov/landsat/metadata_service/bulk_metadata_files/LANDSAT_ARD_TILE_C2.parquet.gz -O LANDSAT_ARD_TILE_C2.parquet.gz \
&& gdal pipeline \
! read /vsigzip/LANDSAT_ARD_TILE_C2.parquet.gz \
! rename-layer --output-layer landsat \
! sql --dialect sqlite --sql="$(cat<<EOF
select
*
# create a date column as integer for easy filtering
,cast(replace(acquisitionDate,'-','') as integer) as date
# build the polygon from values
,st_multi(st_geomfromtext(format('POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))'
,upperLeftCornerLongitude
,upperLeftCornerLatitude
,upperRightCornerLongitude
,upperRightCornerLatitude
,lowerRightCornerLongitude
,lowerRightCornerLatitude
,lowerLeftCornerLongitude
,lowerLeftCornerLatitude
,upperLeftCornerLongitude
,upperLeftCornerLatitude
), 4326)) as geom
from landsat
EOF
)" \
# write it as FGB
! write -o LANDSAT_ARD_TILE_C2.fgb --output-layer LANDSAT_ARD_TILE_C2 --overwrite \
# ...and zip it
&& sozip --overwrite LANDSAT_ARD_TILE_C2.fgb.zip LANDSAT_ARD_TILE_C2.fgb \
&& rm LANDSAT_ARD_TILE_C2.fgb
Now that we have a spatially indexed FGB file, selecting metadata by bounding box and date range is simple and fast. The following gets ARD metadata for the Crescent City, CA area, between May 1, 2026 and July 30, 2026 that has cloud cover of less than 5%:
gdal pipeline \
! read /vsizip/LANDSAT_ARD_TILE_C2.fgb.zip \
! rename-layer --output-layer landsat \
! sql --dialect sqlite --sql "$(cat<<EOF
select
date
,cloudCover
,browseURL
,cartURL
,geometry
from landsat
where st_intersects(st_envelope(st_geomfromtext('LINESTRING(-124.274 41.739,-124.039 41.831)', 4326)), geometry)
and date between 20260501 and 20260730
and cloudCover < 5
EOF
)" \
! write -o result.gpkg --overwrite --output-layer landsat
This will return 13 rows, which look like:
gdal vector info result.gpkg --features ... OGRFeature(landsat):1 date (Integer) = 20260612 cloudCover (Real) = 0.0001 browseURL (String) = https://landsatlook.usgs.gov/gen-browse?size=rrb&type=t_refl&product_id=LC08_CU_002005_20260612_20260622_02 cartURL (String) = https://earthexplorer.usgs.gov/download/external/options/LANDSAT_ARD_TILE_C2/LC08_CU_002005_20260612_20260622_02/M2M MULTIPOLYGON (((-124.36375 43.39616,-122.08121 43.39616,-122.08121 41.72982,-124.36375 41.72982,-124.36375 43.39616))) OGRFeature(landsat):2 date (Integer) = 20260622 cloudCover (Real) = 0.0008 browseURL (String) = https://landsatlook.usgs.gov/gen-browse?size=rrb&type=t_refl&product_id=LC09_CU_002005_20260622_20260626_02 cartURL (String) = https://earthexplorer.usgs.gov/download/external/options/LANDSAT_ARD_TILE_C2/LC09_CU_002005_20260622_20260626_02/M2M MULTIPOLYGON (((-124.36375 43.39616,-122.08121 43.39616,-122.08121 41.72982,-124.36375 41.72982,-124.36375 43.39616))) OGRFeature(landsat):3 date (Integer) = 20260614 cloudCover (Real) = 0.0013 browseURL (String) = https://landsatlook.usgs.gov/gen-browse?size=rrb&type=t_refl&product_id=LC08_CU_002005_20260614_20260622_02 cartURL (String) = https://earthexplorer.usgs.gov/download/external/options/LANDSAT_ARD_TILE_C2/LC08_CU_002005_20260614_20260622_02/M2M MULTIPOLYGON (((-124.36375 43.39616,-122.08121 43.39616,-122.08121 41.72982,-124.36375 41.72982,-124.36375 43.39616))) ...
That's it!