Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Use case area: Energy and Industrial
Industrial facilities contain many processes that generate, transfer, or release heat. While these processes may be difficult to distinguish in visible imagery alone, thermal imagery provides an effective way to observe activity across a site and over time.
By comparing thermal observations temporally, we can identify changes in heat signatures associated with operational units, flaring, water discharge, and other industrial processes.
The images below show Hydrosat longwave infrared observations of the Mina Al-Ahmadi and Mina Abdullah refinery sites in Kuwait.
At first glance, visible imagery provides useful context about the layout of the facilities. Thermal imagery adds another layer of information by showing differences in emitted heat across the site.
Areas with stronger thermal signals stand out from their surroundings, making it possible to see where heat-producing processes are occurring within a large and complex industrial facility.
A single thermal image provides a snapshot. Because industrial processes are so dynamic, repeated observations make it possible to compare activity from one date to another.
Hydrosat collected multiple observations of the refinery sites between September 2025 and January 2026, including both daytime and nighttime imagery.
Across the series, some thermal features remain relatively consistent while others appear, disappear, or change in intensity. These differences provide a simple way to identify areas where facility activity levels are changing.
Looking more closely at individual parts of the site reveals several examples of changing thermal activity.
Flaring is clearly visible in some observations and absent in others. Other parts of the facility shift between higher and lower levels of thermal activity, as indicated by temperature. Comparing the same locations across dates makes these changes easier to distinguish from the surrounding background.
Thermal imagery can also reveal features beyond the main processing areas.
In this example, a warm industrial discharge is visible in the January 14 nighttime observation. A nearby tailings basin also shows a thermal signal on some dates but not others.
These examples illustrate an important advantage of thermal monitoring: rather than relying only on how a facility looks with visible imagery, we can observe how its heat signature physically changes over time to understand aspects that are on, off, or operating at different thresholds.
Industrial sites can be large, complex, and geographically dispersed. Satellite thermal imagery provides a consistent way to observe heat-producing activity across these sites without requiring sensors to be installed at each facility.
Repeated thermal observations can help analysts:
Monitor variations in facility activity
Identify persistent or intermittent heat sources
Observe flaring and other high-temperature events
Monitor discharge and thermal patterns around infrastructure
Large-area coverage, spectral diversity, frequent revisit, and nighttime imaging make it possible to monitor industrial activity across broad areas and compare changing thermal conditions consistently over time.
Thermal data provides a key physical signal that identifies when, where and to what degree industrial changes happen.
Compare activity across multiple facilities or dates




