---
title: "Get blended weather for a location."
method: GET
path: "/v1/weather"
tags: ["Weather"]
---

# Get blended weather for a location.

`GET /v1/weather`

## Request

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

Returns a composite blended weather payload for a single location.
A single call covers current conditions, hourly forecast, daily
forecast, active alerts, convective outlooks, and astronomical data
by default; other sections (location echo, geographic metadata,
narrative text, forecaster guidance) are available via the
`include` parameter.

### Location selector

Exactly **one** of the following must be provided:

- `lat` **and** `lon` — latitude/longitude pair.
- `zip` — 5-digit US ZIP code.
- `location` — free-text location name (e.g., `"Boston, MA"`,
  `"94043"`, `"Denver"`). Resolved by the same upstream geocoder
  that powers the dedicated `/v1/geocode/{search,autosuggest,reverse}`
  endpoints; clients that need geocoding without a weather
  fetch should call those directly.

Sending more than one selector, or none, returns `400`.

### Units

- `imperial` (default): °F, mph, inHg, mi, in.
- `metric`: °C, kph, hPa, km, mm.

SI is not exposed in v1; partners that need SI should convert
client-side.

### Include semantics

`include` is a comma-separated list. Unknown tokens are silently
dropped. See the API-level "Include tokens" table in the
`info.description` for the full token catalog, defaults, and
stability markers.

### Coverage

Coverage is the 50 US states and DC, for every caller. A location
outside it (another country, Puerto Rico, Guam and the other
territories, or open water beyond about 3 nm offshore) returns
`404` with type `https://smarterweather.com/errors/outside-coverage`
and `next_steps` (`recommended = coverage`); in
`POST /v1/weather/batch` that row's `error.code` is
`outside_coverage`. Do not retry the same point. A `zip` or
`location` resolves to its first match inside coverage (so
`Carolina` is Carolina, RI, not Carolina, PR); one whose every
match is outside coverage returns `outside-coverage`, and one that
matches no US place returns `404` `not-found`, both with
`next_steps`. Partial-coverage scenarios inside
coverage are reported via `meta.warnings[]` with a structured
code so clients can respond programmatically.

### Response density, sampling and sparse fieldsets

Three independent, opt-in controls shrink the `hourly` and `daily`
rows. Without them the response is exactly the `detailed` body.
They never add sections, never change `current`, `alerts`,
`outlooks`, `astro`, `location`, `meta` (beyond
`meta.hourly_sampling`) or `attribution`, and never change pricing.

**`view`** is a column preset: `detailed` (default) returns every
column; `concise` returns a documented subset and never drops rows.

- `concise` hourly columns: `time`, `temperature`, `feels_like`,
  `humidity`, `wind_speed`, `wind_gust`, `wind_direction`,
  `precip_probability`, `precip_amount`, `precip_type`,
  `snow_amount`, `thunderstorm_probability`, `cape`,
  `cloud_ceiling`, `snow_level`, `uv_index`, `conditions`, `icon`,
  `severe`, `thunder_possible`.
- `concise` daily columns: `date`, `relative_day`,
  `active_period`, `high`, `low`, `conditions`, `icon`, `severe`,
  `precip_probability`, `precip_total`, `snow_total`, `ice_total`,
  `wind_speed_max`, `wind_direction`, `humidity_avg`,
  `uv_index_max`, `cape_max`, `thunder_possible`, `threats`,
  `astro`, `day.precip_probability`, `night.precip_probability`.
- `include` wins over the preset: with `include=uncertainty`,
  `hourly[].uncertainty` and `daily[].uncertainty` stay; with
  `include=climate`, `daily[].climate` stays.
- The preset only grows. A column may be added to `concise`
  (announced in the changelog); none is ever removed from it.

**`hourly_step`** and **`hourly_step_from`** thin hourly rows,
after `hourly_hours` sets the window and independently of `view`
and `fields`. Rows whose offset from `hourly[0].time` is below
`hourly_step_from` hours are all kept; from there on, only rows at
`hourly_step_from + k * hourly_step` hours are kept. A row's offset
is the whole hours between its `time` and `hourly[0].time`, both
floored to the hour, so a partial first hour (`14:30`, then
`15:00`, ...) is offset 0 and the full hours after it are 1, 2, ...
Offsets come from timestamps, not array indexes, so a gap in the
series does not shift the grid. `hourly_step=3&hourly_step_from=24`
keeps the first day at full resolution and every third hour after
it (48 hours become 32 rows, 72 become 40, 168 become 72). When
thinning drops at least one row, `meta.hourly_sampling` echoes the
effective parameters so a thinned series is distinguishable from a
short forecast.

**`fields`** selects exact members of hourly and daily rows, as
`section.name` paths (`hourly.temperature,daily.high`). It is exact
for every section it names and beats `view` there; a section it
does not name follows `view`. `hourly[].time` and `daily[].date`
are always returned. `fields` never implies `include`: a path whose
section, or `uncertainty` / `climate` object, is not in the
effective `include` is a `400`. Unknown names are a `400` that
lists the valid names, never a silent drop (unlike `include`,
which drops unknown tokens).

**Presence semantics.** A selected (or preset) field is present,
and `null` when its value is unavailable, exactly as in `detailed`.
A field the schema marks optional can be absent for its zero
value, exactly as in `detailed`. A field that was not selected is
absent. A field that does not exist is a `400` at request time, so
a `200` never hides an unknown name. Every shaped response is a
key-subset of the `detailed` response for the same request.

## Query parameters

- `lat` number, double
- `lon` number, double
- `zip` string
- `location` string
- `units` 'imperial' | 'metric'
- `include` string
- `days` integer
- `hourly_hours` integer
- `view` 'concise' | 'detailed'
- `hourly_step` integer
- `hourly_step_from` integer
- `fields` string

## Response `200`

Blended weather data for the requested location.

