Landsat 8-9 Level-2
Available now Collection id landsat-ot-l2. Aliases include landsat-c2-l2 (Earth Search and Planetary Computer, here limited to Landsat 8 and 9) and LOTL2 (Sentinel Hub), matched without regard to case.
Overview
Section titled “Overview”Landsat 8 and 9 each carry the Operational Land Imager (OLI, or OLI-2 on Landsat 9), with nine reflective bands, and the Thermal Infrared Sensor (TIRS, TIRS-2), with two thermal bands. USGS processes their scenes to Collection 2 Level-2: surface reflectance for the seven OLI bands below 2.3 µm, and surface temperature from TIRS band 10, plus quality layers.
satellitedata.net finds scenes through Element 84’s Earth Search landsat-c2-l2 index and reads the files from the USGS usgs-landsat bucket in AWS us-west-2. That bucket is requester-pays: the API pays for its own reads, and asset href values in items are s3://usgs-landsat/... URLs, so a client that reads them directly pays its own request charges. Earth Search’s index also holds Landsat 4, 5 and 7; this collection returns Landsat 8 and 9 only. Item ids are USGS product ids, for example LC09_L2SP_198032_20260825_02_T1.
Mission references: USGS pages on Landsat 8, Landsat 9, Collection 2 Level-2 science products and quality assessment bands.
Basic facts
Section titled “Basic facts”| Satellites | Landsat 8 (launched February 2013) and Landsat 9 (launched September 2021); item platform is landsat-8 or landsat-9 |
| Spatial resolution | 30 m for every band in Level-2; thermal is acquired at 100 m and resampled to 30 m |
| Revisit | 16 days per satellite, 8 days with both |
| Swath | 185 km |
| Tiling | WRS-2 path and row; one item per scene, such as path 198 row 032 (grid:code = WRS2-198032) |
| Processing level | Collection 2 Level-2: surface reflectance (LaSRC) and surface temperature |
| Temporal extent in the API | From 18 March 2013, as the live collection document reports |
| Zoom range for tiles | 7 to 14 |
| Credit line | Landsat data courtesy of the U.S. Geological Survey |
Spectral and thermal bands, with the wavelengths and bandwidths Earth Search publishes in each item’s eo:bands:
| Band | Common name | Earth Search asset | Also accepted | Centre (µm) | Bandwidth (µm) | Resolution | Type | Scale | Offset | Nodata |
|---|---|---|---|---|---|---|---|---|---|---|
| SR_B1 | coastal |
coastal |
B01 |
0.44 | 0.02 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| SR_B2 | blue |
blue |
B02 |
0.48 | 0.06 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| SR_B3 | green |
green |
B03 |
0.56 | 0.06 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| SR_B4 | red |
red |
B04 |
0.65 | 0.04 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| SR_B5 | nir08 |
nir08 |
B05 |
0.87 | 0.03 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| SR_B6 | swir16 |
swir16 |
B06 |
1.61 | 0.09 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| SR_B7 | swir22 |
swir22 |
B07 |
2.20 | 0.19 | 30 m | uint16 | 0.0000275 | −0.2 | 0 |
| ST_B10 | lwir11 |
lwir11 |
B10 |
10.9 | 0.59 | 30 m (100 m native) | uint16 | 0.00341802 | 149.0 | 0 |
ST_B10 converts to kelvin, not reflectance. OLI’s panchromatic band 8 and cirrus band 9, and TIRS band 11, are not in Level-2; they are in Level-1, which is coming later.
Quality and surface temperature auxiliary layers:
| Layer | Earth Search asset | Also accepted | Content | Type | Scale | Offset | Nodata |
|---|---|---|---|---|---|---|---|
| QA_PIXEL | qa_pixel |
BQA |
Pixel quality bit field (bits) | uint16 | none | 0 | 1 |
| QA_RADSAT | qa_radsat |
Saturation and terrain occlusion bit field (bits) | uint16 | none | 0 | none | |
| SR_QA_AEROSOL | qa_aerosol |
Aerosol retrieval quality bit field | uint8 | none | 0 | 1 | |
| ST_QA | qa |
lwir_qa |
Surface temperature uncertainty, K | int16 | 0.01 | 0 | −9999 |
| ST_CDIST | cdist |
Distance to the nearest cloud, km | int16 | 0.01 | 0 | −9999 | |
| ST_ATRAN | atran |
Atmospheric transmittance | int16 | 0.0001 | 0 | −9999 | |
| ST_EMIS | emis |
Emissivity of band 10 | int16 | 0.0001 | 0 | −9999 | |
| ST_EMSD | emsd |
Emissivity standard deviation | int16 | 0.0001 | 0 | −9999 | |
| ST_DRAD, ST_URAD, ST_TRAD | drad, urad, trad |
Downwelled, upwelled and thermal radiance, W/(m² sr µm) | int16 | 0.001 | 0 | −9999 |
Every layer is 30 m. In POST /v1/process, a band resolves by native name, common name, asset name or alias, without regard to case, with one gap: the three radiance layers and ST_EMSD resolve by asset name only (drad, urad, trad, emsd). The GeoTIFF’s band descriptions are the asset names.
Landsat has one near-infrared band, SR_B5, so nir fails here with unknown-band and a hint to use nir08. That makes a request written with nir08 work on Sentinel-2 and Landsat alike.
Scenes without thermal bands
Section titled “Scenes without thermal bands”Some scenes are processed to surface reflectance only, marked L2SR in the id (LC09_L2SR_086110_20261011_02_T2) and landsat:correction: L2SR in the properties, instead of L2SP. USGS does this when it lacks the inputs for surface temperature. These scenes have no lwir11 and none of the ST layers, and asking for one answers 422 unknown-band. Prefer lwir11 over ST_B10 in code: the common name will survive renaming in a later Landsat collection.
Units and values
Section titled “Units and values”surface reflectance = DN × 0.0000275 − 0.2surface temperature (K) = DN × 0.00341802 + 149.0The same scale and offset apply to Landsat 8 and 9 and to every date: Landsat has no baseline offset like Sentinel-2’s, so harmonize: "offset_removed" changes nothing here.
units |
Spectral bands | lwir11 |
Sample type |
|---|---|---|---|
REFLECTANCE (default) |
reflectance | kelvin | FLOAT32, nodata NaN |
DN |
stored integers, with scale and offset in the GeoTIFF | stored integers | UINT16, nodata 0 |
- With
REFLECTANCEthe ST auxiliary layers get their scale too: ST_CDIST in km, ST_QA in K, ST_ATRAN and ST_EMIS as fractions. The QA bit fields always come back as stored. clipNegative: truesets reflectance below zero to zero. It has no effect withDN, which is never negative.output.sampleType, if given, must beFLOAT32forREFLECTANCEandUINT16forDN.KELVINandCELSIUSas separate units are coming later.
Valid surface reflectance DN run from 7,273 (reflectance 0) to 43,636 (reflectance 1). The true-color preset stretches DN 7,273 to 18,182, which is reflectance 0 to 0.3.
Cloud and quality
Section titled “Cloud and quality”Items carry eo:cloud_cover for the whole scene and landsat:cloud_cover_land for its land part. Filter scenes on eo:cloud_cover as for Sentinel-2.
QA_PIXEL bits
Section titled “QA_PIXEL bits”QA_PIXEL is a 16-bit field from the CFMask algorithm. Test a bit with (qa >> bit) & 1, and a two-bit confidence with (qa >> bit) & 3.
| Bit | Meaning when set |
|---|---|
| 0 | Fill, no data |
| 1 | Dilated cloud |
| 2 | Cirrus (high confidence) |
| 3 | Cloud |
| 4 | Cloud shadow |
| 5 | Snow |
| 6 | Clear: set when neither the cloud bit nor the dilated cloud bit is set |
| 7 | Water |
| 8-9 | Cloud confidence |
| 10-11 | Cloud shadow confidence |
| 12-13 | Snow and ice confidence |
| 14-15 | Cirrus confidence |
Confidence values: 0 none, 1 low, 2 medium, 3 high. For snow and ice, and for cirrus, USGS uses 0 none, 1 low and 3 high (2 is reserved). Two values come up often: 21824 is clear land and 21952 is clear water, both with low confidence in every class.
A strict clear-pixel test keeps pixels where bit 6 is set and bits 0 to 4 are clear:
import numpy as np
clear = ((qa >> 6) & 1).astype(bool) & ((qa & 0b11111) == 0)QA_RADSAT bits
Section titled “QA_RADSAT bits”| Bit | Meaning when set |
|---|---|
| 0 to 6 | Band 1 to band 7 saturated (bit 0 is band 1) |
| 7 | Not used |
| 8 | Band 9 saturated |
| 9-10 | Not used |
| 11 | Terrain occlusion |
| 12-15 | Not used |
A pixel saturated in a band you use holds a clipped value; drop it from quantitative work.
Lean v1 applies no pixel cloud mask: tiles and previews show every pixel of the scenes they mosaic, and processing.cloudMask is rejected. The unified cloud mask, built from QA_PIXEL with a choice of confidence level, is planned.
Data availability
Section titled “Data availability”- Landsat 8 from March 2013 and Landsat 9 from late 2021, onwards, as Earth Search indexes them.
- Both Tier 1 and Tier 2 scenes are in the collection. Tier 1 meets USGS’s geometric accuracy requirement; Tier 2 does not, often because of cloud. For Tier 1 only, add
"landsat:collection_category": {"eq": "T1"}toquery. - New scenes appear once USGS publishes the Level-2 product. The example scene of 25 August 2026 was in the index the next day.
- Before relying on a period, run
/v1/searchfor your area and dates.
Collection-specific parameters
Section titled “Collection-specific parameters”Search
Section titled “Search”These properties work in the query extension:
| Property | Example | Notes |
|---|---|---|
eo:cloud_cover |
{"eo:cloud_cover": {"lte": 20}} |
Whole-scene cloud percentage |
platform |
{"platform": {"in": ["landsat-9"]}} |
Only landsat-8 and landsat-9 match in this collection |
landsat:wrs_path, landsat:wrs_row |
{"landsat:wrs_path": {"eq": "198"}, "landsat:wrs_row": {"eq": "032"}} |
Strings with leading zeros |
landsat:collection_category |
{"landsat:collection_category": {"eq": "T1"}} |
T1 or T2 |
Presets
Section titled “Presets”From GET /v1/presets, the web map’s recipes for this collection:
| Preset | Assets | Stretch |
|---|---|---|
true-color |
red, green, blue |
DN 7,273 to 18,182, reflectance 0 to 0.3 |
false-color |
nir08, red, green |
DN 7,273 to 21,818, reflectance 0 to 0.4 |
ndvi |
nir08, red |
(b1 - b2) / (b1 + b2 - 14545.45), −0.2 to 0.8 on rdylgn |
The NDVI expression removes both −0.2 offsets (2 × 7,272.7 DN) from the denominator, so Landsat NDVI matches Sentinel-2’s.
Tiles exist from zoom 7 to 14. Outside that range a tile answers 400 zoom-out-of-range.
Examples
Section titled “Examples”Routes marked anonymous need no key; the others send X-API-Key. The area is west of Tarragona, Spain, on WRS-2 path 198, row 032.
Search one area and month
Section titled “Search one area and month”curl -s -X POST https://api.satellitedata.net/v1/search \ -H "X-API-Key: $SDN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "collections": ["landsat-ot-l2"], "bbox": [0.7, 40.7, 0.8, 40.8], "datetime": "2026-08-01T00:00:00Z/2026-08-31T23:59:59Z", "limit": 10, "query": {"eo:cloud_cover": {"lte": 20}} }'import osimport requests
API = "https://api.satellitedata.net/v1"headers = {"X-API-Key": os.environ["SDN_API_KEY"]}
body = { "collections": ["landsat-ot-l2"], "bbox": [0.7, 40.7, 0.8, 40.8], "datetime": "2026-08-01T00:00:00Z/2026-08-31T23:59:59Z", "limit": 10, "query": {"eo:cloud_cover": {"lte": 20}},}page = requests.post(f"{API}/search", json=body, headers=headers).json()for item in page["features"]: p = item["properties"] print(item["id"], p["platform"], p["eo:cloud_cover"], p["landsat:correction"])One item
Section titled “One item”Anonymous.
curl -s https://api.satellitedata.net/v1/collections/landsat-ot-l2/items/LC09_L2SP_198032_20260825_02_T1A Landsat 5 or 7 id under this collection answers 404 not-found.
Preview image
Section titled “Preview image”curl -s -o preview.jpg \ -H "X-API-Key: $SDN_API_KEY" \ "https://api.satellitedata.net/v1/collections/landsat-ot-l2/items/LC09_L2SP_198032_20260825_02_T1/preview.jpg?preset=false-color&max_size=256"TileJSON and a tile URL for MapLibre
Section titled “TileJSON and a tile URL for MapLibre”The TileJSON is anonymous; the tiles need the key.
curl -s "https://api.satellitedata.net/v1/tiles/ndvi/WebMercatorQuad/tilejson.json?collection=landsat-ot-l2&datetime=2026-08-01/2026-08-31&maxCloudCoverage=40"{ "tilejson": "3.0.0", "name": "ndvi", "attribution": "Landsat data courtesy of the U.S. Geological Survey", "tiles": ["https://api.satellitedata.net/v1/tiles/ndvi/WebMercatorQuad/{z}/{x}/{y}.png?collection=landsat-ot-l2&datetime=2026-08-01%2F2026-08-31&maxCloudCoverage=40"], "minzoom": 7, "maxzoom": 14}(Shortened.) One tile at zoom 11:
curl -s -o tile.png -H "X-API-Key: $SDN_API_KEY" \ "https://api.satellitedata.net/v1/tiles/ndvi/WebMercatorQuad/11/1027/767.png?collection=landsat-ot-l2&datetime=2026-08-01%2F2026-08-31&maxCloudCoverage=40"For a MapLibre page, see the Quickstart; set the collection to landsat-ot-l2 and start at zoom 7 or more.
Reflectance and temperature as a GeoTIFF
Section titled “Reflectance and temperature as a GeoTIFF”red, nir08 and lwir11 over a 2 km box, as float32: reflectance for the first two, kelvin for the third.
curl -s -L -X POST https://api.satellitedata.net/v1/process \ -H "X-API-Key: $SDN_API_KEY" \ -H "Content-Type: application/json" \ -o landsat.tif \ -d '{ "input": { "bounds": {"bbox": [0.73815, 40.71096, 0.76185, 40.72904]}, "data": [{ "type": "landsat-ot-l2", "dataFilter": {"timeRange": {"from": "2026-08-01T00:00:00Z", "to": "2026-08-31T23:59:59Z"}} }] }, "bands": ["red", "nir08", "lwir11"], "output": {"width": 200, "height": 150, "crs": "EPSG:32631"} }'import ioimport osimport numpy as npimport rasterioimport requests
API = "https://api.satellitedata.net/v1"headers = {"X-API-Key": os.environ["SDN_API_KEY"]}
body = { "input": { "bounds": {"bbox": [0.73815, 40.71096, 0.76185, 40.72904]}, "data": [{ "type": "landsat-ot-l2", "dataFilter": {"timeRange": {"from": "2026-08-01T00:00:00Z", "to": "2026-08-31T23:59:59Z"}}, }], }, "bands": ["red", "nir08", "lwir11"], "output": {"width": 200, "height": 150, "crs": "EPSG:32631"},}r = requests.post(f"{API}/process", json=body, headers=headers)r.raise_for_status()with rasterio.open(io.BytesIO(r.content)) as ds: red, nir, lst = ds.read()ndvi = (nir - red) / (nir + red)print("mean surface temperature", np.nanmean(lst) - 273.15, "°C")Without mosaickingOrder the most recent scenes in the month come first. Add "maxCloudCoverage": 20 and "mosaickingOrder": "leastCC" to the dataFilter for the clearest scene instead. If the chosen scene is an L2SR one, the request fails with unknown-band for lwir11.
Stored integers
Section titled “Stored integers”The same request with "processing": {"units": "DN", "harmonize": "none"} in the data source returns UINT16 DN, with the scale and offset written into the GeoTIFF for GDAL and rasterio.
A larger area as a job
Section titled “A larger area as a job”The same body as for Sentinel-2, with a Landsat scene id:
curl -s -i -X POST https://api.satellitedata.net/v1/process/jobs \ -H "X-API-Key: $SDN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "bounds": {"bbox": [0.63147, 40.62956, 0.86853, 40.81044]}, "data": [{ "type": "landsat-ot-l2", "dataFilter": {"ids": ["LC09_L2SP_198032_20260825_02_T1"]} }] }, "bands": ["blue", "green", "red", "nir08", "swir16", "swir22"], "output": {"width": 3000, "height": 3000, "crs": "EPSG:32631"} }'
curl -s -H "X-API-Key: $SDN_API_KEY" https://api.satellitedata.net/v1/process/jobs/$JOB_IDPoll until status is done, then download each files[].url within the hour.