---
title: "List the data catalog's products."
method: GET
path: "/v1/catalog"
tags: ["Meta"]
---

# List the data catalog's products.

`GET /v1/catalog`

## Request

- Base URL: `https://api.smarterweather.com`
- URL: `https://api.smarterweather.com/v1/catalog`
- Auth: HTTP bearer

Lists every product the REST API serves, from the Smarter Weather
data catalog: its family, kind, cadence, coverage, horizon,
stability, cost class, entitlement and source attribution, sorted by
`id`. `GET /v1/catalog/{product_id}` adds a product's variables and
retrieval fields. Field names match the MCP `list_datasets` and
`describe_dataset` tools.

### Completeness and paging

Without `limit` the list is complete: it is never truncated and
carries no cursor. When the catalog outgrows one response, paging
ships as an additive change using the house paging words (`limit`
and `truncated`, then `cursor` and `next_cursor`). Filters, when
added, are named after the field they filter (`kind`,
`product_type`, `domain`, `family`, and `var` for a variable).

### Freshness

`include=freshness` adds each product's `freshness` (see
`CatalogFreshness`, **experimental**). Without it the response is a
static view of the catalog and does not depend on live data.

### Caching

Without `include=freshness` the response carries a weak `ETag`
(`W/"..."`, a hash of the response body, so it changes whenever the
body does), `Vary: Accept-Encoding`, and
`Cache-Control: max-age=300, stale-while-revalidate=60`; send the
tag back in `If-None-Match` to get `304 Not Modified` with no body.
`Range` is ignored: the body is always the whole document.
With `include=freshness` there is no `ETag` and the budget is
`max-age=60, stale-while-revalidate=30`. `Cache-Control` is
`public` for first-party keys and `private` for partner keys.

### Auth and cost

Standard sw-api Bearer auth and rate limits apply. Catalog calls
are free: they are not metered. Trial keys may call both catalog
routes without spending any of the trial's lifetime calls (an
ended trial still gets `402`); a trial key minted before the
catalog opened to trials gets `403 trial-route-not-allowed` and
should mint a new trial key.

## Query parameters

- `include` string

## Response `200`

Every product on the REST door.

- CatalogList: `GET /v1/catalog`: every product on the REST API, sorted by `id`. Complete without `limit` (see the operation description).
  - `products` CatalogProductSummary[], required
    - `id` string, required: Product id, the `product` of `/v1/point` and the MCP `dataset_id`.
    - `title` string, required
    - `family` string, required: Product family, e.g. `nbm`, `mrms`, `spc`.
    - `kind` string, required: Data shape: `grid` (a gridded field over time), `snapshot` (the latest gridded analysis), `feature` (vector features such as outlook polygons) or `text` (text products). New kinds may be added.
    - `product_type` string, nullable, required: `forecast`, `analysis` or `climatology`; `null` when the catalog does not classify the product. New values may be added.
    - `domain` string, required: Coverage domain, e.g. `conus`, `alaska`, `hawaii`, `global`.
    - `coverage` string[], required: Domains the product covers.
    - `cadence` string, nullable, required: Nominal update cadence, an ISO 8601 duration.
    - `horizon` string, nullable, required: Forecast horizon, an ISO 8601 duration; `null` for analyses.
    - `step` string, nullable, required: Native frame spacing, an ISO 8601 duration: how far apart the product's frames are (`PT6H` for a 6-hourly model, `PT2M` for an MRMS mosaic, `P1D` for a daily product). Set on every `grid` and `snapshot` product; `null` for `feature` and `text` products, which have no frames. A sub-day step divides 24 h and a longer one is whole days. A rolling `/v1/series` forecast window starts at now floored to it from 00:00Z (a step of a day or more floors to UTC midnight), so the frame covering now is in.
    - `temporal_type` string, nullable, required: How the product is stored in time: `now`, `hourly`, `daily` or `climatology`. New values may be added.
    - `grid_res_km` number, double, nullable, required: Grid spacing in kilometres; `null` for non-gridded products.
    - `stability` string, required: Catalog stability of the product, `stable`, `beta` or `experimental`.
    - `cost_class` string, required: Cost class of the product's data calls.
    - `entitlement` string, nullable, required: Entitlement label the product requires; `null` when none.
    - `source` CatalogSource, required: The product's data source and attribution terms.
      - `id` string, required
      - `name` string, nullable, required
      - `provider_id` string, required
      - `attribution` string, required: Attribution text to display with the data.
      - `license` string, required
      - `terms_url` string, nullable, required
    - `freshness` object, nullable: **Experimental** (`x-stability: experimental`). How current a product's data is, as the MCP `list_datasets` tool reports it. The shape is final; the values' sources are still being completed: freshness checks cover a fixed set of products today, so some products (for example untracked analyses) have `freshness: null` and an untracked forecast has a `latest_run` but no `freshness_status`. Values are refreshed at most once a minute; while the freshness source is unavailable the last values are kept for up to 5 minutes (`data_age_seconds` is computed when the response is made, so it still shows how old the data is), then `freshness` is `null`.
      - `freshness_status` 'fresh' | 'stale' | 'missing': The platform's freshness check for the product: `fresh` within its expected age, `stale` older, `missing` no data found. When a product is checked in more than one store, the stalest result. Absent when the product is not checked.
      - `latest_time` string, date-time: Valid time of the newest data the freshness check found.
      - `latest_run` string, date-time: Reference time of the latest model run served (forecasts only).
      - `data_age_seconds` integer, nullable, required: Age of the newest data in seconds: from `latest_run` for a forecast, else from `latest_time`. `null` when unknown or when the time is in the future (a forecast valid time says nothing about age).

## Other responses

- `304`: The `If-None-Match` tag matches the current catalog: no body. The `ETag` and `Cache-Control` headers are repeated.
- `401`: Missing or invalid API key. Carries a `WWW-Authenticate` challenge and a `next_steps` extension. `recommended` is `device_flow`: the object is a complete no-install mint recipe.
- `402`: Trial key free value ended (lifetime cap spent or key clock expired) while the claim window is still open. Carries `next_steps.recommended = claim` and the per-key claim href.
- `403`: API key lacks the required scope. Carries a `WWW-Authenticate` challenge with `error="insufficient_scope"` and a `next_steps` extension.
- `429`: Rate limit exceeded. Carries a `next_steps` extension (`recommended: upgrade`, plus `pricing` and `usage`) telling the caller how to lift the limit.
- `500`: Internal server error.

---

[API](https://skmtc.dev/smarterweather/apis/smarter-weather-api.md) · [All operations](https://skmtc.dev/smarterweather/apis/smarter-weather-api/llms.txt) · [OpenAPI document](https://skmtc.dev/smarterweather/apis/smarter-weather-api/revisions/520d55a696e4?raw)