In this case study, we’ll show how you can identify changes in industrial activity with a few simple analyses applied to Hydrosat data.
Tata Steel IJmuiden is one of Europe's largest integrated steelworks and a major employer and exporter for the Netherlands, producing several million tonnes of steel a year at its site on the North Sea coast. In April 2026, the plant's DSP unit was shut down after air quality measurements showed chromium-6 emissions from one of its chimneys exceeding permissible limits, a serious environmental and public health concern. Source: GMK Center.
Thermal satellite imagery offers a way to independently verify whether the DSP has genuinely gone offline, without relying solely on self-reported compliance data.
We'll start with a single Level-1B LIRI scene, VZ02_L1b_20260302_132735, taken before the DSP shutdown. Before diving into any analysis, we'll take a quick look at the images and their metadata.
from pathlib import Path
import matplotlib.pyplot as plt
import rioxarray
import xarray as xr
# Define the scene ID and data directory for the imagery
DATA_DIR = Path("data")
SCENE_ID = "VZ02_L1b_20260302_132735"# Read LWIR bands
lwir1 = rioxarray.open_rasterio(DATA_DIR / SCENE_ID / "LWIR1.tiff", masked=True).squeeze("band", drop=True)
lwir2 = rioxarray.open_rasterio(DATA_DIR / SCENE_ID / "LWIR2.tiff", masked=True).squeeze("band", drop=True)
# Build an RGB preview from the individual RED/GREEN/BLUE bands
red = rioxarray.open_rasterio(DATA_DIR / SCENE_ID / "RED.tiff", masked=True).squeeze("band", drop=True)
green = rioxarray.open_rasterio(DATA_DIR / SCENE_ID / "GREEN.tiff", masked=True).squeeze("band", drop=True)
blue = rioxarray.open_rasterio(DATA_DIR / SCENE_ID / "BLUE.tiff", masked=True).squeeze("band", drop=True)
preview = xr.concat([red, green, blue], dim="band").assign_coords(band=["red", "green", "blue"])rioxarray already masked out nodata pixels for us on load. L1B LIRI pixel values are scaled brightness temperature, so multiplying by the 0.01 scaling factor converts them to Kelvin, which we then convert to Fahrenheit for readability. We'll also show the full-resolution RGB preview alongside the two thermal bands for context.
Here we can see the broader context of the area, including the North Sea coastline and the harbor canal running through IJmuiden. Let's focus in on the steelworks for our next steps.
The full scene extends well beyond the steelworks. We'll crop LWIR1 to a region of interest (ROI) around the plant, loaded from a local file, so the same box can be reused across scenes with different projections or pixel grids.
Here, several distinct hotspots stand out clearly against the surrounding land: individual stacks and process units across the steelworks, each running well above ambient. reveals that the DSP is located in the middle-left part of the cropped region. Since this image was taken before the DSP shutdown, its thermal signature is visible (the two hotspots near the shoreline). Let's take a look at how the overall thermal picture changes after the DSP was taken offline.
We have two LWIR1 acquisitions bracketing the shutdown: 20260302 (before, March 2026) and 20260619 (after, June 2026), both from the VZ02 sensor. We'll plot them side by side with a shared color scale for direct comparison.
When sharing a color scale, the March scene shows a handful of sharp, distinct hotspots against a cool background, while the June scene is warm across almost the entire site. This is a product of summer solar heating, making it hard to tell whether the DSP (or anything else) is active. Normalizing each frame relative to its own background can help separate genuine activity from ambient seasonal heating.
Different acquisitions can have different baseline brightness temperatures due to atmospheric and seasonal conditions unrelated to plant activity. Simply subtracting each frame's median still leaves frames with a lot of background texture (e.g. a hot summer day) looking uniformly "hot," since the deviations are on a different scale from frame to frame.
Instead, we use a robust z-score, which normalizes both the center and the spread of each frame using statistics that resist being skewed by a small number of extreme hot pixels:
where MAD(x) = median(|x - median(x)|) is the median absolute deviation. Subtracting the median (instead of the mean) keeps the center from being pulled up by a handful of very hot pixels, and dividing by the (rescaled) MAD instead of the standard deviation keeps the scale from being inflated by those same outliers. The 1.4826 constant rescales MAD so it's comparable to a standard deviation for normally-distributed data, making the resulting z-score readable the same way as an ordinary one (e.g. +3 ≈ "3 robust standard deviations above typical").
After normalization, the difference between the images becomes clear: by June, Tata Steel has shut off the DSP (as per the article), and the Warmband facility (the hot-rolled strip steel mill) also appears to be offline. With a few simple image processing steps, we have gone from raw imagery to actionable compliance insights.
Beyond a visual inspection, extracting pixel values can provide further insight into the thermal activity of a scene. A profile (a cross-sectional view that shows the change in pixel values along a linear path) through the hottest point tells us two distinct things at once:
Whether a given unit is active or idle (does the profile spike well above the surrounding background, or does it stay flat and close to ambient?)
How active it is (values along the profile indicate the intensity of the thermal signal, not just its presence)
We'll use the 20260302 acquisition, taken before the DSP shutdown, since it's where the plant's activity registers most clearly.
This profile is (taken through the Warmband) shows a clear signal of activity. By extracting similar profiles through our images, or by taking pixel statistics for an AOI polygon, we can further learn about the thermal characteristics of locations and derive insights from thermal imagery.
# Scaling factor for brightness temperature (from DN to Kelvin)
BT_SCALE = 0.01
# Convert brightness temperature from Kelvin to Fahrenheit
def kelvin_to_fahrenheit(da):
return (da - 273.15) * 9 / 5 + 32
# Stretch each band independently to enhance contrast in RGB composite
def percentile_stretch(da, low=1, high=99):
vmin, vmax = da.quantile(low / 100), da.quantile(high / 100)
return ((da - vmin) / (vmax - vmin)).clip(0, 1)
# Convert LWIR bands to brightness temperature and stretch RGB preview
lwir1_bt = kelvin_to_fahrenheit(lwir1 * BT_SCALE)
lwir2_bt = kelvin_to_fahrenheit(lwir2 * BT_SCALE)
preview_stretched = preview.groupby("band").map(percentile_stretch)
# Plot LWIR brightness temperature and RGB preview
fig, axes = plt.subplots(1, 3, figsize=(19, 6))
for ax, da, label in zip(
axes[:2], [lwir1_bt, lwir2_bt], ["LWIR1 (10.9 µm)", "LWIR2 (12.0 µm)"]
):
da.plot.imshow(ax=ax, cmap="magma", add_colorbar=True, cbar_kwargs={"label": "°F"})
ax.set_title(f"{label} Brightness Temperature")
ax.set_aspect("equal")
ax.axis("off")
preview_stretched.plot.imshow(ax=axes[2], rgb="band")
axes[2].set_title("RGB Preview")
axes[2].set_aspect("equal")
axes[2].axis("off")
fig.suptitle(SCENE_ID)
plt.tight_layout()
plt.show()import json
# Function to extract ROI bounds from a GeoJSON file
def roi_bounds_from_geojson(path):
coords = json.loads(Path(path).read_text())["features"][0]["geometry"]["coordinates"][0]
lons, lats = zip(*coords)
return (min(lons), min(lats), max(lons), max(lats))
# Extract ROI bounds from the GeoJSON file and clip the LWIR band to the ROI
ROI_BOUNDS = roi_bounds_from_geojson("assets/tata_steel.geojson")
lwir1_roi = lwir1_bt.rio.clip_box(*ROI_BOUNDS, crs="EPSG:4326")from matplotlib.patches import Rectangle
from rasterio.warp import transform_bounds
# Reproject the ROI bounds into the raster's CRS so the box lines up on the full scene
roi_minx, roi_miny, roi_maxx, roi_maxy = transform_bounds(
"EPSG:4326", lwir1_bt.rio.crs, *ROI_BOUNDS
)
# Create a figure with two subplots: one for the full scene and one for the ROI
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
lwir1_bt.plot.imshow(ax=axes[0], cmap="magma", add_colorbar=True, cbar_kwargs={"label": "°F"})
axes[0].add_patch(
Rectangle(
(roi_minx, roi_miny),
roi_maxx - roi_minx,
roi_maxy - roi_miny,
edgecolor="cyan",
facecolor="none",
linewidth=2,
)
)
axes[0].set_title("LWIR1 (10.9 µm) - Full Scene")
axes[0].set_aspect("equal")
axes[0].axis("off")
lwir1_roi.plot.imshow(ax=axes[1], cmap="magma", add_colorbar=True, cbar_kwargs={"label": "°F"})
axes[1].set_title("LWIR1 (10.9 µm) - ROI")
axes[1].set_aspect("equal")
axes[1].axis("off")
fig.suptitle(f"{SCENE_ID}")
plt.tight_layout()
plt.show()# Close all opened raster files to free up resources
lwir1.close(); lwir2.close(); red.close(); green.close(); blue.close(); preview.close()# Imagery scene IDs
TIMESERIES_SCENES = [
"VZ02_L1b_20260302_132735",
"VZ02_L1b_20260619_132916",
]
# Helper function to extract the date from a scene ID
def scene_date(scene_id):
return scene_id.split("_")[2]
# Load and crop LWIR1 imagery for a given scene ID
def load_cropped_lwir1(scene_id):
da = rioxarray.open_rasterio(
Path("data") / scene_id / f"LWIR1.tiff", masked=True
).squeeze("band", drop=True)
da = da.rio.clip_box(*ROI_BOUNDS, crs="EPSG:4326") * BT_SCALE
return kelvin_to_fahrenheit(da)
# Load the LWIR1 time series for all scenes
timeseries = {scene_id: load_cropped_lwir1(scene_id) for scene_id in TIMESERIES_SCENES}
dates = sorted(timeseries, key=scene_date)from datetime import datetime
# Determine the min and max brightness temperature values for consistent color scaling
vmin = min(float(da.min()) for da in timeseries.values())
vmax = max(float(da.max()) for da in timeseries.values())
# Plot the LWIR1 time series
fig, axes = plt.subplots(1, len(dates), figsize=(7 * len(dates), 6))
for ax, scene_id in zip(axes, dates):
da = timeseries[scene_id]
da.plot.imshow(ax=ax, cmap="magma", vmin=vmin, vmax=vmax, cbar_kwargs={"label": "°F"})
ax.set_title(datetime.strptime(scene_date(scene_id), "%Y%m%d").strftime("%B %d, %Y"))
ax.axis("off")
fig.suptitle("LWIR1 Brightness Temperature")
plt.tight_layout()
plt.show()# Compute robust z-score normalized timeseries for each scene
def robust_zscore(da):
median = float(da.median())
mad = float(abs(da - median).median()) * 1.4826 # scaled to be comparable to std
return (da - median) / mad
# Apply robust z-score normalization to all scenes in the timeseries
normalized_timeseries = {
scene_id: robust_zscore(da) for scene_id, da in timeseries.items()
}# Determine the global min and max for the normalized timeseries for consistent color scaling
norm_vmin = min(float(da.min()) for da in normalized_timeseries.values())
norm_vmax = max(float(da.max()) for da in normalized_timeseries.values())
# Plot the normalized timeseries
fig, axes = plt.subplots(1, len(dates), figsize=(7 * len(dates), 6))
for ax, scene_id in zip(axes, dates):
da = normalized_timeseries[scene_id]
da.plot.imshow(
ax=ax,
cmap="magma",
vmin=norm_vmin,
vmax=norm_vmax,
cbar_kwargs={"label": "Robust z-score"},
)
ax.set_title(datetime.strptime(scene_date(scene_id), "%Y%m%d").strftime("%B %d, %Y"))
ax.axis("off")
fig.suptitle("LWIR1 Brightness Temperature (robust z-score)")
plt.tight_layout()
plt.show()from rasterio.warp import transform
# Get the data for the profile scene
profile_scene_id = "VZ02_L1b_20260302_132735"
profile_da = timeseries[profile_scene_id]
# Find the hotspot location in the profile scene
hotspot_idx = profile_da.argmax(dim=["y", "x"])
hotspot_row = int(hotspot_idx["y"])
hotspot_col = int(hotspot_idx["x"])
hotspot_northing = float(profile_da["y"].isel(y=hotspot_row))
hotspot_easting = float(profile_da["x"].isel(x=hotspot_col))
# Extract the transect along the hotspot row
transect = profile_da.isel(y=hotspot_row)
# Convert the hotspot's own coordinates to latitude for the title
_, hotspot_lat = transform(
profile_da.rio.crs, "EPSG:4326", [hotspot_easting], [hotspot_northing]
)
hotspot_lat = hotspot_lat[0]
# Convert the transect's easting coordinates to longitude for the x-axis
transect_lons, _ = transform(
profile_da.rio.crs,
"EPSG:4326",
transect["x"].values,
[hotspot_northing] * transect.sizes["x"],
)
# Compute overall statistics for the profile scene
overall_min = float(profile_da.min())
overall_max = float(profile_da.max())
overall_mean = float(profile_da.mean())
overall_std = float(profile_da.std())
# Plot the profile scene and the transect through the hotspot
fig, (ax_img, ax_profile) = plt.subplots(1, 2, figsize=(16, 6))
profile_da.plot.imshow(ax=ax_img, cmap="magma", cbar_kwargs={"label": "°F"})
ax_img.axhline(hotspot_northing, color="cyan", linestyle="--", linewidth=1.5)
ax_img.set_title(f"{profile_scene_id} - Latitude: {hotspot_lat:.4f}°N")
ax_img.axis("off")
ax_profile.plot(transect_lons, transect.values, color="crimson", label="Transect")
ax_profile.axhline(overall_mean, color="black", linewidth=1, label="Mean")
ax_profile.axhspan(
overall_mean - overall_std,
overall_mean + overall_std,
color="gray",
alpha=0.3,
label="Mean ± 1 std",
)
ax_profile.axhline(overall_min, color="steelblue", linestyle=":", label="Min")
ax_profile.axhline(overall_max, color="firebrick", linestyle=":", label="Max")
ax_profile.set_xlabel("Longitude")
ax_profile.set_ylabel("°F")
ax_profile.set_title("Thermal Profile")
ax_profile.legend()
plt.tight_layout()
plt.show()





