All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
501 lines
15 KiB
Markdown
501 lines
15 KiB
Markdown
# weatherapi HTTP API
|
|
|
|
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.
|
|
|
|
## Base URL
|
|
|
|
All paths are relative to the deployment root:
|
|
|
|
```text
|
|
http://localhost:8080
|
|
```
|
|
|
|
Use your deployment host in production.
|
|
|
|
## Authentication
|
|
|
|
`weatherapi` does not implement authentication or authorization. Put access
|
|
control in front of the service when a deployment requires it.
|
|
|
|
## Response Envelope
|
|
|
|
Successful JSON and XML responses use a top-level envelope:
|
|
|
|
```json
|
|
{
|
|
"data": {}
|
|
}
|
|
```
|
|
|
|
When no latest/current resource exists, the request still succeeds and returns
|
|
`data: null`.
|
|
|
|
Text responses are rendered from endpoint-specific templates. When the payload
|
|
is missing, the templates return a short no-data message.
|
|
|
|
## Formats
|
|
|
|
Every implemented endpoint can produce:
|
|
|
|
| Format | Media type |
|
|
| --- | --- |
|
|
| `json` | `application/json` |
|
|
| `xml` | `application/xml` |
|
|
| `text` | `text/plain` |
|
|
|
|
Format selection is:
|
|
|
|
1. `format` query parameter;
|
|
2. `Accept` header;
|
|
3. configured `server.default_format`.
|
|
|
|
`format` values are case-insensitive. Unsupported formats return `406
|
|
Not Acceptable` with an error envelope.
|
|
|
|
## Shared Query Rules
|
|
|
|
Unknown query parameters are rejected with `400 Bad Request`.
|
|
|
|
| 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 |
|
|
| `tz` or `TZ` | timezone selector | UTC/no conversion | forecasts, discussions, weather stories, outlooks |
|
|
|
|
`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
|
|
materially changed by it. Outlooks also accept `units` without changing payload
|
|
values or field names.
|
|
|
|
`precision` controls numeric rounding. The default `0` rounds to whole numbers.
|
|
`precision` is rejected on alerts, discussions, weather stories, and outlooks.
|
|
|
|
Timezone selectors accepted by `tz` / `TZ`:
|
|
|
|
- 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`.
|
|
|
|
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.
|
|
|
|
## Errors
|
|
|
|
API errors use a stable error envelope:
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "invalid_parameter",
|
|
"message": "..."
|
|
}
|
|
}
|
|
```
|
|
|
|
Implemented API error statuses:
|
|
|
|
| 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 |
|
|
|
|
Unhandled service or database errors are returned as server errors by the
|
|
runtime.
|
|
|
|
## Behavior Not Implemented
|
|
|
|
`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.
|
|
|
|
## Endpoints
|
|
|
|
### Observations
|
|
|
|
```http
|
|
GET /observations
|
|
```
|
|
|
|
Returns the latest weather observation.
|
|
|
|
Query parameters: `format`, `units`, `precision`.
|
|
|
|
Metric `data` fields:
|
|
|
|
| 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 |
|
|
|
|
US mode replaces unit-bearing fields with `temperatureF`, `dewpointF`,
|
|
`apparentTemperatureF`, `windSpeedMph`, `windGustMph`,
|
|
`barometricPressureInHg`, and `visibilityMiles`. Direction and percentage
|
|
fields keep the same names.
|
|
|
|
Example:
|
|
|
|
```http
|
|
GET /observations?units=us&precision=1
|
|
```
|
|
|
|
### Current Conditions
|
|
|
|
```http
|
|
GET /conditions/current
|
|
```
|
|
|
|
Returns current conditions aggregated from recent `observations` rows. The
|
|
implemented observation window is 30 minutes.
|
|
|
|
Query parameters: `format`, `units`, `precision`.
|
|
|
|
Common `data` fields:
|
|
|
|
| Field | Type | Notes |
|
|
| --- | --- | --- |
|
|
| `conditionText` | string | optional text derived from WMO code and day/night flag |
|
|
| `isDay` | boolean | optional |
|
|
| `relativeHumidityPercent` | number | optional |
|
|
| `windDirectionDegrees` | number | optional |
|
|
|
|
Metric fields: `temperatureC`, `apparentTemperatureC`, `dewpointC`,
|
|
`windSpeedKmh`.
|
|
|
|
US fields: `temperatureF`, `apparentTemperatureF`, `dewpointF`,
|
|
`windSpeedMph`.
|
|
|
|
Example:
|
|
|
|
```http
|
|
GET /conditions/current?format=json&precision=0
|
|
```
|
|
|
|
### Active Alerts
|
|
|
|
```http
|
|
GET /alerts/active
|
|
```
|
|
|
|
Returns the latest stored alert run filtered to alerts active at request time.
|
|
|
|
Query parameters: `format`, `units`.
|
|
|
|
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.
|
|
|
|
Run `data` fields:
|
|
|
|
| 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 |
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
Reference fields are `id`, `identifier`, `sender`, and `sent`.
|
|
|
|
Example:
|
|
|
|
```http
|
|
GET /alerts/active?format=text
|
|
```
|
|
|
|
### Convective Outlooks
|
|
|
|
```http
|
|
GET /outlooks/convective
|
|
GET /outlooks/convective/active
|
|
```
|
|
|
|
Returns the latest SPC convective outlook run reconstructed from
|
|
weatherfeeder-owned `weather.outlook.v2` Postgres tables.
|
|
|
|
Route behavior:
|
|
|
|
- `/outlooks/convective` returns the latest run with stored location-filtered
|
|
outlook polygons unless user filters are supplied.
|
|
- `/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
|
|
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.
|
|
|
|
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` |
|
|
|
|
`outlookType` values are normalized case-insensitively. Weatherfeeder v2
|
|
outlooks are already filtered for the configured location. `precision`,
|
|
`containsLocation`, and unknown parameters are rejected.
|
|
|
|
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 |
|
|
| `discussions` | array | ordered day-level discussions, possibly empty |
|
|
|
|
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 |
|
|
| `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 |
|
|
|
|
Discussion fields:
|
|
|
|
| Field | Type | Notes |
|
|
| --- | --- | --- |
|
|
| `day` | integer | SPC outlook day |
|
|
| `headline`, `summary`, `discussion` | string | optional |
|
|
| `updatedAt` | RFC3339 datetime | optional |
|
|
|
|
GeoJSON coordinates use standard GeoJSON coordinate order: longitude, then
|
|
latitude. Timezone conversion applies to run `asOf`, run `issuedAt`, and each
|
|
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.
|
|
|
|
Examples:
|
|
|
|
```http
|
|
GET /outlooks/convective?day=1&outlookType=categorical
|
|
GET /outlooks/convective/active?format=text&tz=CDT
|
|
```
|
|
|
|
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]
|
|
]
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"discussions": [
|
|
{
|
|
"day": 1,
|
|
"headline": "Severe storms possible",
|
|
"summary": "Scattered severe storms are possible.",
|
|
"discussion": "SPC discussion text.",
|
|
"updatedAt": "2026-06-11T17:30:00Z"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
Text format uses the shared convective outlook template for both outlook routes
|
|
and renders a no-data message when `data` is null.
|
|
|
|
### Forecasts
|
|
|
|
```http
|
|
GET /forecast/hourly
|
|
GET /forecast/hourly/today
|
|
GET /forecast/hourly/tomorrow
|
|
GET /forecast/narrative
|
|
GET /forecast/narrative/today
|
|
GET /forecast/narrative/tomorrow
|
|
```
|
|
|
|
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.
|
|
|
|
Query parameters: `format`, `units`, `precision`, `tz` / `TZ`.
|
|
|
|
Run `data` fields:
|
|
|
|
| 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`.
|
|
|
|
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:
|
|
|
|
```http
|
|
GET /forecast/hourly?units=us&precision=1&TZ=-5
|
|
GET /forecast/narrative/tomorrow?format=text&tz=Chicago
|
|
```
|
|
|
|
### Forecast Discussions
|
|
|
|
```http
|
|
GET /discussion
|
|
GET /discussion/key-messages
|
|
GET /discussion/short-term
|
|
GET /discussion/long-term
|
|
```
|
|
|
|
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:
|
|
|
|
```http
|
|
GET /discussion/short-term?format=text&tz=CDT
|
|
```
|
|
|
|
### Weather Stories
|
|
|
|
```http
|
|
GET /weatherstories
|
|
GET /weatherstories/latest
|
|
```
|
|
|
|
`/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:
|
|
|
|
```http
|
|
GET /weatherstories?tz=America/Chicago
|
|
GET /weatherstories/latest?format=xml
|
|
```
|
|
|
|
## Copyable Requests
|
|
|
|
See [`examples/requests.http`](../examples/requests.http) for a compact set of
|
|
requests covering the implemented endpoint families.
|