# weatherapi External API

This document describes the public HTTP API exposed by `weatherapi` for external consumers.

## Base URL

The service is typically served at your deployment host, for example:

- `https://weather.example.com`

All paths below are relative to the service root.

## Common Conventions

### Response envelope

All endpoints return a top-level envelope:

- `data`: endpoint payload or `null` when no current/latest resource is available.

JSON example:

```json
{
  "data": {"...": "..."}
}
```

### Output format

Supported via `format` query parameter (case-insensitive):

- `json` (default)
- `xml`
- `text`

### Units

Supported via `units` query parameter (case-insensitive):

- `metric` (default)
- `us`

Endpoints that include unit-based numeric fields return either metric or US field variants depending on this value.

### Precision

Supported where documented via `precision` query parameter:

- integer range: `0` to `2`
- controls decimal rounding of numeric output fields

### Timezone (`tz` / `TZ`)

Supported where documented:

- accepted values include:
  - IANA timezone names (example: `America/Chicago`)
  - common US abbreviations (example: `CDT`, `EST`)
  - UTC offsets in `±H`, `±HH`, or `±HH:MM` (example: `-5`, `+09:30`)
  - aliases including `Chicago` and `Stl`
- `tz` and `TZ` are treated equivalently
- if both are provided, they must match exactly or the request fails

Timezone affects datetime rendering and day-slice filtering for `/today` and `/tomorrow` forecast routes.
It also affects weather story and discussion datetime rendering.

### Query validation

- Unknown query parameters are rejected with `400 Bad Request`.
- Invalid parameter values are rejected with `400 Bad Request`.

Error response body follows the service error envelope; exact fields may vary by error type.

## Endpoints

## `GET /observations`

Returns the latest weather observation.

Query parameters:

- `units`: `metric` | `us`
- `format`: `json` | `xml` | `text`
- `precision`: `0..2`

Response `data` fields:

- `stationId` (string, optional)
- `stationName` (string, optional)
- `timestamp` (RFC3339 datetime, required)
- `conditionCode` (integer WMO code, required)
- `isDay` (boolean, optional)
- `textDescription` (string, optional)
- Metric mode fields:
  - `temperatureC`, `dewpointC`, `windSpeedKmh`, `windGustKmh`, `barometricPressurePa`, `visibilityMeters`, `relativeHumidityPercent`, `apparentTemperatureC` (number, optional)
  - `windDirectionDegrees` (number, optional)
- US mode fields:
  - `temperatureF`, `dewpointF`, `windSpeedMph`, `windGustMph`, `barometricPressureInHg`, `visibilityMiles`, `relativeHumidityPercent`, `apparentTemperatureF` (number, optional)
  - `windDirectionDegrees` (number, optional)
- `presentWeather` (array, optional)

## `GET /alerts/active`

Returns the latest active alert run.

Query parameters:

- `units`: `metric` | `us` (accepted; does not materially alter alert payload)
- `format`: `json` | `xml` | `text`

Response `data` fields:

- Weather alert run object from canonical model (includes run metadata and active alerts list).

## `GET /conditions/current`

Returns current conditions synthesized from latest observation/forecast data.

Query parameters:

- `units`: `metric` | `us`
- `format`: `json` | `xml` | `text`
- `precision`: `0..2`

Response `data` fields:

- Common:
  - `conditionText` (string, optional)
  - `isDay` (boolean, optional)
  - `relativeHumidityPercent` (number, optional)
  - `windDirectionDegrees` (number, optional)
- Metric mode:
  - `temperatureC`, `apparentTemperatureC`, `dewpointC`, `windSpeedKmh` (number, optional)
- US mode:
  - `temperatureF`, `apparentTemperatureF`, `dewpointF`, `windSpeedMph` (number, optional)

## Forecast endpoints

- `GET /forecast/hourly`
- `GET /forecast/hourly/today`
- `GET /forecast/hourly/tomorrow`
- `GET /forecast/narrative`
- `GET /forecast/narrative/today`
- `GET /forecast/narrative/tomorrow`

Query parameters:

- `units`: `metric` | `us`
- `format`: `json` | `xml` | `text`
- `precision`: `0..2`
- `tz` or `TZ`: timezone selector