Learn how thermal imagery can be applied to real-world challenges.
Agriculture
Urban Heat
Wildfire
Energy & Industrial
Oceans
Defense & Intelligence
Weather Forecasting
Forestry
Biodiversity









Hydrosat's Discovery STAC API requires a bearer token for authentication and authorization.
This article covers the process of using a client ID and secret pair to generate a bearer token and then including that bearer token in requests to the STAC API.
For documentation on how to create an API client ID and secret, view the Managing API Clients article for Discovery Portal. If you don't already have access to Discovery Portal, see here.
When creating an API client in the Discovery Portal, you will be issued a client ID and client secret for that API client.
The client ID and secret are unique credentials that allow the client to generate a temporary bearer token which provides access to use the API with any permissions provided to that client.
The client can request a valid token by submitting its client ID and secret in a form POST payload to the following token url:
https://auth.hydrosat.com/oauth2/tokenThe response contains the values shown below, including the amount of time the token will live before expiring, in seconds. Because a token is only temporarily valid, the client must manage getting refreshed tokens regularly in order to have consistent access to the API.
The example below assumes that the user has stored their API client ID and secret in a separate file called creds.json with the following structure:
For security, we suggest setting credential file permissions to 600 (chmod 600 creds.json) so that only the owner has read and write access. Consult with your organization's security team to ensure you are complying with preferred methods for storing and accessing API client credentials.
The Python example below covers using the credentials stored in creds.json to generate a new token for use with the Hydrosat Discovery STAC API and storing the resulting token in the access_token variable.
Once the client has a valid token, it can make requests to the as normal.
In the below example, the token is stored in the access_token variable from the previous code example and is used to make a request to the /collections endpoint using the requests Python library.
Unauthenticated requests to the STAC API will return an error (401 Unauthorized Error)
For lengthier examples and next steps, please see our .
{
"access_token": "<token_value>",
"expires_in": 3600,
"token_type": "Bearer"
}{
"client_id":"<clientID>",
"client_secret":"<clientsecret>"
}tokenUrl = "https://auth.hydrosat.com/oauth2/token"
with open('creds.json') as f:
creds = json.loads(f.read())
client_id = creds["client_id"]
client_secret = creds["client_secret"]
payload = f'grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
token_response = requests.request("POST", tokenUrl, headers=headers, data=payload)
token_data = token_response.json()
access_token = token_data["access_token"]
STACheaders = {"Authorization":f"Bearer {access_token}"}
collection_response = requests.request("GET", 'https://stac.hydrosat.com/collections', headers=STACheaders)
collection_data = collection_response.json()
print(collection_data)If your Discovery Platform account is configured to allow data downloads, your access is based on areas of interest and/or specific scene IDs. The Discovery Portal includes some helpful tools to help you locate, search, and understand your downloadable data.
For help locating downloadable data via the STAC API, check out this separate article.
If you have access to downloadable data, the Downloadable toggle is available on the imagery search bar. Combine the Downloadable toggle with additional search filters, such as Acquisition Date or Cloud Cover , to find results for a subset of your downloadable data.
A Downloadable search includes results based on area of interest and specific scene ID, depending on how your account is configured.
For accounts with area of interest-based download access, you can view your Download Access Areas on the map. When using the Downloadable search toggle, as discussed above, the search automatically looks for results matching your Download Access Areas.


