Files
weatherapi/docs/api.md

505 lines
16 KiB
Markdown
Raw Normal View History

2026-06-11 14:13:46 +00:00
# weatherapi HTTP API
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
This is the canonical public HTTP contract for `weatherapi`. The service is a
read-only API over the latest weather records available in the configured
weatherfeeder-populated Postgres database.
2026-05-29 20:46:06 -05:00
## Base URL
2026-06-11 14:13:46 +00:00
All paths are relative to the deployment root:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
```text
http://localhost:8080
```
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Use your deployment host in production.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Authentication
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
`weatherapi` does not implement authentication or authorization. Put access
control in front of the service when a deployment requires it.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Response Envelope
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Successful JSON and XML responses use a top-level envelope:
2026-05-29 20:46:06 -05:00
```json
{
2026-06-11 14:13:46 +00:00
"data": {}
2026-05-29 20:46:06 -05:00
}
```
2026-06-11 14:13:46 +00:00
When no latest/current resource exists, the request still succeeds and returns
`data: null`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Text responses are rendered from endpoint-specific templates. When the payload
is missing, the templates return a short no-data message.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Formats
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Every implemented endpoint can produce:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Format | Media type |
| --- | --- |
| `json` | `application/json` |
| `xml` | `application/xml` |
| `text` | `text/plain` |
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Format selection is:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
1. `format` query parameter;
2. `Accept` header;
3. configured `server.default_format`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
`format` values are case-insensitive. Unsupported formats return `406
Not Acceptable` with an error envelope.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Shared Query Rules
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Unknown query parameters are rejected with `400 Bad Request`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Parameter | Values | Default | Supported on |
| --- | --- | --- | --- |
| `format` | `json`, `xml`, `text` | configured default | all endpoints |
| `units` | `metric`, `us` | `metric` | all endpoints |
| `precision` | integer `0` through `2` | `0` | observations, current conditions, forecasts |
2026-06-11 15:46:00 +00:00
| `tz` or `TZ` | timezone selector | UTC/no conversion | forecasts, discussions, weather stories, outlooks |
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
`units`, `format`, and `precision` values are normalized case-insensitively
where applicable. `units=metric` returns metric field names; `units=us` returns
US-customary field names for unit-bearing payloads. Alerts, discussions, and
weather stories accept `units` but their current payload fields are not
2026-06-11 15:46:00 +00:00
materially changed by it. Outlooks also accept `units` without changing payload
values or field names.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
`precision` controls numeric rounding. The default `0` rounds to whole numbers.
2026-06-11 15:46:00 +00:00
`precision` is rejected on alerts, discussions, weather stories, and outlooks.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Timezone selectors accepted by `tz` / `TZ`:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
- IANA timezone names such as `America/Chicago`;
- common US abbreviations such as `CDT`, `CST`, `EDT`, `EST`, `MDT`, `MST`,
`PDT`, and `PST`;
- UTC offsets in `+H`, `+HH`, `+HH:MM`, `-H`, `-HH`, or `-HH:MM` form, bounded
to `-14:00` through `+14:00`;
- aliases `Chicago` and `Stl`, both mapped to `America/Chicago`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
If both `tz` and `TZ` are provided, their values must match
case-insensitively. Timezone conversion affects rendered timestamps and forecast
`/today` and `/tomorrow` day-slice filtering. Without a timezone parameter,
day-slice routes use UTC.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Errors
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
API errors use a stable error envelope:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
```json
{
"error": {
"code": "invalid_parameter",
"message": "..."
}
}
```
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Implemented API error statuses:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Status | Code | Cause |
| --- | --- | --- |
| `400 Bad Request` | `invalid_parameter` | unknown query parameter, invalid parameter value, invalid timezone, conflicting `tz` / `TZ`, or unsupported parameter on a route |
| `406 Not Acceptable` | `unsupported_format` | requested response format is not supported by the endpoint/renderers |
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Unhandled service or database errors are returned as server errors by the
runtime.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Behavior Not Implemented
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
`weatherapi` does not implement pagination, cache-control headers, rate-limit
headers, idempotency keys, retries, writes, or historical browsing outside the
implemented latest-resource and forecast day-slice routes.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
## Endpoints
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
### Observations
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
```http
GET /observations
```
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Returns the latest weather observation.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Query parameters: `format`, `units`, `precision`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Metric `data` fields:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Field | Type | Notes |
| --- | --- | --- |
| `stationId`, `stationName` | string | optional |
| `timestamp` | RFC3339 datetime | required when `data` is not null |
| `conditionCode` | integer | WMO weather code |
| `isDay` | boolean | optional |
| `textDescription` | string | optional |
| `temperatureC`, `dewpointC`, `apparentTemperatureC` | number | optional |
| `windDirectionDegrees`, `windSpeedKmh`, `windGustKmh` | number | optional |
| `barometricPressurePa`, `visibilityMeters` | number | optional |
| `relativeHumidityPercent` | number | optional |
| `presentWeather` | array | optional |
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
US mode replaces unit-bearing fields with `temperatureF`, `dewpointF`,
`apparentTemperatureF`, `windSpeedMph`, `windGustMph`,
`barometricPressureInHg`, and `visibilityMiles`. Direction and percentage
fields keep the same names.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Example:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
```http
GET /observations?units=us&precision=1
```
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
### Current Conditions
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
```http
GET /conditions/current
```
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Returns current conditions aggregated from recent `observations` rows. The
implemented observation window is 30 minutes.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Query parameters: `format`, `units`, `precision`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Common `data` fields:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Field | Type | Notes |
| --- | --- | --- |
| `conditionText` | string | optional text derived from WMO code and day/night flag |
| `isDay` | boolean | optional |
| `relativeHumidityPercent` | number | optional |
| `windDirectionDegrees` | number | optional |
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Metric fields: `temperatureC`, `apparentTemperatureC`, `dewpointC`,
`windSpeedKmh`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
US fields: `temperatureF`, `apparentTemperatureF`, `dewpointF`,
`windSpeedMph`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Example:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
```http
GET /conditions/current?format=json&precision=0
2026-05-29 20:46:06 -05:00
```
2026-06-11 14:13:46 +00:00
### Active Alerts
2026-05-29 20:46:06 -05:00
```http
2026-06-11 14:13:46 +00:00
GET /alerts/active
2026-05-29 20:46:06 -05:00
```
Returns the latest stored alert run filtered to alerts active at request time,
omitting older alerts superseded by newer alert references in the same run.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Query parameters: `format`, `units`.
2026-05-29 20:46:06 -05:00
When no latest alert run exists, `data` is null. When a latest run exists but
no alerts are currently active, `data` remains an object and `alerts` is an
empty array.
2026-06-11 14:13:46 +00:00
Run `data` fields:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Field | Type | Notes |
| --- | --- | --- |
| `locationId`, `locationName` | string | optional |
| `asOf` | RFC3339 datetime | required when `data` is not null |
| `latitude`, `longitude` | number | optional |
| `alerts` | array | active alerts, possibly empty |
2026-05-29 20:46:06 -05:00
Alerts are active when `messageType` is not `Cancel`, `effective` is absent or
at or before request time, and the alert end boundary is absent or after request
time. The end boundary prefers `ends`; if `ends` is absent, `expires` is used as
a fallback for older rows or providers that do not supply an alert-period end.
`onset` is presented when available but is not used as the active boundary.
After active-time filtering, alerts referenced by another alert in the same run
are omitted as superseded. References from update and cancel messages are both
honored, even when the referencing alert is not itself returned.
2026-06-11 14:13:46 +00:00
Alert fields include `id`, `event`, `headline`, `severity`, `urgency`,
`certainty`, `status`, `messageType`, `category`, `response`, `description`,
`instruction`, `sent`, `effective`, `onset`, `ends`, `expires`,
`areaDescription`, `senderName`, and `references`. Most alert fields are
optional except `id` when an alert item is present. `ends` is the alert-period
end; `expires` is provider expiration metadata.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Reference fields are `id`, `identifier`, `sender`, and `sent`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Example:
```http
GET /alerts/active?format=text
2026-05-29 20:46:06 -05:00
```
2026-06-11 15:46:00 +00:00
### Convective Outlooks
```http
GET /outlooks/convective
GET /outlooks/convective/active
```
Returns the latest SPC convective outlook run reconstructed from
2026-06-12 13:07:17 +00:00
weatherfeeder-owned `weather.outlook.v2` Postgres tables.
2026-06-11 15:46:00 +00:00
Route behavior:
2026-06-12 13:07:17 +00:00
- `/outlooks/convective` returns the latest run with stored location-filtered
outlook polygons unless user filters are supplied.
2026-06-11 15:46:00 +00:00
- `/outlooks/convective/active` adds an active-time filter using the server's
current UTC time. Outlooks are active when `validFrom <= now < validTo`.
When no latest run exists, `data` is null. When a run exists but filters match
2026-06-12 13:07:17 +00:00
no outlooks, `data` remains an object and `outlooks` and `discussions` are
empty arrays. Outlook endpoints use latest-run semantics and do not accumulate
historical active outlooks across older runs.
2026-06-11 15:46:00 +00:00
Query parameters:
| Parameter | Supported on | Values |
| --- | --- | --- |
| `format`, `units`, `tz` / `TZ` | all outlook routes | shared rules above |
| `day` | all outlook routes | `1`, `2`, or `3` |
| `outlookType` | all outlook routes | `categorical`, `tornado`, `hail`, or `wind` |
2026-06-12 13:07:17 +00:00
`outlookType` values are normalized case-insensitively. Weatherfeeder v2
outlooks are already filtered for the configured location. `precision`,
`containsLocation`, and unknown parameters are rejected.
2026-06-11 15:46:00 +00:00
Run `data` fields:
| Field | Type | Notes |
| --- | --- | --- |
| `locationId`, `locationName` | string | optional |
| `latitude`, `longitude` | number | optional |
| `asOf` | RFC3339 datetime | required when `data` is not null |
| `issuedAt` | RFC3339 datetime | optional |
| `outlooks` | array | ordered outlook polygons, possibly empty |
2026-06-12 13:07:17 +00:00
| `discussions` | array | ordered day-level discussions, possibly empty |
2026-06-11 15:46:00 +00:00
Outlook fields:
| Field | Type | Notes |
| --- | --- | --- |
| `id`, `provider`, `product`, `outlookType`, `label` | string | required when an outlook is present |
| `day` | integer | SPC outlook day |
| `labelText`, `forecaster` | string | optional |
2026-06-11 15:46:00 +00:00
| `severityRank` | integer | optional |
| `validFrom`, `validTo`, `issuedAt`, `expiresAt` | RFC3339 datetime | required when an outlook is present |
| `sourceUrl`, `imageUrl` | string | optional |
| `containsLocation` | boolean | whether the outlook polygon contains the configured location |
| `geometry` | GeoJSON | stored outlook geometry |
2026-06-12 13:07:17 +00:00
Discussion fields:
| Field | Type | Notes |
| --- | --- | --- |
| `day` | integer | SPC outlook day |
| `headline`, `summary`, `discussion` | string | optional |
| `updatedAt` | RFC3339 datetime | optional |
2026-06-11 15:46:00 +00:00
GeoJSON coordinates use standard GeoJSON coordinate order: longitude, then
latitude. Timezone conversion applies to run `asOf`, run `issuedAt`, and each
2026-06-12 13:07:17 +00:00
outlook's `validFrom`, `validTo`, `issuedAt`, and `expiresAt`, and discussion
`updatedAt`. Active filtering compares instants and is not changed by the
presentation timezone. Endpoint filters also filter `discussions` to days
represented by retained outlooks.
2026-06-11 15:46:00 +00:00
Examples:
```http
GET /outlooks/convective?day=1&outlookType=categorical
GET /outlooks/convective/active?format=text&tz=CDT
2026-06-11 15:46:00 +00:00
```
Example JSON response:
```json
{
"data": {
"locationId": "stl",
"locationName": "St. Louis",
"asOf": "2026-06-11T18:00:00Z",
"issuedAt": "2026-06-11T17:00:00Z",
"outlooks": [
{
"id": "spc-day1-cat-slight",
"provider": "spc",
"product": "convective",
"day": 1,
"outlookType": "categorical",
"label": "SLGT",
"labelText": "Slight Risk",
"severityRank": 5,
"validFrom": "2026-06-11T18:00:00Z",
"validTo": "2026-06-12T12:00:00Z",
"issuedAt": "2026-06-11T17:00:00Z",
"expiresAt": "2026-06-12T12:00:00Z",
"containsLocation": true,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-91.0, 38.0],
[-90.0, 38.0],
[-90.0, 39.0],
[-91.0, 38.0]
]
]
}
}
2026-06-12 13:07:17 +00:00
],
"discussions": [
{
"day": 1,
"headline": "Severe storms possible",
"summary": "Scattered severe storms are possible.",
"discussion": "SPC discussion text.",
"updatedAt": "2026-06-11T17:30:00Z"
}
2026-06-11 15:46:00 +00:00
]
}
}
```
Text format uses the shared convective outlook template for both outlook routes
and renders a no-data message when `data` is null.
2026-06-11 15:46:00 +00:00
2026-06-11 14:13:46 +00:00
### Forecasts
2026-05-29 20:46:06 -05:00
```http
2026-06-11 14:13:46 +00:00
GET /forecast/hourly
GET /forecast/hourly/today
GET /forecast/hourly/tomorrow
GET /forecast/narrative
GET /forecast/narrative/today
GET /forecast/narrative/tomorrow
2026-05-29 20:46:06 -05:00
```
2026-06-11 14:13:46 +00:00
Returns the latest hourly or narrative forecast run. `/today` and `/tomorrow`
return a copy of the latest run with `periods` filtered by each period's
`startTime` in the resolved timezone. If no periods match, `data` remains an
object and `periods` is an empty array.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Query parameters: `format`, `units`, `precision`, `tz` / `TZ`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
Run `data` fields:
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
| Field | Type | Notes |
| --- | --- | --- |
| `locationId`, `locationName` | string | optional |
| `issuedAt` | RFC3339 datetime | required when `data` is not null |
| `updatedAt` | RFC3339 datetime | optional |
| `product` | string | `hourly` or `narrative` for implemented routes |
| `latitude`, `longitude` | number | optional |
| `elevationMeters` or `elevationFeet` | number | optional, depends on `units` |
| `periods` | array | ordered forecast periods |
Metric period fields:
`startTime`, `endTime`, `name`, `isDay`, `conditionCode`,
`textDescription`, `temperatureC`, `temperatureCMin`, `temperatureCMax`,
`dewpointC`, `relativeHumidityPercent`, `windDirectionDegrees`,
`windSpeedKmh`, `windGustKmh`, `barometricPressurePa`, `visibilityMeters`,
`apparentTemperatureC`, `cloudCoverPercent`,
`probabilityOfPrecipitationPercent`, `precipitationAmountMm`,
`snowfallDepthMm`, and `uvIndex`.
2026-05-29 20:46:06 -05:00
2026-06-11 14:13:46 +00:00
US mode uses the same non-unit fields and replaces unit-bearing fields with
`temperatureF`, `temperatureFMin`, `temperatureFMax`, `dewpointF`,
`windSpeedMph`, `windGustMph`, `barometricPressureInHg`, `visibilityMiles`,
`apparentTemperatureF`, `precipitationAmountIn`, `snowfallDepthIn`, and
`elevationFeet` at run level.
`startTime` and `endTime` are required for each period. Other period fields are
optional, including `conditionCode`; narrative forecasts may omit it.
Examples:
2026-05-30 07:06:29 -05:00
```http
2026-06-11 14:13:46 +00:00
GET /forecast/hourly?units=us&precision=1&TZ=-5
GET /forecast/narrative/tomorrow?format=text&tz=Chicago
2026-05-30 07:06:29 -05:00
```
2026-06-11 14:13:46 +00:00
### Forecast Discussions
```http
GET /discussion
GET /discussion/key-messages
GET /discussion/short-term
GET /discussion/long-term
2026-05-30 07:06:29 -05:00
```
2026-06-11 14:13:46 +00:00
Returns the latest forecast discussion or a focused subresource.
Query parameters: `format`, `units`, `tz` / `TZ`.
Full discussion `data` fields:
| Field | Type | Notes |
| --- | --- | --- |
| `officeId`, `officeName` | string | optional |
| `product` | string | currently `afd` from weatherfeeder data |
| `issuedAt` | RFC3339 datetime | required when `data` is not null |
| `updatedAt` | RFC3339 datetime | optional |
| `keyMessages` | array of strings | optional |
| `shortTerm`, `longTerm` | object | optional section objects |
Discussion section fields are `qualifier`, `issuedAt`, and `text`, all
optional.
Subresources return the same metadata plus only their focused field:
`keyMessages`, `shortTerm`, or `longTerm`.
Example:
2026-05-30 07:06:29 -05:00
```http
2026-06-11 14:13:46 +00:00
GET /discussion/short-term?format=text&tz=CDT
2026-05-30 07:06:29 -05:00
```
2026-06-11 14:13:46 +00:00
### Weather Stories
```http
GET /weatherstories
GET /weatherstories/latest
2026-05-30 07:06:29 -05:00
```
2026-06-11 14:13:46 +00:00
`/weatherstories` returns the latest weather-story run and its ordered stories.
`/weatherstories/latest` returns the latest individual story.
Query parameters: `format`, `units`, `tz` / `TZ`.
Run `data` fields:
| Field | Type | Notes |
| --- | --- | --- |
| `officeId` | string | optional |
| `asOf` | RFC3339 datetime | required when `data` is not null |
| `stories` | array | ordered story objects |
Story fields:
| Field | Type | Notes |
| --- | --- | --- |
| `officeId` | string | optional |
| `startTime`, `endTime`, `updatedAt` | RFC3339 datetime | required when a story is present |
| `title`, `description`, `altText`, `downloadUrl` | string | optional |
| `priority` | boolean | required when a story is present |
| `order` | integer | required when a story is present |
Examples:
2026-05-29 20:46:06 -05:00
```http
2026-06-11 14:13:46 +00:00
GET /weatherstories?tz=America/Chicago
GET /weatherstories/latest?format=xml
2026-05-29 20:46:06 -05:00
```
2026-06-11 14:13:46 +00:00
## Copyable Requests
See [`examples/requests.http`](../examples/requests.http) for a compact set of
requests covering the implemented endpoint families.