Day-slice routes:

- `/today` returns periods with `period.startTime` in the current calendar day for the resolved timezone.
- `/tomorrow` returns periods with `period.startTime` in the next calendar day for the resolved timezone.

Response `data` fields:

- Run-level:
  - `locationId` (string, optional)
  - `locationName` (string, optional)
  - `issuedAt` (RFC3339 datetime, required)
  - `updatedAt` (RFC3339 datetime, optional)
  - `product` (string, required; e.g. `hourly`, `narrative`)
  - `latitude`, `longitude` (number, optional)
  - Metric mode: `elevationMeters` (number, optional)
  - US mode: `elevationFeet` (number, optional)
  - `periods` (array, required)
- Period fields:
  - `startTime`, `endTime` (RFC3339 datetime, required)
  - `name` (string, optional)
  - `isDay` (boolean, optional)
  - `conditionCode` (integer WMO code, optional)
  - `textDescription` (string, optional)
  - Metric mode (optional):
    - `temperatureC`, `temperatureCMin`, `temperatureCMax`, `dewpointC`, `windSpeedKmh`, `windGustKmh`, `barometricPressurePa`, `visibilityMeters`, `apparentTemperatureC`, `cloudCoverPercent`, `probabilityOfPrecipitationPercent`, `precipitationAmountMm`, `snowfallDepthMM`, `uvIndex`, `relativeHumidityPercent`, `windDirectionDegrees`
  - US mode (optional):
    - `temperatureF`, `temperatureFMin`, `temperatureFMax`, `dewpointF`, `windSpeedMph`, `windGustMph`, `barometricPressureInHg`, `visibilityMiles`, `apparentTemperatureF`, `cloudCoverPercent`, `probabilityOfPrecipitationPercent`, `precipitationAmountIn`, `snowfallDepthIn`, `uvIndex`, `relativeHumidityPercent`, `windDirectionDegrees`

Notes:

- Narrative periods may omit `conditionCode`.
- Text format uses forecast-specific templates (`hourly` and `narrative`).

## Discussion endpoints

- `GET /discussion`
- `GET /discussion/key-messages`
- `GET /discussion/short-term`
- `GET /discussion/long-term`

Query parameters:

- `units`: `metric` | `us` (accepted; does not materially alter discussion payload)
- `format`: `json` | `xml` | `text`
- `tz` or `TZ`: timezone selector

Response `data` fields:

- `/discussion`:
  - `officeId` (string, optional)
  - `officeName` (string, optional)
  - `product` (string, required)
  - `issuedAt` (RFC3339 datetime, required)
  - `updatedAt` (RFC3339 datetime, optional)
  - `keyMessages` (array of string)
  - `shortTerm` (object, optional)
  - `longTerm` (object, optional)
- `/discussion/key-messages`:
  - `officeId`, `officeName`, `product`, `issuedAt`, `updatedAt`
  - `keyMessages` (array of string)
- `/discussion/short-term`:
  - `officeId`, `officeName`, `product`, `issuedAt`, `updatedAt`
  - `shortTerm` (object, optional)
- `/discussion/long-term`:
  - `officeId`, `officeName`, `product`, `issuedAt`, `updatedAt`
  - `longTerm` (object, optional)

Discussion section object fields:

- `title` (string, optional)
- `narrative` (string, optional)
- `issuedAt` (RFC3339 datetime, optional)

## Weather Stories endpoints

- `GET /weatherstories`
- `GET /weatherstories/latest`

Query parameters:

- `units`: `metric` | `us` (accepted; does not materially alter weather story payloads)
- `format`: `json` | `xml` | `text`
- `tz` or `TZ`: timezone selector

Response behavior:

- `/weatherstories` returns the latest stored weather story run and its ordered stories.
- `/weatherstories/latest` returns the single story with the greatest `updatedAt`.
- When no weather story data exists, `data` is `null`.

`/weatherstories` response `data` fields:

- `officeId` (string, optional)
- `asOf` (RFC3339 datetime, required)
- `stories` (array, required)

Weather story fields:

- `officeId` (string, optional)
- `startTime` (RFC3339 datetime, required)
- `endTime` (RFC3339 datetime, required)
- `updatedAt` (RFC3339 datetime, required)
- `title` (string, optional)
- `description` (string, optional)
- `altText` (string, optional)
- `priority` (boolean, required)
- `order` (integer, required)
- `downloadUrl` (string, optional)

## Examples

### Observation (JSON, metric)

```http
GET /observations?format=json&units=metric&precision=1
```

```json
{
  "data": {
    "stationId": "KSTL",
    "timestamp": "2026-05-29T14:00:00Z",
    "conditionCode": 3,
    "isDay": true,
    "textDescription": "Partly cloudy",
    "temperatureC": 24.4,
    "windSpeedKmh": 17.2,
    "relativeHumidityPercent": 56.0
  }
}
```

### Alerts (JSON)

```http
GET /alerts/active?format=json
```

```json
{
  "data": {
    "asOf": "2026-05-29T14:00:00Z",
    "alerts": []
  }
}
```

### Current conditions (JSON, US)

```http
GET /conditions/current?format=json&units=us&precision=1
```

```json
{
  "data": {
    "conditionText": "Partly cloudy",
    "isDay": true,
    "temperatureF": 75.9,
    "apparentTemperatureF": 76.1,
    "windSpeedMph": 10.7,
    "relativeHumidityPercent": 56.0
  }
}
```

### Forecast narrative (JSON, optional `conditionCode`)

```http
GET /forecast/narrative?format=json&units=metric&precision=1&tz=America/Chicago
```

```json
{
  "data": {
    "locationId": "nws-lsx-grid-90-74",
    "issuedAt": "2026-05-29T10:30:00-05:00",
    "product": "narrative",
    "periods": [
      {
        "startTime": "2026-05-29T13:00:00-05:00",
        "endTime": "2026-05-29T19:00:00-05:00",
        "name": "Today",
        "isDay": true,
        "textDescription": "Partly sunny, with a high near 81.",
        "temperatureC": 27.2,
        "windSpeedKmh": 18.0,
        "probabilityOfPrecipitationPercent": 10.0
      }
    ]
  }
}
```

### Forecast hourly today (text)

```http
GET /forecast/hourly/today?format=text&units=us&precision=1&tz=CDT
```

```text
<plain text forecast output>
```

### Discussion key messages (JSON)

```http
GET /discussion/key-messages?format=json&tz=Chicago
```

```json
{
  "data": {
    "officeId": "LSX",
    "product": "discussion",
    "issuedAt": "2026-05-29T09:25:00-05:00",
    "keyMessages": [
      "Scattered showers possible this evening.",
      "Warmer temperatures this weekend."
    ]
  }
}
```

### Weather stories (JSON)

```http
GET /weatherstories?format=json&tz=America/Chicago
```

```json
{
  "data": {
    "officeId": "LSX",
    "asOf": "2026-05-30T04:00:34-05:00",
    "stories": [
      {
        "officeId": "LSX",
        "startTime": "2026-05-30T03:46:00-05:00",
        "endTime": "2026-05-31T06:00:00-05:00",
        "updatedAt": "2026-05-30T04:00:34-05:00",
        "title": "Several Chances for Rain Through Monday",
        "description": "Scattered showers and thunderstorms remain possible.",
        "altText": "This slide shows the forecast for today through Tuesday.",
        "priority": false,
        "order": 1,
        "downloadUrl": "https://api.weather.gov/offices/LSX/weatherstories/download/3228e499-2aae-45a8-9ff9-1c060311026f"
      }
    ]
  }
}
```

### Latest weather story (JSON)

```http
GET /weatherstories/latest?format=json&tz=CDT
```

```json
{
  "data": {
    "officeId": "LSX",
    "startTime": "2026-05-30T03:46:00-05:00",
    "endTime": "2026-05-31T06:00:00-05:00",
    "updatedAt": "2026-05-30T04:00:34-05:00",
    "title": "Several Chances for Rain Through Monday",
    "description": "Scattered showers and thunderstorms remain possible.",
    "priority": false,
    "order": 1
  }
}
```

### Invalid timezone error example

```http
GET /forecast/narrative?tz=not-a-timezone
```

```json
{
  "error": {
    "code": "invalid_parameter",
    "message": "tz must be a valid timezone"
  }
}
```