Creating an API client is required to use Hydrosat's Discovery STAC API. This article covers the workflow for managing API Clients in the Discovery Portal.
The API Clients management interface is available only to users on your account with the Org Admin role. If there is already an Org Admin on your account, this person can generate API clients for you to use the API.
To view the Client ID interface, click your user avatar in the top right corner of Discovery Portal and select Account.
Users with the requisite permissions will see the API Clients section on the Account page.
Each account is limited to 4 total API clients, so consider this limit when creating new API clients.
Click New API Client on the right side of the API Clients section.
Provide a name for the API client that is easily recognizable to you and other Org Admins. Name is required.
(Optional) Provide a description.
Click Create API Client
Immediately upon creation, the client secret for the API client is displayed. This display is only temporary. Store the client secret in a safe and secure location in your environment as you will not be able to retrieve it from Discovery Portal in the future.



If your Discovery Platform account is configured to allow data downloads, your download entitlements are based on areas of interest and/or specific scene IDs. If you already know what your entitlement areas of interest (Download Access Areas) or scene IDs are, you can use that information to query data via the STAC API.
For help locating downloadable data in the Discovery Portal, check out this separate article.
If you don't know what your download entitlements are, you can look them up using Hydrosat's Accounts API.
If you would like to get access to downloadable data, contact our sales team.
Accounts API uses the same bearer token Authentication and API credentials as the STAC API.
Use the following endpoint to GET the active access policy for your account, which includes information about your download entitlements.
https://accounts.hydrosat.com/v2/me/access-policiesThe polygons for area of interest-based download entitlements are listed within product_entitlements and scene ID-based entitlements are listed within scene_entitlements. The product_entitlements and scene_entitlements also include information about which STAC collections are you entitled to download from.
A product_entitlements example with a single polygon areas_of_interest is shown below.
A scene_entitlements example containing 2 scene_ids is shown below.
meResponse = requests.request("GET",'https://accounts.hydrosat.com/v2/me/access-policies', headers=myheaders)
meContent = meResponse.json()
access_policy = meContent.get("access_policies", [])[0]
scene_entitlements = access_policy.get("scene_entitlements", [])
product_entitlements = access_policy.get("product_entitlements", [])[
{
"id":"d1ab374b-7623-416d-9dc9-b62df7866625",
"access_policy_id":"98ab1aa0-02a2-4a75-b5bf-19e95f5eb08e",
"product_name":"archive",
"configuration":{
"product_name":"archive",
"collections":[
"vz-liri-l1a",
"vz-viri-l1a",
"vz-l1b",
"vz-l2"
],
"restrict_search":false,
"restrict_items":true,
"restrict_properties":true
},
"areas_of_interest":[
{
"id":"448e8a88-0c56-4406-b9ba-a64b2c946584",
"product_entitlement_id":"d1ab374b-7623-416d-9dc9-b62df7866625",
"name":"Lake",
"geometry":{
"type":"Polygon",
"coordinates":[
[
[
-94.0733085212435,
31.98981826138629
],
[
-93.82521380712805,
31.113313105933432
],
[
-93.41658721917305,
31.01958485756203
],
[
-93.38010270239135,
31.275556307422413
],
[
-93.5260407695181,
31.704771952854895
],
[
-93.82521380712805,
31.97125515504588
],
[
-94.0733085212435,
31.98981826138629
]
]
]
},
"created":"2026-07-10T23:12:25.815575Z",
"updated":"2026-07-10T23:12:25.815575Z"
}
],
"created":"2026-07-10T23:12:25.587717Z",
"updated":"2026-07-10T23:12:25.587717Z"
}
][
{
"id":"5d756296-38d0-4c61-a256-1cfc4eeee411",
"access_policy_id":"ffdf58c5-c527-4c32-98cf-933a2114f0c5",
"collections":[
"vz-liri-l1a",
"vz-viri-l1a",
"vz-l1b",
"vz-l2"
],
"scene_id":"VZ01_20260306_050940",
"created":"2026-07-10T21:54:04.501632Z",
"updated":"2026-07-10T21:54:04.501632Z"
},
{
"id":"e9377d49-6342-4530-ad87-325cd3a04989",
"access_policy_id":"ffdf58c5-c527-4c32-98cf-933a2114f0c5",
"collections":[
"vz-liri-l1a",
"vz-viri-l1a",
"vz-l1b",
"vz-l2"
],
"scene_id":"VZ02_20260514_073333",
"created":"2026-07-10T21:54:04.391214Z",
"updated":"2026-07-10T21:54:04.391214Z"
}
]Hydrosat's Data Discovery STAC API is available at https://stac.hydrosat.com/. The STAC API requires authentication using a bearer token.
This page includes high-level information on the basic features of the API and provides examples for a few methods of working with it.
Through Hydrosat's STAC API, you can search for data that meets your needs. Data are stored as Cloud-Optimized GeoTIFFs (COGs), which means you can either download the full files or stream only the parts you need using standard web requests.
Once you’re authenticated, you can browse Hydrosat's STAC items and view thumbnail previews. However, only users who have ordered specific data can download the full COGs for their selected areas.
Learn more:
STAC (SpatioTemporal Asset Catalog): stacspec.org
Cloud-Optimized GeoTIFFs (COGs):
While you can use the STAC API directly, we recommend using one of the following tools to make it easier:
Python library:
Hydrosat's STAC catalog is organized into four collections.
Each collection contains a series of items representing individual scenes. Within each item, you will find metadata and links to associated imagery.
We recommend using the pystac-client Python library. This library is specifically designed to interact with STAC catalogs and APIs. Complete documentation for this library can be found . More examples with pystac are included in our full .
The STAC server can also be queried directly from your terminal using curl. For example, you can use a POST request to perform a query, specifying the search parameters as JSON-formatted raw data. For more information, please see the curl .
GET /collections
Return list of available collections
GET /collections/{collection_id}
Return metadata for a single collection
GET /collections/{collection_id}/items
Collection Name
Description
vz-viri-l1a
Level-1A radiance data from the visible and near-infrared sensor (VIRI)
vz-liri-l1a
Level-1A radiance data from the longwave infrared thermal sensor (LIRI)
vz-l1b
import pystac
from pystac_client import Client
# Connect to the STAC catalog.
# See our page on authentication for how to set up your headers.
catalog = Client.open('https://stac.hydrosat.com/', headers=headers)
# Search for Level-2 data intersecting southern Australia from 19 April 2026
search = catalog.search(
collections = ['vz-l2'],
bbox = [140.173825, -37.63983, 149.28178, -34.929824],
datetime = ["2026-01-19T00:00:00Z", "2026-04-19T00:00:00Z"]
)
# Print a list of STAC items returned from the search
items = list(search.items())
print(items)curl -X POST \
--header 'Content-Type: application/json' \
--data '{
"collections": ["vz-l2"],
"bbox": [140.173825, -37.63983, 149.28178, -34.929824],
"datetime": "2026-04-01T00:00:00Z/2025-05-01T00:00:00Z"
}' \
https://stac.hydrosat.com/Return list of items in the specified collection
GET /collections/{collection_id}/items/{item_id}
Retrieve a specific item in a specific collection
POST /search
Search for items using filters (e.g., date, location)
Level-1B data: top-of-atmosphere reflectance (VIRI) and brightness temperature (LIRI) aligned and resampled to same resolution
vz-l2
Level-2 data: surface reflectance and surface temperature