- Weather: Composite blended weather payload returned by `GET /v1/weather`. Section presence is controlled by the `include` query parameter; `location` and `gis` are always present. With the default `view=detailed` and no `fields`, every section that is present is the complete shape documented below. `view=concise` and `fields` project the members of `hourly[]` and `daily[]` rows (every other section stays complete), and `hourly_step` thins `hourly[]` rows; see "Response density, sampling and sparse fieldsets" on `GET /v1/weather`. A shaped body is always a key-subset of the detailed body for the same request.
  - `location` Location, required: Resolved location.
    - `lat` number, double, required
    - `lon` number, double, required
    - `name` string, required: Human-readable place name, usually without the state; see `region`. The resolved place name, else the city, else the nearest city. Empty when none is known.
    - `region` string: State or region abbreviation.
    - `country` string: ISO 3166-1 alpha-2 country code.
    - `timezone` string, required: IANA timezone identifier.
    - `county` string: County name (US only).
    - `zip` string: ZIP code (US only).
  - `current` Current: Current weather conditions derived from the RTMA + MRMS blend.
    - `time` string, date-time, required: Observation time (ISO 8601, UTC).
    - `temperature` number, double, nullable: Temperature (unit per `units.temperature`).
    - `feels_like` number, double, nullable: Apparent temperature (wind chill or heat index).
    - `humidity` number, double, nullable: Relative humidity, percent.
    - `dew_point` number, double, nullable
    - `pressure` number, double, nullable: Atmospheric pressure (unit per `units.pressure`).
    - `wind_speed` number, double, nullable
    - `wind_gust` number, double, nullable
    - `wind_direction` integer, nullable: Wind direction in degrees (0 = N, 90 = E).
    - `visibility` number, double, nullable: Visibility distance (null when no station or model value is available).
    - `cloud_cover` number, double, nullable: Cloud cover, percent.
    - `cloud_ceiling` number, double, nullable: Analysis cloud-base height above ground (RTMA/URMA), in the `units.altitude` unit. Null when no ceiling is reported (clear or high thin cloud) or outside analysis coverage.
    - `cloud_cover_low` number, double, nullable: HRRR low-cloud-layer cover, percent (CONUS). Present only with `cloud_cover_mid` and `cloud_cover_high` (all three numbers or all three null). Null outside HRRR coverage or before the first post-deploy cycle.
    - `cloud_cover_mid` number, double, nullable: HRRR mid-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`.
    - `cloud_cover_high` number, double, nullable: HRRR high-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`.
    - `uv_index` number, double, nullable
    - `aqi` integer, nullable: Air-quality index (US EPA scale): usually 0-500, can exceed 500 in extreme events ("Beyond the AQI"). `null` when AirNow has no value at the point, or when its latest observation is older than the source's freshness limit (currently 3 hours for AirNow). The stale case also adds a `stale_source` warning to `meta.warnings` (`section: current`, `product_id: airquality_combined`), which tells the two apart. The product's age is the `meta.freshness` entry with the same `product_id`. A reported 0 is currently also served as `null`: AirNow stores cells outside its coverage as 0, so a 0 cannot yet be told from no data.
    - `aqi_category` string: Human-readable AQI category (e.g., `"Good"`, `"Moderate"`, `"Unhealthy for Sensitive Groups"`). Omitted when AQI is unavailable.
    - `pm25` number, double, nullable: AirNow AQI sub-index for PM2.5, on the same scale as `aqi`: usually 0-500, can exceed 500 in extreme events. Integer-valued at the source; a value may show a small fractional error, so round to a whole number. Not a concentration: the field name names the pollutant and is kept for compatibility. `null` when AirNow has no value at the point, when its latest hour is stale (reported by a `stale_source` warning, see `aqi`), or when the reported value is 0 (currently indistinguishable from no data, see `aqi`).
    - `ozone` number, double, nullable: AirNow AQI sub-index for ozone, on the same scale as `aqi`: usually 0-500, can exceed 500 in extreme events. Integer-valued at the source; a value may show a small fractional error, so round to a whole number. Not a concentration: the field name names the pollutant and is kept for compatibility. `null` when AirNow has no value at the point, when its latest hour is stale (reported by a `stale_source` warning, see `aqi`), or when the reported value is 0 (currently indistinguishable from no data, see `aqi`).
    - `pm10` number, double, nullable: AirNow AQI sub-index for PM10, on the same scale as `aqi`: usually 0-500, can exceed 500 in extreme events. Integer-valued at the source; a value may show a small fractional error, so round to a whole number. Not a concentration: the field name names the pollutant and is kept for compatibility. `null` when AirNow has no value at the point, when its latest hour is stale (reported by a `stale_source` warning, see `aqi`), or when the reported value is 0 (currently indistinguishable from no data, see `aqi`).
    - `precip_rate` number, double, nullable: Current precipitation rate.
    - `precip_type` 'none' | 'rain' | 'snow' | 'mix' | 'hail' | 'unknown': Current precipitation type. Omitted when no precipitation data is available for the location.
    - `precip_24h` number, double, nullable: Observed precipitation accumulated over the trailing 24 hours, from MRMS MultiSensor QPE Pass 2. The window ends at the product's valid time, listed under `meta.freshness`. Always millimetres regardless of `units`. CONUS only; `null` outside CONUS, when no sample is available, or when the sample's valid time is older than the product's hourly-cadence staleness window (currently 3 hours, the same window `/v1/point` uses to mark it approximate). Source resolution is about 2 mm. **Experimental.** May change without notice: excluded from the 90-day breaking-change commitment.
    - `lightning_density` number, double, nullable: Lightning strike density (strikes per unit area per time window).
    - `lightning_prob` integer, nullable: Lightning probability, percent.
    - `conditions` string, required: Human-readable weather description.
    - `icon` string, required: Icon slug for UI rendering.
    - `severe` boolean: True when severe conditions are present or imminent.
  - `hourly` Hourly[]: Hourly forecast, ordered ascending by `time`.
    - `time` string, date-time, required: Forecast valid time (ISO 8601, UTC).
    - `temperature` number, double, nullable
    - `feels_like` number, double, nullable
    - `humidity` number, double, nullable
    - `wind_speed` number, double, nullable
    - `wind_direction` integer, nullable
    - `precip_probability` number, double, nullable: Probability of measurable precipitation (0–100) for the hour. When `include=nowcast` is requested, near-term hours overlapping the radar nowcast horizon are reconciled against the nowcast summary: wet peak intensity may floor values upward; when peak intensity is `none`, covered hours may be pulled down toward zero (unless the hour already carries precip conditions). Null when the hour's amount is no data and no model PoP covers the hour.
    - `precip_amount` number, double, nullable: Liquid-equivalent precipitation for the hour ending at `time`. `0` is a dry hour; null when no forecast source covers the hour.
    - `conditions` string
    - `icon` string
    - `severe` boolean
    - `wind_gust` number, double, nullable
    - `dew_point` number, double, nullable
    - `cloud_cover` number, double, nullable
    - `visibility` number, double, nullable
    - `pressure` number, double, nullable
    - `thunderstorm_probability` number, double, nullable
    - `uv_index` number, double, nullable
    - `aqi` number, nullable
    - `precip_type` 'none' | 'rain' | 'snow' | 'mix' | 'hail' | 'unknown'
    - `precip_type_rain_prob` number, double, nullable
    - `precip_type_snow_prob` number, double, nullable
    - `precip_type_freezing_rain_prob` number, double, nullable
    - `precip_type_ice_pellets_prob` number, double, nullable
    - `cape` number, double, nullable: Convective available potential energy (J/kg).
    - `snow_amount` number, double, nullable: Null when no snow is forecast or no source covers the hour.
    - `cloud_ceiling` number, double, nullable: NBM forecast cloud-base height above ground, in the `units.altitude` unit. Null when no ceiling is reported (clear or high thin cloud).
    - `cloud_cover_low` number, double, nullable: HRRR low-cloud-layer cover, percent (CONUS). Present only with `cloud_cover_mid` and `cloud_cover_high` for hours inside the HRRR horizon (18 h, or 48 h on 00/06/12/18Z cycles).
    - `cloud_cover_mid` number, double, nullable: HRRR mid-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`.
    - `cloud_cover_high` number, double, nullable: HRRR high-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`.
    - `snow_level` number, double, nullable
    - `thunder_possible` boolean: True when any hourly thunder indicator (`thunderstorm_probability` or `cape`) crosses the blender's lightning-risk threshold for this hour. This is a distilled flag; use `uncertainty.*` for raw probabilistic fields.
    - `uncertainty` object, nullable: **Experimental** (`x-stability: experimental`): excluded from the 90-day breaking-change commitment. NBM probabilistic companion fields for a single hour. Each field is optional and absent when NBM does not carry it for the hour (ceiling and visibility probabilities are not on every frame); the sub-object is omitted when no field is available. Each property names the catalog `standard_name` and products it is read from, so the same series can be fetched through `/v1/series`.
      - `temperature_stddev` number, double, nullable: Ensemble standard deviation of `temperature`, in the requested temperature unit (a spread, converted without the °F/°C offset). Catalog: `temperature_2m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `dew_point_stddev` number, double, nullable: Ensemble standard deviation of `dew_point`, in the requested temperature unit (offset-free). Catalog: `dew_point_2m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `wind_speed_stddev` number, double, nullable: Ensemble standard deviation of `wind_speed`, in the requested wind speed unit. Catalog: `wind_speed_10m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `wind_gust_stddev` number, double, nullable: Ensemble standard deviation of `wind_gust`, in the requested wind speed unit. Catalog: `wind_gusts_10m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `visibility_prob_below_1mi` number, double, nullable: Probability (percent) that visibility is below 1 mi. Catalog: `visibility_prob_below_1mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `visibility_prob_below_2mi` number, double, nullable: Probability (percent) that visibility is below 2 mi. Catalog: `visibility_prob_below_2mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `visibility_prob_below_3mi` number, double, nullable: Probability (percent) that visibility is below 3 mi. Catalog: `visibility_prob_below_3mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `visibility_prob_below_5mi` number, double, nullable: Probability (percent) that visibility is below 5 mi. Catalog: `visibility_prob_below_5mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `ceiling_prob_below_500ft` number, double, nullable: Probability (percent) that the cloud ceiling is below 500 ft AGL. Catalog: `ceiling_prob_below_500ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `ceiling_prob_below_1000ft` number, double, nullable: Probability (percent) that the cloud ceiling is below 1000 ft AGL. Catalog: `ceiling_prob_below_1000ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `ceiling_prob_below_2000ft` number, double, nullable: Probability (percent) that the cloud ceiling is below 2000 ft AGL. Catalog: `ceiling_prob_below_2000ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `ceiling_prob_below_3000ft` number, double, nullable: Probability (percent) that the cloud ceiling is below 3000 ft AGL. Catalog: `ceiling_prob_below_3000ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
      - `ceiling_prob_below_6600ft` number, double, nullable: Probability (percent) that the cloud ceiling is below 6600 ft AGL. Catalog: `ceiling_prob_below_6600ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.
  - `daily` Daily[]: Daily forecast, ordered ascending by `date`.
    - `date` string, date, required: Forecast date (ISO 8601, local to the location's timezone).
    - `high` number, double, nullable: Daytime maximum. May be null on the `relative_day: -1` overnight row, whose daytime is already past: that row reports a maximum only when the forecast cycle still carries one for the date, and never estimates it from the overnight minimum. When `high` is null the row also omits `day`.
    - `low` number, double, nullable: Overnight minimum, covering 00:00–05:59 of the following calendar date. Present on the `relative_day: -1` row, whose remaining overnight hours are still ahead.
    - `conditions` string
    - `icon` string
    - `severe` boolean
    - `precip_probability` number, double, nullable
    - `precip_total` number, double, nullable: Null when no forecast source covers any hour of the day.
    - `snow_total` number, double, nullable: Null when no forecast source covers any hour of the day.
    - `ice_total` number, double, nullable: Null when no forecast source covers any hour of the day.
    - `wind_speed_max` number, double, nullable
    - `wind_gust_max` number, double, nullable: Maximum sustained-gust value across the calendar day.
    - `wind_direction` integer, nullable
    - `humidity_avg` number, double, nullable
    - `cloud_cover_avg` number, double, nullable
    - `uv_index_max` number, double, nullable: The day's maximum UV index. It is worked out from the hourly forecast the server builds for the request, `hourly_hours` long, even when `include` omits `hourly`. On a day that forecast covers from 06:00 to 18:00 local, it is the highest hourly UV index. On other days it is an estimate from the UV index at solar noon and the forecast cloud cover, raised to any higher hourly value. Late in the day, today's value reflects the remaining forecast, not the observed peak: after 18:00 local the estimate can use tonight's forecast cloud, so a clear day followed by a cloudy evening can report a low maximum. Any day's value can change between requests as the hours the forecast covers move. Null when it can't be determined.
    - `cape_max` number, double, nullable
    - `dew_point_max` number, double, nullable
    - `dew_point_min` number, double, nullable
    - `pressure_avg` number, double, nullable
    - `visibility_min` number, double, nullable
    - `cloud_ceiling_min` number, double, nullable
    - `thunderstorm_probability_max` number, double, nullable
    - `aqi_max` number, double, nullable
    - `feels_like_high` number, double, nullable
    - `feels_like_low` number, double, nullable
    - `solar_radiation_max` number, double, nullable: Peak downward shortwave radiation across the day (W/m^2).
    - `snow_level` number, double, nullable
    - `thunder_possible` boolean: Distilled flag; true if any hour in the day crosses the blender's lightning-risk threshold.
    - `astro` Astro: Astronomical data for the requested location and date.
      - `civil_dawn` string, date-time, nullable: Null when civil twilight does not begin or end on this date.
      - `sunrise` string, date-time, nullable: Null on polar day and night (see `polar`).
      - `solar_noon` string, date-time, nullable
      - `sunset` string, date-time, nullable: Null on polar day and night (see `polar`).
      - `civil_dusk` string, date-time, nullable: Null when civil twilight does not begin or end on this date.
      - `day_length` integer, required: Seconds from sunrise to sunset; 86400 under midnight sun, 0 in polar night.
      - `daylight_length` integer, required: Seconds from civil dawn to civil dusk; 86400 when civil twilight never ends (white nights, midnight sun). 0 when there is no civil daylight or it is unknown; see civil_dawn, civil_dusk and polar.
      - `moonrise` string, date-time, nullable: Null when no moonrise occurs on this date.
      - `moonset` string, date-time, nullable: Null when no moonset occurs on this date.
      - `moon_phase` number, double, required: Moon phase (0 = new, 0.5 = full, 1 = new).
      - `moon_phase_name` 'New Moon' | 'Waxing Crescent' | 'First Quarter' | 'Waxing Gibbous' | 'Full Moon' | 'Waning Gibbous' | 'Last Quarter' | 'Waning Crescent', required
      - `moon_illumination` number, double, required
      - `polar` 'midnight_sun' | 'polar_night' | 'null', nullable: Set when the sun neither rises nor sets on this date: midnight_sun (above the horizon all day; day_length is 86400) or polar_night (below it all day; day_length is 0). sunrise and sunset are then null. Null when the sun rises and sets.
    - `day` DailyPeriod: Forecast data for a single period (day or night) within a `Daily` entry.
      - `temperature` number, double, nullable: High for `day`, low for `night`; null when the half has no temperature.
      - `wind_speed` number, double, nullable
      - `wind_direction` integer, nullable
      - `precip_probability` number, double, nullable: Probability of measurable precipitation (0–100) for the day or night period. When `include=nowcast` is requested, the active near-term period may be raised or lowered from hourly values reconciled against the radar nowcast (NWS longer-period ≥ subperiod consistency).
      - `precip_total` number, double, nullable: Null when no forecast source covers any hour of the day.
      - `snow_total` number, double, nullable: Null when no forecast source covers any hour of the day.
      - `ice_total` number, double, nullable: Null when no forecast source covers any hour of the day.
      - `humidity` number, double, nullable
      - `cloud_cover` number, double, nullable
      - `wind_gust` number, double, nullable
      - `dew_point` number, double, nullable
      - `feels_like` number, double, nullable: High-for-day / low-for-night of hourly apparent temperature.
      - `visibility` number, double, nullable
      - `pressure` number, double, nullable
      - `cloud_ceiling` number, double, nullable
      - `uv_index` number, double, nullable: Max UV index within the period (day only; ~0 overnight).
      - `thunderstorm_probability` number, double, nullable
      - `cape` number, double, nullable
      - `aqi` number, double, nullable
      - `precip_type` '' | 'none' | 'rain' | 'snow' | 'mix' | 'sleet' | 'freezing_rain' | 'thunderstorms' | 'unknown'
      - `thunder_possible` boolean
      - `conditions` string
      - `icon` string
      - `severe` boolean
      - `narrative` string: Human-readable narrative for this period.
    - `night` DailyPeriod: Forecast data for a single period (day or night) within a `Daily` entry.
      - `temperature` number, double, nullable: High for `day`, low for `night`; null when the half has no temperature.
      - `wind_speed` number, double, nullable
      - `wind_direction` integer, nullable
      - `precip_probability` number, double, nullable: Probability of measurable precipitation (0–100) for the day or night period. When `include=nowcast` is requested, the active near-term period may be raised or lowered from hourly values reconciled against the radar nowcast (NWS longer-period ≥ subperiod consistency).
      - `precip_total` number, double, nullable: Null when no forecast source covers any hour of the day.
      - `snow_total` number, double, nullable: Null when no forecast source covers any hour of the day.
      - `ice_total` number, double, nullable: Null when no forecast source covers any hour of the day.
      - `humidity` number, double, nullable
      - `cloud_cover` number, double, nullable
      - `wind_gust` number, double, nullable
      - `dew_point` number, double, nullable
      - `feels_like` number, double, nullable: High-for-day / low-for-night of hourly apparent temperature.
      - `visibility` number, double, nullable
      - `pressure` number, double, nullable
      - `cloud_ceiling` number, double, nullable
      - `uv_index` number, double, nullable: Max UV index within the period (day only; ~0 overnight).
      - `thunderstorm_probability` number, double, nullable
      - `cape` number, double, nullable
      - `aqi` number, double, nullable
      - `precip_type` '' | 'none' | 'rain' | 'snow' | 'mix' | 'sleet' | 'freezing_rain' | 'thunderstorms' | 'unknown'
      - `thunder_possible` boolean
      - `conditions` string
      - `icon` string
      - `severe` boolean
      - `narrative` string: Human-readable narrative for this period.
    - `threats` DailyThreat[]: Per-day hazard callouts (winter, severe, heat, etc.).
      - `type` string, required: Hazard category (e.g., `severe`, `winter`, `heat`, `fire`).
      - `risk` string: Raw risk identifier from the upstream source.
      - `probability` number, double: Fractional probability (0-1) when known.
      - `risk_label` string, required: Human-readable risk label (e.g., `"Slight"`, `"Enhanced"`).
    - `relative_day` integer: Signed calendar offset from the local current date at request time (-1 = yesterday, 0 = today, 1 = tomorrow). Before 6 AM local an extra entry with `relative_day: -1` prepends the in-progress overnight row. That row always reports `low` and `night`, but `high` and `day` only when the cycle still carries that date's daytime maximum. Select today by `relative_day == 0` rather than by position in the array, which shifts by one inside that window.
    - `active_period` 'day' | 'night', nullable: Non-null on exactly the row containing the current local time (`day` during 06:00–17:59, `night` during 18:00–05:59). Clients use this with `relative_day` to label rows Today/Tonight without wall-clock heuristics.
    - `uncertainty` object, nullable: **Experimental** (`x-stability: experimental`): excluded from the 90-day breaking-change commitment. NBM probabilistic companion fields for a single day, from the NBM daily products. All fields are optional. The daily rules store these under the same `standard_name`s as the hourly fields; each property names the daily product it is read from. NBM stores daily rows stamped 00Z and 12Z UTC on each date; a row's fields aggregate the rows stamped with its `date` (the UTC date, as for every daily field), `*_avg` fields as their mean and `*_max` fields as their maximum.
      - `temperature_stddev_avg` number, double, nullable: Day-mean ensemble standard deviation of temperature, in the requested temperature unit (offset-free). Catalog: `temperature_2m_stddev` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.
      - `dew_point_stddev_avg` number, double, nullable: Day-mean ensemble standard deviation of dew point, in the requested temperature unit (offset-free). Catalog: `dew_point_2m_stddev` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.
      - `wind_speed_stddev_avg` number, double, nullable: Day-mean ensemble standard deviation of wind speed, in the requested wind speed unit. Catalog: `wind_speed_10m_stddev` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.
      - `wind_gust_stddev_avg` number, double, nullable: Day-mean ensemble standard deviation of wind gust, in the requested wind speed unit. Catalog: `wind_gusts_10m_stddev` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.
      - `temperature_max_stddev` number, double, nullable: Ensemble spread around `high`: NBM's standard deviation of the daily maximum temperature, in the requested temperature unit (offset-free). Present only when `high` is NBM's native daily maximum, read from the same stored row; absent when `high` comes from another source or was adjusted. Catalog: `temperature_2m_max_stddev` on `nbm_daily_temps` / `nbm_daily_temps_ak` / `nbm_daily_temps_hi`.
      - `temperature_min_stddev` number, double, nullable: Ensemble spread around `low`: NBM's standard deviation of the daily minimum temperature, in the requested temperature unit (offset-free). Present only when `low` is NBM's native daily minimum, read from the same stored row (the following morning's minimum when that one sets `low`); absent when `low` comes from another source or was adjusted. Catalog: `temperature_2m_min_stddev` on `nbm_daily_temps` / `nbm_daily_temps_ak` / `nbm_daily_temps_hi`.
      - `visibility_prob_below_1mi_max` number, double, nullable: Day-maximum probability (percent) that visibility is below 1 mi. Catalog: `visibility_prob_below_1mi` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.
      - `ceiling_prob_below_1000ft_max` number, double, nullable: Day-maximum probability (percent) that the cloud ceiling is below 1000 ft AGL. Catalog: `ceiling_prob_below_1000ft` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.
    - `climate` DailyClimate: The day's normal paired with this forecast's departure from it. Temperatures are in the requested `unit` system, matching `high` and `low` on the same row. Departures are signed so that positive is warmer than normal, and are differences rather than absolute temperatures. Every field is nullable: a station can hold a normal high without a normal low, and a forecast day can fall in a gap in the station's record. A `0` departure means the forecast landed exactly on the normal, which is not the same as an absent one. Station provenance for these values — which station, how far away, and over what base period — lives in the top-level `climate` block.
      - `normal_high` number, double, nullable: Normal daily maximum temperature for this day of year.
      - `normal_low` number, double, nullable: Normal daily minimum temperature for this day of year.
      - `high_departure` number, double, nullable: Forecast high minus normal high; positive is warmer than normal.
      - `low_departure` number, double, nullable: Forecast low minus normal low; positive is warmer than normal.
  - `alerts` Alert[]: Active NWS alerts for the location, present whenever the section is included and its fetch succeeded. `[]` means no alerts were in effect in the latest alerts snapshot, which can trail `meta.generated_at` by up to about 30 seconds. Absent when the section is not included, or when its fetch failed (a `meta.warnings` entry with `section: alerts` says so). A stale replay (`X-SW-Stale: 1` header, or a `stale_response` warning on a batch row) repeats the alerts as of its own `meta.generated_at`, not the current set.
    - `id` string, required: Unique alert identifier (the NWS message id). Pass it to `GET /v1/alerts/{id}` for the full alert.
    - `event` string, required
    - `severity` 'Extreme' | 'Severe' | 'Moderate' | 'Minor' | 'Unknown', required
    - `urgency` 'Immediate' | 'Expected' | 'Future' | 'Past' | 'Unknown', required
    - `certainty` 'Observed' | 'Likely' | 'Possible' | 'Unlikely' | 'Unknown', required
    - `headline` string, required
    - `sender` string, required: Issuing NWS office identifier.
    - `status` string, required: Alert lifecycle status (e.g., `Actual`, `Exercise`, `Test`).
    - `onset` string, date-time, required
    - `ends` string, date-time, required
    - `effective` string, date-time, required
    - `expires` string, date-time, required
    - `expiration` string, date-time, required: Final expiration time used for cache invalidation.
    - `area_desc` string: Human-readable affected area.
    - `highlights` object: Map of salient callouts extracted from the alert text (e.g., `{"hazard": "Strong winds", "impact": "Loose debris possible"}`).
  - `outlooks` Outlook[]: SPC convective outlooks active for the location, present whenever the section is included and its fetch succeeded. `[]` means no outlook was in effect in the latest outlooks snapshot, which can trail `meta.generated_at` by up to about 30 seconds. Absent when the section is not included, or when its fetch failed (a `meta.warnings` entry with `section: outlooks` says so). A stale replay repeats the outlooks as of its own `meta.generated_at`.
    - `product_id` string, required: Upstream product identifier (e.g., `spc_otlk_day1`).
    - `type` string, required: Hazard category (`severe`, `tornado`, `hail`, `wind`, etc.).
    - `day` integer, required: Day number relative to issuance (1 = today).
    - `risk` string: Raw risk identifier from the upstream source.
    - `probability` number, double: Fractional probability (0-1) when the source provides one.
    - `risk_label` string, required: Human-readable risk label.
    - `valid` string, date-time, required
    - `expires` string, date-time, required
    - `significant` boolean: The location falls inside SPC's hatched significant-severe area for this hazard — tornadoes of EF2+, hail 2 in or larger, or wind gusts of 65 kt or more. SPC ships this as its own polygon, so it is reported here as a flag on the hazard it qualifies rather than as a separate entry. Absent (or `false`) outside the hatched area.
  - `astro` Astro: Astronomical data for the requested location and date.
    - `civil_dawn` string, date-time, nullable: Null when civil twilight does not begin or end on this date.
    - `sunrise` string, date-time, nullable: Null on polar day and night (see `polar`).
    - `solar_noon` string, date-time, nullable
    - `sunset` string, date-time, nullable: Null on polar day and night (see `polar`).
    - `civil_dusk` string, date-time, nullable: Null when civil twilight does not begin or end on this date.
    - `day_length` integer, required: Seconds from sunrise to sunset; 86400 under midnight sun, 0 in polar night.
    - `daylight_length` integer, required: Seconds from civil dawn to civil dusk; 86400 when civil twilight never ends (white nights, midnight sun). 0 when there is no civil daylight or it is unknown; see civil_dawn, civil_dusk and polar.
    - `moonrise` string, date-time, nullable: Null when no moonrise occurs on this date.
    - `moonset` string, date-time, nullable: Null when no moonset occurs on this date.
    - `moon_phase` number, double, required: Moon phase (0 = new, 0.5 = full, 1 = new).
    - `moon_phase_name` 'New Moon' | 'Waxing Crescent' | 'First Quarter' | 'Waxing Gibbous' | 'Full Moon' | 'Waning Gibbous' | 'Last Quarter' | 'Waning Crescent', required
    - `moon_illumination` number, double, required
    - `polar` 'midnight_sun' | 'polar_night' | 'null', nullable: Set when the sun neither rises nor sets on this date: midnight_sun (above the horizon all day; day_length is 86400) or polar_night (below it all day; day_length is 0). sunrise and sunset are then null. Null when the sun rises and sets.
  - `gis` GIS, required: Geographic metadata for the resolved location. Contains coordinate data, political boundaries, population, timezone, and nearest NEXRAD radar site metadata. Radar data URLs (visual-products manifest, NEXRAD imagery) are **not** returned in the v1 response. Radar imagery will ship later as a separate `/v1/radar/*` family.
    - `lat` number, double, required
    - `lon` number, double, required
    - `city` string: City name covering the point (if the point lies inside city limits).
    - `nearest_city` string: Nearest named city (useful when `city` is not populated).
    - `state` string: State or equivalent subdivision (abbreviation).
    - `county` string
    - `country` string: ISO 3166-1 alpha-2 country code.
    - `population` integer, nullable: Approximate population of `city` or `nearest_city`.
    - `timezone` string, required: IANA timezone identifier.
    - `nearest_radar` NearestRadar: Metadata for the nearest NEXRAD radar site. Data URLs are not included here; a later `/v1/radar/*` family will return them.
      - `id` string, required: Radar site code without ICAO prefix (e.g., `LOT`, `OKX`, `AKC`).
      - `name` string, required
      - `lat` number, double, required
      - `lon` number, double, required
      - `distance_km` number, double, required: Great-circle distance from the requested point to the radar site, rounded to 0.1 km.
    - `nearest_station` NearestStation: Metadata for the nearest METAR observation station.
      - `id` string, required: ICAO station identifier (e.g., `KJFK`).
      - `name` string
      - `state` string
      - `lat` number, double, required
      - `lon` number, double, required
      - `distance_km` number, double, required: Great-circle distance from the requested point to the station, rounded to 0.1 km.
      - `elevation_m` number, nullable
  - `units` Units: Unit labels for the numeric fields in this response. The exact label set is determined by the `units` request parameter.
    - `temperature` 'F' | 'C', required: Temperature unit.
    - `wind_speed` 'mph' | 'kph', required
    - `pressure` 'inHg' | 'hPa', required
    - `visibility` 'mi' | 'km', required
    - `precip_amount` 'in' | 'mm', required
    - `altitude` 'm' | 'ft': Unit for altitude-dimensioned fields (snow_level, cloud_ceiling).
    - `solar_radiation` 'W/m^2': Unit for irradiance fields (currently always `W/m^2`).
  - `attribution` Attribution, required: Attribution for the data sources that contributed to this response. `sources` is an explicit list of canonical identifiers suitable for compliance checklists; `notice` is a human-readable catch-all covering licensing / credit language.
    - `sources` string[], required: Canonical source identifiers that contributed data. `EPA AirNow` is listed only when the body as returned carries an air-quality value (`current.aqi`, `pm25`, `ozone` or `pm10`, `hourly[].aqi`, `daily[].aqi_max`, `daily[].day.aqi` or `daily[].night.aqi`), so a `view` or `fields` selection that drops those columns drops it.
    - `notice` string, required: Human-readable attribution notice.
  - `narrations` Narrations: Rule-based narrative text for current and short-term conditions, generated deterministically from the blended forecast (not an LLM). **Experimental.** Field shapes and prose styling may change without notice: excluded from the 90-day breaking-change commitment.
    - `current` string: Time-of-day-aware summary of current conditions. Weaves in, when available, a category- and time-aware alert cue (e.g. "Elevated fire-weather conditions through 1 AM."), imminent-precip nowcast, an optional plain-language cue from an overlapping SPC Mesoscale Discussion (truncated enrichment summary; not a second LLM call), the current observation, the next-few-hours trend, and a time-aware high/low (today's high during the day; tonight's low and tomorrow's high in the evening). Phrasing varies by location and hour for natural variety but is stable between refreshes within the hour. Temperatures follow the requested `units`. Requires `current` data; `daily` adds the high/low, `alerts` the alert cue, `nowcast` the imminent-precip cue, and overlapping mesoscale discussions the optional guidance cue. Area Forecast Discussions and Hazardous Weather Outlooks are not folded into this field.
    - `short_term` string: Narrative covering the next few hours.
  - `guidance` Guidance: NWS forecaster text products (AFD, HWO, NOW, HLS, MCD, MPD, PMD) for the point: structured fields plus raw bulletin `body` when available. Canonical weather surface for forecaster guidance; map polygons live on `GET /v1/guidance/mesoscale` and `GET /v1/guidance/precipitation` (bodies omitted there). `guidance.now` is the WFO short-term NOW product, not the radar `nowcast` block. `guidance.hls` is the WFO Hurricane Local Statement for the office covering the point; omitted when none is live. `guidance.pmds` is the latest national Prognostic Discussion per enabled desk (SPD/EPD/HI/CA/MRD/THR/ENS); omitted when empty. Distinct from `guidance.mpds` (WPC precipitation discussions). **Experimental.** Field shapes may change without notice: excluded from the 90-day breaking-change commitment.
    - `afd` AFDGuidance: Area Forecast Discussion: structured fields plus optional raw bulletin `body`.
      - `synopsis` string
      - `key_takeaway` string
      - `confidence` string: Forecaster-stated confidence level (free text).
      - `issued_at` string, date-time, nullable
      - `wfo` string: Issuing Weather Forecast Office identifier.
      - `body` string: Full AFD bulletin text when available.
    - `hwo` HWOGuidance: Hazardous Weather Outlook: structured fields plus optional raw bulletin `body`.
      - `day_one` string: Day-one hazard summary text.
      - `summary` string: Pipeline LLM overall hazard summary when enrichment has run.
      - `has_active_hazards` boolean, required
      - `issued_at` string, date-time, nullable
      - `body` string: Full HWO bulletin text when available.
    - `now` NOWGuidance: WFO short-term NOW product (NWS NOW, not radar nowcast): pipeline LLM `summary` plus optional raw bulletin `body`. No `key_takeaway` (NOW uses the default enricher prompt).
      - `summary` string: Pipeline LLM summary when enrichment has run.
      - `issued_at` string, date-time, nullable
      - `wfo` string: Issuing Weather Forecast Office identifier.
      - `body` string: Full NOW bulletin text when available.
    - `hls` HLSGuidance: WFO Hurricane Local Statement for the office covering the point: pipeline LLM `summary` plus optional raw bulletin `body`. Latest product for the WFO (`office_for_point`); omitted when none is live. No map overlay and no tropical-storm join.
      - `summary` string: Pipeline LLM summary when enrichment has run.
      - `issued_at` string, date-time, nullable
      - `wfo` string: Issuing Weather Forecast Office identifier.
      - `body` string: Full HLS bulletin text when available.
    - `active_mcds` integer, required: Count of non-expired SPC Mesoscale Discussions whose polygons overlap the location (matches `mcds` length when present).
    - `mcds` MCDGuidance[]: SPC Mesoscale Discussions overlapping the location (structured fields plus raw `body`). Expired products are omitted. Cap 10 (newest first).
      - `mcd_number` integer: SPC mesoscale discussion number.
      - `issued_at` string, date-time, nullable
      - `expires_at` string, date-time, nullable: End of the product valid window when parsed.
      - `concerning` string: SPC "Concerning..." line (e.g. severe potential / watch possible).
      - `areas_affected` string
      - `watch_probability` integer: Stated probability of watch issuance, percent.
      - `summary` string: Pipeline LLM summary when enrichment has run.
      - `body` string: Full MCD bulletin text when available.
    - `active_mpds` integer: Count of non-expired WPC Mesoscale Precipitation Discussions whose polygons overlap the location (matches `mpds` length when present). Always emitted; not in `required` so older clients keep decoding.
    - `mpds` MPDGuidance[]: WPC Mesoscale Precipitation Discussions overlapping the location. Expired products are omitted. Cap 10 (newest first).
      - `mpd_number` integer: WPC mesoscale precipitation discussion number.
      - `issued_at` string, date-time, nullable
      - `expires_at` string, date-time, nullable
      - `concerning` string
      - `areas_affected` string
      - `flash_flood_tier` string: Headline tier (possible / likely / occurring).
      - `summary` string
      - `body` string
    - `pmds` PMDGuidance[]: Latest national Prognostic Discussion per enabled desk: WPC (`PMDSPD`, `PMDEPD`, `PMDHI`, `PMDCA`) and CPC (`PMDMRD` 6-10/8-14 day, `PMDTHR` week-2 hazards, `PMDENS` ENSO). Omitted when empty. Distinct from `mpds` (precipitation discussions). Each desk is fetched by `awips_id` (limit 1) and dropped when older than its cadence allows (3 days; `PMDCA` and `PMDTHR` 7, `PMDENS` 45).
      - `awips_id` string: Full AWIPS identifier (e.g. PMDSPD).
      - `pmd_desk` string: Desk suffix after the PMD prefix (SPD, EPD, HI, CA, MRD, THR, ENS).
      - `issued_at` string, date-time, nullable
      - `summary` string: Pipeline LLM summary when enrichment has run.
      - `body` string: Full PMD bulletin text when available.
    - `swo_day1_risk` string: SPC Day-1 convective outlook categorical risk label (when active).
  - `discussions` Discussions: Deprecated. Prefer `include=guidance` (`guidance.mcds` / `guidance.afd`). Map polygons live on `GET /v1/guidance/mesoscale`. **Experimental.** Field shapes may change without notice: excluded from the 90-day breaking-change commitment.
    - `mesoscale` MCDGuidance[], required: Active SPC Mesoscale Discussions whose polygons overlap the location. Expired products are omitted. Cap 10 (newest first).
      - `mcd_number` integer: SPC mesoscale discussion number.
      - `issued_at` string, date-time, nullable
      - `expires_at` string, date-time, nullable: End of the product valid window when parsed.
      - `concerning` string: SPC "Concerning..." line (e.g. severe potential / watch possible).
      - `areas_affected` string
      - `watch_probability` integer: Stated probability of watch issuance, percent.
      - `summary` string: Pipeline LLM summary when enrichment has run.
      - `body` string: Full MCD bulletin text when available.
    - `area_forecast` AFDGuidance: Area Forecast Discussion: structured fields plus optional raw bulletin `body`.
      - `synopsis` string
      - `key_takeaway` string
      - `confidence` string: Forecaster-stated confidence level (free text).
      - `issued_at` string, date-time, nullable
      - `wfo` string: Issuing Weather Forecast Office identifier.
      - `body` string: Full AFD bulletin text when available.
  - `nowcast` Nowcast: Minute-scale MRMS radar precipitation nowcast for the queried location. Populated when `include=nowcast` is requested.
    - `interval_seconds` integer: Length of each step, in seconds: the most common spacing between the frame's lead times (300 today).
    - `generated_at` string, date-time: Nowcast reference (anchor) time from the latest radar frame.
    - `summary` NowcastSummary: Server-computed headline for the first precipitation episode in the nowcast window, derived from the frame's steps. Each step covers one `interval_seconds` interval starting at its `time`. The frame's first lead is `generated_at` + 5 minutes today, and the window runs to the last lead's `time` + one interval. A step is wet when its `intensity` is not `none`. Precipitation starts at the first of two consecutive wet steps; a lone wet step is not an onset. The episode ends at the first two consecutive dry steps after the onset (one dry step inside the episode does not end it). A lead the radar frame has no value for is a missing step: it is left out of `points` but keeps its place in the window and counts as dry. So wet points either side of a missing step are not consecutive, and missing steps count toward the two dry steps that end an episode, including missing leads at the end of the window. When leading leads are missing, `points[0]` is later than `generated_at` + 5 minutes. With no onset, the summary is `type: none`, `peak_intensity: none`, and `starts_at`, `ends_at`, `duration_minutes` and `already_occurring` are omitted. The summary is computed from the latest nowcast frame and the request time; `generated_at` says how old that frame is.
      - `starts_at` string, date-time: `time` of the onset step: the first of two consecutive wet steps. Omitted when there is no onset.
      - `ends_at` string, date-time, nullable: End of the last wet step of the episode: that step's `time` plus `interval_seconds`. Omitted while precipitation continues to the end of the window (no two consecutive dry or missing steps after the onset), and when there is no onset.
      - `duration_minutes` integer, nullable: Minutes from `starts_at` to `ends_at`, so a multiple of the step length (at least 10 for a two-step episode). Omitted whenever `ends_at` is omitted.
      - `type` string: Precipitation type of the onset step, as on `points[].type` (`rain`, `snow`, `freezing_rain`, `wintry_mix`, or `unknown` for an unrecognized radar type code), or `none` when there is no onset.
      - `peak_intensity` 'none' | 'light' | 'moderate' | 'heavy': Highest `intensity` across the episode, from the onset to its end (or to the end of the window while it continues). Later episodes in the window are not counted. `none` when there is no onset.
      - `already_occurring` boolean: True when `starts_at` is at or before the request time: the onset step has begun. A lone wet step is not an onset, so it never sets this. The first lead is `generated_at` + 5 minutes, so a request in the first minutes after a frame sees false here with `starts_at` still ahead. Omitted when false, and when there is no onset.
    - `points` NowcastPoint[]
      - `time` string, date-time, required
      - `type` string, required: Radar precipitation type at this step: `none`, `rain`, `snow`, `freezing_rain`, `wintry_mix`, `no_coverage` (outside radar coverage), or `unknown` for an unrecognized radar type code.
      - `intensity_dbz` number, double, required
      - `intensity` 'none' | 'light' | 'moderate' | 'heavy', required
  - `climate` ClimateNormals: Day-of-year climate normals for a point, read from the gridded normals across CONUS and otherwise from the nearest station with records. The resolved station is always named, whichever supplied the values.
    - `station` ClimateNormalsStation, required: The station a normals series was drawn from. Normals describe this station, not necessarily the point you asked about.
      - `station_id` string, required: GHCN station identifier.
      - `name` string: Station name as published by NCEI.
      - `state` string: Two-letter state or territory code.
      - `latitude` number, double, required: Station latitude.
      - `longitude` number, double, required: Station longitude.
      - `elevation_m` number, double: Station elevation in metres. Worth comparing against your point: an elevation difference biases normals more than distance does.
      - `distance_km` number, double, required: Great-circle distance from the requested point to the station, rounded to 0.1 km. The single number that says how much to trust this series.
    - `meta` ClimateNormalsMeta, required: Provenance for a normals response.
      - `normals_period` string: Base period the normals were computed over.
      - `source` string, required: Where the values came from: `station` for the NCEI station product, `grid` for gridded normals, `mixed` when a series draws on both.
      - `normals_method` 'official' | 'derived': How the normals were produced, which `source` does not answer. `official` means the provider publishes them as a normals product -- true of both the NCEI station normals and the NCEI gridded normals, so a station/grid mix is still wholly official. `derived` means they were computed from raw climate data, which is a weaker claim a client should qualify. Absent when upstream does not report it.
      - `unit` 'imperial' | 'metric', required: Unit system applied to the values: °F and inches for `imperial`, °C and millimetres for `metric`.
      - `approximate` boolean: True when a station more than 50 km away supplied values that are actually being served — `station.distance_km` says how far. It is not a plain distance test. A `grid` day is the requested point's own normal, so no station distance makes it approximate, and a series whose `source` is `grid` is never flagged however far the named station sits. A `mixed` series is flagged, because the station-sourced days in it genuinely are approximations.
      - `missing_days` integer: How many days in the requested range no normal is available for, from either the grid or the station. Those days are still present in `days` with their temperature fields omitted.
    - `days` ClimateNormalsDay[], required: Per-day normals series, in requested date order.
      - `date` string, date, required: Calendar date this slice was requested for.
      - `month` integer, required: Month of the day-of-year slice (1-12).
      - `day` integer, required: Day of month (1-31).
      - `normal_high` number, double: Average daily maximum temperature.
      - `normal_low` number, double: Average daily minimum temperature.
      - `normal_mean` number, double: Average daily mean temperature.
      - `normal_high_stddev` number, double: Standard deviation of the daily maximum. An interval, so it scales without the freezing-point offset: 3 °C of spread is 5.4 °F. Always from the nearest station, including on a day whose temperatures came from the grid — the gridded normals publish no standard deviations. On such a day the temperatures describe your point while this describes the named station, so read `station.distance_km` before narrating "unusually warm" off this spread. The same holds for `normal_low_stddev`, `normal_mean_stddev`, `years` and `completeness_flag`.
      - `normal_low_stddev` number, double: Standard deviation of the daily minimum. Station-sourced on the same terms as `normal_high_stddev`.
      - `normal_mean_stddev` number, double: Standard deviation of the daily mean. Station-sourced on the same terms as `normal_high_stddev`.
      - `normal_precip` number, double: Average daily precipitation amount — inches when `unit=imperial`, millimetres when `unit=metric`. The exact inverse of `normal_high_stddev`: that is always station-sourced, this is always grid-sourced. The station product publishes no daily precipitation normal at all, so this field comes only from the gridded CONUS product and is **omitted** on any day whose `source` is `station` — which includes every point outside CONUS. A series over Alaska or Hawaii carries temperatures with no precipitation until gridded normals exist there. `0` is a real value and is not the same as omitted: a daily normal is a 30-year mean spread across wet and dry days, so much of the arid West is a few hundredths of an inch. Do not treat a present zero as missing data.
      - `years` integer: Years of the base period contributing to this day, at the station. Station-sourced on the same terms as `normal_high_stddev`, so it does not describe the depth of a gridded value.
      - `completeness_flag` string: NCEI per-value completeness code, for the station. Station-sourced on the same terms as `normal_high_stddev`.
    - `location` Location: Resolved location.
      - `lat` number, double, required
      - `lon` number, double, required
      - `name` string, required: Human-readable place name, usually without the state; see `region`. The resolved place name, else the city, else the nearest city. Empty when none is known.
      - `region` string: State or region abbreviation.
      - `country` string: ISO 3166-1 alpha-2 country code.
      - `timezone` string, required: IANA timezone identifier.
      - `county` string: County name (US only).
      - `zip` string: ZIP code (US only).
  - `metar` Metar: Compact nearest METAR observation for the queried point. Populated when `include=metar` is requested. Sky layers and present-weather tokens are parsed from `raw_metar` (stop at RMK); the raw string itself is not forwarded. Omitted when no recent station is near the point or the fetch fails; those cases report `coverage_limited` or a fetch warning in `meta.warnings[]` rather than failing the request.
    - `station_id` string, required
    - `distance_km` number, required: Great-circle distance from the requested point to the station, rounded to 0.1 km.
    - `observed_at` string, date-time, required
    - `age_min` number, required: Minutes between `observed_at` and response `meta.generated_at`.
    - `ceiling_ft` integer: Lowest BKN/OVC/VV base, feet AGL.
    - `layers` MetarSkyLayer[]: Parsed sky groups. Empty when the station reports CLR/SKC/NSC. Omitted when sky condition is unknown.
      - `cover` 'FEW' | 'SCT' | 'BKN' | 'OVC' | 'VV', required
      - `base_ft` integer: Layer base in feet AGL. Omitted when the group has no height (`VV///`).
      - `convective` 'CB' | 'TCU'
    - `weather` string[]: Raw present-weather tokens (`-RA`, `VCTS`, `TSRA`).
  - `meta` Meta, required: Per-response metadata: response schema version, generation time, upstream data freshness, and any non-fatal warnings.
    - `schema_version` string, required: Response schema version, a semver string (currently `1.0.0`). The major part changes only with a non-additive change, which follows the deprecation policy. Cache on it if you cache response shapes.
    - `generated_at` string, date-time, required: Server time at which this response body was generated.
    - `freshness` SourceFreshness[]: Freshness metadata for each upstream data source used.
      - `source` string, required: Source family (e.g., `rtma`, `hrrr`, `nbm`, `mrms`).
      - `product_id` string, required: Specific product identifier.
      - `reference_time` string, date-time, nullable: Model run / observation reference time.
      - `latest_valid` string, date-time, nullable: Latest valid time represented in the source.
      - `age_seconds` integer, required: Seconds elapsed since `reference_time`.
    - `warnings` MetaWarning[]: Non-fatal warnings generated during request fulfillment. Each entry has a stable `code` clients can branch on and a human-readable `detail`.
      - `code` 'partial_fetch' | 'upstream_timeout' | 'upstream_unavailable' | 'coverage_limited' | 'geocode_ambiguous' | 'stale_response' | 'stale_source' | 'not_enabled', required: Stable machine-parseable warning code. Clients should branch on `code`, not on `detail`. - `partial_fetch`: the section could not be fully fetched and is missing or incomplete. - `upstream_timeout`: an upstream weather source timed out, so the section is missing or incomplete. - `upstream_unavailable`: an upstream weather source was unreachable, so the section is missing or incomplete. - `coverage_limited`: the data does not cover this location (for example no nearby station, or a product outside its domain); retrying will not help. - `geocode_ambiguous`: the place name matched more than one location; the response uses the best match. - `stale_response`: the section was served from a recent cached copy because the upstream source failed. - `stale_source`: one upstream product's latest data is older than its source's freshness limit. `product_id` names the product (its age is the `meta.freshness` entry with the same `product_id`) and `fields` lists the fields in `section` it leaves `null` (or omits, for optional fields such as `aqi_category`). Unlike `stale_response`, the response was not replayed from cache. - `not_enabled`: the requested section is not enabled for this API key. New codes may be added without a breaking-change announcement; treat an unknown code as informational.
      - `section` string, required: Response section the warning applies to (e.g., `"alerts"`, `"hourly"`, `"outlooks"`). May also be `"_request"` for request-level warnings.
      - `detail` string: Human-readable explanation.
      - `product_id` string: Catalog product id (`GET /v1/catalog/{product_id}`) of the upstream product the warning is about; the same id as the product's `meta.freshness` entry. Set only for `stale_source`.
      - `fields` string[]: Response fields in `section` that this warning leaves `null` or omits. Set only for `stale_source`.
    - `hourly_sampling` object: Present only when `hourly_step` dropped at least one hourly row. Echoes the effective thinning so a thinned series is distinguishable from a short forecast: every row is kept below `from_hour` hours from `hourly[0].time`, then one row every `step_hours` hours.
      - `step_hours` integer, required: Effective `hourly_step`.
      - `from_hour` integer, required: Effective `hourly_step_from`.

## Other responses

- `400`: Bad request.
- `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.
- `404`: Resource not found: a location that cannot be resolved (unknown ZIP, unrecognised free-text location), no upstream data for the location, or a missing resource. On a route that checks coverage (its description says so), a location outside the 50 US states and DC returns type `https://smarterweather.com/errors/outside-coverage`; that and the location-not-found problem carry `next_steps` (`recommended = coverage`).
- `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.
- `502`: Bad gateway. The service received an invalid response from an upstream data source. Transient; clients should retry with backoff.
- `503`: Service unavailable. An upstream dependency is unavailable or shedding load (type `service-unavailable`). Transient; retry with backoff and honor any `Retry-After` header.
- `504`: Gateway timeout. An upstream data source did not respond within the service's budget.

---

[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)