To access the Discovery Portal or use the API, you will need an account.
Fill out the Request Access form on our website.
Once your account has been created, you will receive an email from support@hydrosat.com with your username and temporary password. Check your spam folder if you don't see your invitation email.
Follow the instructions in the email to log in with the temporary password and choose a new password. You have 7 days to log in and reset your password. If your temporary password expires, contact to get a new temporary password.
Once you've reset your password, you will be able to use your new account to log into the . If you need to generate API credentials for use with our , follow the instructions for .
Understand thermal imagery and how to apply it.
Thermal imagery is unique. By capturing heat emitted directly by the Earth's surface, it reveals physical processes that drive changes across landscapes. Here, we'll build some intuition about how thermal imaging works and why it's fundamentally distinct from other remote sensing techniques.
Every object with a temperature above absolute zero emits electromagnetic radiation. Hotter objects emit more energy, a relationship described by blackbody radiation curves:
Real-world materials aren't perfect blackbodies, but they follow the same fundamental behavior. As temperature increases, the total emitted energy increases. Thermal sensors exploit this relationship by measuring emitted radiation in specific infrared wavelengths.
This means that thermal imagery and traditional optical imagery observe fundamentally different things. Optical sensors measure sunlight reflected from the Earth's surface, just like how our eyes see the world. But thermal sensors directly measure that emitted radiation, providing a unique lens into how landscapes store and release heat, rather than just how they look. By extension, this also means that thermal sensors can operate both day and night, enabling continuous observation even after sunset.

Different materials emit thermal radiation with different efficiencies. This is a concept known as emissivity. A perfect blackbody has an emissivity of 1; it emits the maximum possible radiation for its temperature. Real-world materials have lower emissivities, and those emissivities can vary significantly depending on the material type. This means that two surfaces at the same physical temperature can emit very different amounts of thermal radiation. For example, metals are highly reflective, so they absorb less and emit less radiation than vegetation or bare soil does. Without accounting for emissivity, a thermal sensor can't distinguish whether a difference in measured radiation is actually due to temperature or to variability in surface properties.
As emitted radiation from the surface makes its way to the satellite, it must pass through the atmosphere. Along the way, gases like water vapor and carbon dioxide absorb and re-emit part of the thermal signal. So by the time the radiation reaches the detector, it has already been modified by the atmosphere. Atmospheric correction algorithms estimate and remove these effects to better recover the true surface signal.
We've seen that thermal sensors measure emitted infrared radiation, not temperature directly. Since many users require knowledge of Earth's surface temperature (LST), LST retrieval algorithms, like the one implemented by Hydrosat, work backward from radiance. These physics-based models correct for atmospheric effects, account for surface emissivity, and apply the principles of thermal radiation to produce a physically meaningful estimate of LST that can be compared across different locations and over time.
Unlike many landscape characteristics that change gradually over the course of weeks or months (e.g., vegetation greenness), LST is one of the most dynamic properties of the Earth's surface. Adjacent agricultural fields might differ by several degrees because of differences in vegetation or moisture levels. A single location may experience large temperature swings throughout the day as it absorbs and releases heat. Frequent, high-resolution LST observations capture these dynamics help users identify anomalies, monitor environmental conditions, and better understand the processes driving change across ecosystems.


Now that you've got the basics down, let's look at how Hydrosat's data products fit in.
Hydrosat's data processing pipeline translates measurements from our satellites into progressively higher-level data products, ranging from raw sensor observations to analysis-ready surface temperature.
The bottom line is that every pixel in a thermal image tells a story, but the context for that story depends on exactly which data product you're looking at.
If you're using the Level-1A data product, you're looking at the closest representation of what the satellite saw.
Level-1A pixels are at-sensor radiance values in units of watts per square meter per steradian per micrometer (W/m²/sr/µm). You can think of this as the uncorrected thermal radiation that reached the detector after traveling through the atmosphere.
Radiance data is a good choice if you're interested in applying your own higher-level processing or developing advanced workflows. It's not the best choice if you want to dive right into analyzing conditions at the land surface.
The Level-1B brightness temperature product is still a direct representation of what the satellite observed, but it's a little more intuitive to work with than Level-1A radiance.
Pixel values represent the apparent temperature in units Kelvin based solely on the radiation measured by the satellite. In other words, this is the temperature a blackbody would need to have in order to emit that amount of radiation.
You might use brightness temperature if you want an intuitive thermal measurement that avoids additional assumptions and uncertainties introduced during Level-2 processing.
The Level-2 data product is our best estimate of what's happening at the Earth's surface, after accounting for emissivity and the atmosphere.
Each pixel value is a temperature in units Kelvin that describes how hot the Earth's surface was—including vegetation, soil, water, pavement, and other surfaces—at the moment the satellite passed overhead.
If you're interested in monitoring something like urban heat, vegetation dynamics, or drought, surface temperature will give you the most direct window into that process.
Visit our and sign up for notifications to get the latest information on our STAC API availability.




A list of satellite data product updates, including release dates and key features.
We've deployed a new version of our cloud detection model using a significantly larger and more diverse training dataset. This results in fewer false positives in the cloud mask and improved detection performance across a wider range of cloud conditions.
We've deployed a minor fix to ensure representativeness of STAC item footprint geometries with actual imaged area.
We've refined our mutual information-based coregistration workflow for improved feature matching between the LWIR and VNIR data.
We've updated our georeferencing and VNIR band alignment workflows to improve geolocation accuracy and band-to-band registration. This release includes:
An enhanced optical distortion correction to reduce residual along-track offsets
Use of a digital elevation model (DEM) to improve band-to-band registration over complex terrain
We've modified the units for our Level-1A and Level-1B data products.
Previously, L1A and L1B imagery assets contained per-band digital number data. To arrive at radiance, the user needed to apply gain and offset coefficients provided in product metadata.
Level-1A Products
L1A imagery assets now natively contain per-band top-of-atmosphere (TOA) radiance values. Gain and offset coefficients are applied as part of Hydrosat's data processing.
We've improved the contrast and visual consistency in our L1B and L2 thermal thumbnails and previews. This includes:
Consistent scaling across the full imaging strip rather than within individual scenes.
A switch from the inferno color ramp to a modified RdYlBu_r color ramp, where cooler pixels are shown in blue and warmer ones in red. These colors map to predictable temperatures; the breakpoint from blue to yellow occurs at approximately 0 degrees Celsius.
We've implemented a minor correction for noise in the thermal products.
We've released a color curve approach that improves the visual contrast in our true color thumbnails. This change has no impacts on the underlying data.
We've released an improved cloud mask, which uses a U-Net convolutional neural network architecture. The new model outperforms our baseline Fmask approach across several key metrics.
Units: W/m2/sr/μm
Scaling factor: 0.01 (VNIR), 0.0001 (LWIR)
Level-1B Products
L1B imagery assets contain per-band TOA reflectance data (for VNIR bands) and brightness temperature data (for LWIR bands).
Units: Unitless (VNIR); Kelvin (LWIR)
Scaling factor: 0.0001 (VNIR); 0.01 (LWIR)
Conversions
Hydrosat provides a full suite of coefficients in product metadata for conversion between TOA radiance and reflectance (or brightness temperature). For more information on usage, see or our product guide.
This change has no impacts on the underlying data.
Insufficient illumination for VNIR
The sun's elevation was low during acquisition. This may reduce the quality of the VNIR imagery.
Geolocation out of spec
Geolocation for the scene does not meet the expected accuracy.
Georeference failure
The nominal georeference procedure did not complete as expected.
Extreme cold values clipped
Some pixels contain fill values. Use the QUALITY_ASSURANCE mask for details.
The is a web interface for visually browsing and downloading Hydrosat's satellite imagery catalog and complements the .
Interested in getting access to the Hydrosat Discovery Platform? Fill out the .
Here are our answers to some frequently asked questions from users.



Welcome to Hydrosat's documentation site! Whether you're getting started with Hydrosat imagery, building advanced workflows, or just curious about thermal data and its applications, here you'll find tutorials, API references, and guides to help you get the most out of our products.
Please reach out to us at if you have any questions. We’ll get back to you as soon as we can.
Learn the fundamentals of thermal imagery.
See how you can explore our imagery archive.
Find information on our available imagery products.
Catch up on recent data processing improvements.
Read through our STAC API documentation.
Explore real case studies with Hydrosat data.






Visit our example code for helpful tutorials for using our STAC API and imagery.
Learn more about the different data products Hydrosat provides.
The Level-1A data product is the least processed of the available imagery. It includes processing applied onboard the instrument, such as time delay integration and non-uniformity corrections, as well as co-registration of sensor bands and georegistration. The full swath data is provided to users in image space with no resampling. Level-1A pixel values represent top-of-atmosphere (TOA) radiance [W/m2/sr/μm].
The Level-1B product includes converting the radiance values to TOA reflectance (VIRI) or brightness temperature (LIRI), clipping the VIRI data to the extent of the LIRI swath, generation of a cloud mask, and orthorectification.
L1 Product Granule Size
L1A VIRI: 122 km x 70 km
L1A LIRI: 70 km x 70 km
L1B: Combined 70 km x 70 km
Each STAC item includes several assets representing the individual data and metadata files associated with the scene. Items from different collections contain a different set of assets, as defined below.
L1A VIRI TOA radiance values can be converted to TOA reflectance by applying the per-band LEVEL1_REFLECTANCE_SCALING coefficient in the companion L1A metadata (MTA) file.
L1A LIRI TOA radiance values can be converted to brightness temperature using the following equation:
where K1 and K2 are per-band LEVEL1_THERMAL_CONSTANTS from the L1A MTA file, and L represents scaled radiance.
L1B VIRI reflectance data can be converted back to TOA radiance by applying the per-band LEVEL1_RADIANCE_SCALING coefficient in the companion L1B MTA file.
L1B LIRI BT values can be converted back to TOA radiance using the following equation:
where K1 and K2 are per-band LEVEL1_BT_TO_RADIANCE_CONSTANTS from the L1B MTA file.
The Level-2 product includes radiometric terrain corrections and conversion to surface reflectance (SR) and land surface temperature (LST).
Level-2 assets include per-band SR and LST COGs (and more).
No Data Value
0
Scaling Factor
LST, LST uncertainty: 0.01
SR, emissivity: 0.0001
L1 Pixel Size
L1A VIRI: 29.9 m
L1A LIRI: 68.8 m
L1B: 30 m
Resampling Method
Bilinear (L1B only)
Bit Depth
16-bit
Map Projection
L1A VIRI & LIRI: EPSG 4326 (RPCs included in metadata)
L1B: Universal Transverse Mercator (UTM)
No Data Value
0
Scaling Factor
L1A VIRI: 0.01 L1A LIRI: 0.001 L1B VIRI: 0.0001 L1B LIRI: 0.01
Conversion Factors
Coefficients for conversion between radiance and TOA reflectance or BT provided in companion metadata file
BT = K2 / ln(K1 / L + 1)L = K1 / (e(K2 / BT) - 1)L2 Product Granule Size
70 km x 70 km
L2 Pixel Size
30 m
Map Projection
UTM
Bit Depth
16-bit
Blue radiance
COG
GREEN
Green radiance
COG
RED
Red radiance
COG
REDEDGE1
Red edge 1 radiance
COG
REDEDGE2
Red edge 2 radiance
COG
REDEDGE3
Red edge 3 radiance
COG
NIR
NIR radiance
COG
QUALITY_ASSURANCE
Radiometric saturation mask
COG
PREVIEW
Full-resolution RGB preview image
COG
THUMBNAIL
Low-resolution RGB thumbnail
PNG
METADATA
Ancillary metadata file
JSON
LWIR 1 radiance
COG
LWIR2
LWIR 2 radiance
COG
QUALITY_ASSURANCE
Radiometric saturation mask
COG
PREVIEW_LWIR
Full-resolution LWIR preview image
COG
THUMBNAIL
Low-resolution thumbnail
PNG
METADATA
Ancillary metadata file
JSON
Blue TOA reflectance
COG
GREEN
Green TOA reflectance
COG
NIR
NIR TOA reflectance
COG
RED
Red TOA reflectance
COG
REDEDGE1
Red edge 1 TOA reflectance
COG
REDEDGE2
Red edge 2 TOA reflectance
COG
REDEDGE3
Red edge 3 TOA reflectance
COG
LWIR1
LWIR 1 brightness temperature
COG
LWIR2
LWIR 2 brightness temperature
COG
QUALITY_ASSURANCE
Radiometric saturation mask
COG
CLOUD_MASK
Mask indicating cloud, cloud shadow, and snow or ice
COG
PREVIEW
Full-resolution RGB preview image
COG
PREVIEW_LWIR
Full-resolution LWIR preview image
COG
THUMBNAIL
Low-resolution RGB thumbnail
PNG
METADATA
Ancillary metadata file
JSON
Blue band surface reflectance
COG
GREEN_SR
Green band surface reflectance
COG
NIR_SR
NIR band surface reflectance
COG
RED_SR
Red band surface reflectance
COG
REDEDGE1_SR
Red edge 1 band surface reflectance
COG
REDEDGE2_SR
Red edge 2 band surface reflectance
COG
REDEDGE3_SR
Red edge 3 band surface reflectance
COG
LWIR1_EMIS
LWIR 1 band emissivity
COG
LWIR2_EMIS
LWIR 2 band emissivity
COG
LST
Land surface temperature
COG
LST_UNCERTAINTY
Land surface temperature uncertainty
COG
QUALITY_ASSURANCE
Radiometric saturation mask
COG
CLOUD_MASK
Mask indicating cloud, cloud shadow, and snow or ice
COG
PREVIEW
Full-resolution RGB preview image
COG
PREVIEW_LST
Full-resolution LST preview image
COG
THUMBNAIL
Low-resolution RGB thumbnail
PNG
THUMBNAIL_LST
Low-resolution LST thumbnail
PNG
METADATA
Ancillary metadata file
JSON