Community. Driven. Weather. Data. | Always Ad-Free | Developer FriendlyChecking statusDiscord
Become a supporter
Sign in
API

Alerts

Active NWS watches, warnings and advisories at a point, in a box, ranked, or framed for you, plus ended alerts for a radar loop and the storm cell behind a warning.


Base URL: https://api.wxalerts.org

Every NWS watch, warning, advisory and statement in effect, as the ingest merges them from NWWS-OI, EMWIN and the api.weather.gov CAP feed. Ask what is in effect at a point, everything inside a map view, the few most serious alerts in the country, or a view chosen for you.

GET /alerts/at?lat=35.2&lon=-97.4
GET /alerts/in-bounds?west=-100&south=30&east=-90&north=40
GET /alerts/ended-in-bounds?west=-100&south=30&east=-90&north=40&minutes=70
GET /alerts/highest-severity?limit=3
GET /alerts/focus?limit=25&strategy=auto
GET /alerts/{identifier}
GET /alerts/page/{short_id}
GET /alerts/page/{short_id}/rundown

No key needed. Browsers block calls from other websites (CORS allows only wxalerts.org), so call it from a server, script or app. These routes are limited per address as described under rate limits.

The whole national feed is the exception:

GET /v1/alerts/active?event=&office=&severity=&limit=500&offset=0
GET /v1/alerts/active/geojson?event=&severity=

Needs an API key (alerts:read). On the bare path these two answer 401 with code api_key_required. Without a key, an in-bounds box around the area you care about gives you the same alerts.

This is polling. To have alerts pushed to you the moment they are issued, and told when they end, use the MQTT alerts feed.

Color by significance, not severity

Each alert carries a CAP severity and a VTEC significance. Color and rank by significance: it is the axis the NWS asks people to read. CAP severity is too coarse to sort on. A Tornado Warning, a Flood Warning and a Winter Storm Watch can all be Severe, plenty of warnings are only Moderate, and a Special Weather Statement is Moderate too, which is not a watch.

significance Meaning
W Warning: occurring or imminent
A Watch: conditions favourable
Y Advisory: less serious
S Statement

significance and phenomena are null on products with no VTEC, chiefly Special Weather Statements and Marine Weather Statements. Fall back to CAP severity for those (Extreme reads as a warning, Severe as a watch, Moderate as an advisory), which is what the API’s own ranking does.

The ranking. Routes that return alerts “most serious first” sort by significance, then by hazard (tornado and tsunami, then storm surge, then flash flood, severe thunderstorm and hurricane, down through winter, fire and flood to marine and beach hazards), then by CAP urgency, then newest. A Flood Warning outranks a Tornado Watch on purpose: a warning is something happening.

The alert object

/alerts/at, /alerts/{identifier} and /v1/alerts/active return alerts without geometry, in this shape. Times are ISO 8601, UTC.

{
  "id": 28449246,
  "vtec_key": "PAFC.BZ.W.0014.2026",
  "cap_id": null,
  "event": "Blizzard Warning",
  "office": "PAFC",
  "phenomena": "BZ",
  "significance": "W",
  "etn": 14,
  "status": "active",
  "severity": "Severe",
  "urgency": "Immediate",
  "certainty": "Observed",
  "issued_at": "2026-11-09T14:15:00Z",
  "onset": "2026-11-09T20:00:00Z",
  "expires": "2026-11-10T01:00:00Z",
  "ends": "2026-11-10T21:00:00Z",
  "ugc": ["AKZ732"],
  "same": ["002063"],
  "headline": "BLIZZARD WARNING REMAINS IN EFFECT FROM 11 AM THIS MORNING TO NOON AKST TUESDAY",
  "description": "WWAK41 PAFC 091415\n\nWSWAER\n\nURGENT - WINTER WEATHER MESSAGE ...",
  "instruction": "A Blizzard Warning means severe winter weather conditions are ...",
  "geom_source": "ugc",
  "sources": ["api", "emwin", "nwws"],
  "updated_at": "2026-11-09T19:06:52.767357Z",
  "short_id": "8k3wWa9l"
}
field
id the API’s numeric id for the alert
vtec_key office.phenomena.significance.ETN.year, the identity a warning keeps through every update. null for products with no VTEC
cap_id the CAP identifier, when the alert came from the CAP feed alone. Usually null when VTEC identifies it
event the NWS event name, e.g. Tornado Warning
office issuing office, four letters (KBRO, PAFC)
phenomena VTEC hazard code: TO tornado, SV severe thunderstorm, FF flash flood, WS winter storm and so on
significance VTEC W, A, Y or S, as above
etn VTEC event tracking number
status active, expired or cancelled (spelled that way, as on the MQTT feed)
severity, urgency, certainty the CAP fields as the NWS sent them
issued_at issuance of the newest product in the alert’s chain, so a follow-up statement moves it
onset when the hazard begins
expires CAP “expect an update by”. Not the end of the hazard: above, the update is due at 01:00 while the blizzard runs to 21:00 the next day
ends when the hazard actually ends, when the product says
ugc NWS zone and county codes covered
same SAME (FIPS) county codes covered, as NOAA Weather Radio uses them
headline, description, instruction the product text, unedited
geom_source where the shape came from: polygon (the storm-based polygon the office drew), ugc (built from the zone or county outlines) or none
sources which feeds delivered it: nwws, emwin, api. See feed latency
updated_at when the row was last seen by the ingest. It moves every minute while an alert is live, so do not read it as “last changed”
short_id eight characters, the alert’s public page: https://wxalerts.org/alerts/{short_id}/

Alerts with geom_source: none have no shape, so they never appear on the routes that search by location (at, in-bounds, ended-in-bounds, focus, /v1/alerts/active/geojson). They are still in /v1/alerts/active.

As GeoJSON

in-bounds, ended-in-bounds, focus and /v1/alerts/active/geojson return features. Each feature’s properties is the object above, plus area (a readable place name built from the zones) on every route except /v1/alerts/active/geojson, which leaves it off to stay small. Geometry is a minified MultiPolygon in longitude, latitude order.

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": 28589429,
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [[[[-97.6, 26.54], [-97.32, 26.6], [-97.29, 26.6], [-97.16, 26.15],
                          [-97.52, 26.15], [-97.69, 26.42], [-97.6, 26.54]]]]
      },
      "properties": {
        "id": 28589429,
        "vtec_key": "KBRO.FA.Y.0065.2026",
        "event": "Flood Advisory",
        "office": "KBRO",
        "phenomena": "FA",
        "significance": "Y",
        "status": "active",
        "severity": "Minor",
        "expires": "2026-11-09T21:30:00Z",
        "ends": "2026-11-09T21:30:00Z",
        "geom_source": "polygon",
        "short_id": "38fIRcsb",
        "area": "Cameron, Willacy (TX)",
        "...": "..."
      }
    }
  ],
  "server_time": "2026-11-09T19:10:39.619349Z",
  "count": 3
}

server_time is the database clock. Use it, not your own clock, to work out how long ago something was issued or how long it has left.

GET /alerts/at

Every active alert covering a point, without geometry: the call for “is anything in effect here”. Newest issued first. An empty list means nothing is in effect at that point.

parameter
lat latitude, -90 to 90. Required
lon longitude, -180 to 180. Required

GET /alerts/in-bounds

Active alerts whose shape touches a box, as a FeatureCollection, most serious first: the order a sidebar list is read in. This is what the live map polls.

parameter
west, east longitude, -180 to 180. Required. west must be less than east; boxes crossing the antimeridian are not supported
south, north latitude, -90 to 90. Required. south must be less than north

A bad box is 422 with a message that says which rule it broke. Responses carry Cache-Control: max-age=30 and an ETag. Send the tag back as If-None-Match and you get 304 Not Modified with no body until something in the active set changes.

GET /alerts/ended-in-bounds

Alerts in a box that ended within the last minutes, for a radar loop. The live set only holds what is in effect now, so a warning that expired twenty minutes ago would be missing from every frame it was in force for. Merge this with in-bounds and a frame at time T can show what was out at T.

parameter
west, south, east, north the box, with the same rules as in-bounds
minutes how far back, 1 to 180. Default 70

Same FeatureCollection shape, most serious first, at most 500 features. status is expired or cancelled, and each feature adds ended_at: the earlier of ends and expires, or for a cancellation the time the cancelling product arrived. An alert was in force at time T when it was issued before T and ended_at is after T.

GET /alerts/highest-severity

The most serious alerts active anywhere, ranked as above. Built for a “what matters right now” strip: no geometry and no text, just enough to label a row and frame a map on it.

parameter
limit 1 to 25. Default 3
{
  "server_time": "2026-11-09T19:07:19.446710Z",
  "alerts": [
    {
      "id": 28449246,
      "vtec_key": "PAFC.BZ.W.0014.2026",
      "bbox": [-145.958206, 60.798342, -144.617057, 61.210759],
      "event": "Blizzard Warning",
      "area": "Thompson Pass (AK)",
      "office": "PAFC",
      "phenomena": "BZ",
      "significance": "W",
      "severity": "Severe",
      "urgency": "Immediate",
      "issued_at": "2026-11-09T14:15:00Z",
      "expires": "2026-11-10T01:00:00Z",
      "ends": "2026-11-10T21:00:00Z",
      "short_id": "8k3wWa9l"
    }
  ]
}

bbox is [west, south, east, north] of the alert’s shape, ready for a map’s fit to bounds call, and null for an alert with no shape.

GET /alerts/focus

Picks a map view for you when you do not have one, and returns only the alerts inside it, most serious first. Useful for a kiosk, a small teaser map or a screenshot bot.

parameter
limit most features to return, 1 to 100. Default 25
strategy auto (default), highest-severity or most-alerts
  • highest-severity frames the top-ranked active warning. Advisories and watches are never the anchor.
  • most-alerts frames the office with the most active warnings and watches (all alerts, if nothing but advisories is out anywhere). Busy Alaska marine offices with a hundred Small Craft Advisories do not win this.
  • auto tries highest-severity and falls back to most-alerts when no warning is in effect.
{
  "server_time": "2026-11-09T19:07:19Z",
  "focus": {
    "reason": "highest-severity",
    "center_lat": 61.0045505,
    "center_lon": -145.28763149999997,
    "bbox": [-146.42760815, 60.0045505, -144.14765484999998, 62.0045505],
    "anchor_event": "Blizzard Warning",
    "anchor_office": "PAFC",
    "anchor_area": "Thompson Pass (AK)"
  },
  "features": ["..."],
  "count": 11,
  "truncated": false
}

focus.reason says which rule chose the view: highest-severity, most-alerts, or none when nothing with a shape is active anywhere (or nothing qualifies for the strategy you asked for). none returns the lower 48 with no features rather than an error, so the map still draws. Use reason and anchor_area to caption the view (“Blizzard Warning, Thompson Pass”); for most-alerts, anchor_event is null and anchor_area names the office’s most serious alert. The bbox is padded around the anchor and at least two degrees on a side, so a small polygon does not fill the screen. truncated: true means more alerts were in the view than limit, and the least serious were dropped.

GET /alerts/{identifier}

One alert, in the alert object shape, by VTEC key (PAFC.BZ.W.0014.2026), CAP id (urn:oid:...) or numeric id. Special Weather Statements have no VTEC key, so look those up by CAP id. Unlike the search routes this also answers for alerts that have ended, while they are retained. No match is 404.

GET /alerts/page/{short_id}

One alert by its eight-character short_id, live or ended: what the alert pages on this site are built from. The alert object, plus:

field
area readable place name, e.g. Maricopa (AZ)
slug event-place-state-date, decoration only. The short id alone identifies the alert
path the canonical page on this site, /alerts/{short_id}/{slug}/
bbox [west, south, east, north] of the shape, or null
geometry the shape as minified GeoJSON, or null

Ended alerts are kept for 30 days and say so in status. A short id that never existed is 404. One whose alert has passed the retention window is 410 Gone, so you can tell “this link was always wrong” from “this alert is over and deleted”. An active alert’s page is cached for 30 seconds, an ended one for a day.

GET /alerts/page/{short_id}/rundown

For a storm-based warning, the storm cell behind it and the places in its path.

{
  "short_id": "9FNqeXAD",
  "status": "cancelled",
  "storm_id": 292687,
  "match": "issue-time",
  "reason": null,
  "cell": {
    "kind": "cell",
    "storm_id": 292687,
    "prob_tor": 1,
    "prob_severe": 2,
    "speed_kmh": 50.9,
    "bearing_deg": 78,
    "valid_time": "2026-11-07T01:12:37Z",
    "lon": -69.381572,
    "lat": 42.483522,
    "...": "..."
  },
  "storm_active": false,
  "valid_time": "2026-11-07T01:12:37Z",
  "as_of": "issue",
  "lead_minutes": null,
  "speed_kmh": null,
  "bearing_deg": null,
  "places": [],
  "pois": []
}

Only Tornado, Severe Thunderstorm, Extreme Wind and Special Marine warnings with a shape get a cell. Anything else answers 200 with reason: "not-a-cell-warning" and everything empty: a flood or winter alert has no storm cell behind it.

field
cell the storm, in exactly the shape of a cell’s properties on the storm cells feed. null when no cell matched
storm_id the storm’s id, for /storms/{storm_id}/track and the rest of the storm cells API
match how the cell was found: issue-time (the ProbSevere cell over the polygon when the warning was issued) or overlap-now (no scan kept for that moment, so the active storm over the polygon now)
as_of latest (the storm’s newest scan) or issue (the scan the warning was matched on, used once the warning has ended or the storm has dissipated)
valid_time the radar scan cell describes
storm_active whether the storm is still being tracked
reason why cell or places is empty: not-a-cell-warning, no-cell-matched, or no-motion (the storm has no track to project yet)
lead_minutes how far ahead places reaches: the warning’s remaining time, held between 15 and 60
speed_kmh, bearing_deg the motion the arrival times use
places up to 30 towns inside the storm’s cone, in arrival order
pois up to 50 schools, hospitals, nursing homes, mobile home parks and other points of interest in the cone, grouped by kind

places and pois are filled only while the warning is active and the storm is still tracked. Each has name, lat, lon, minutes (from valid_time until the storm’s leading edge arrives, 0 if it is already overhead), eta (the same as a time), distance_km down the track, and turn: true when only the turn lobe of the cone reaches it. A place adds display_name, state, county and population; a point of interest adds kind, city and state. The numbers move every scan, so this is cached for 30 seconds even for an ended warning. A short id with no alert is 404.

GET /v1/alerts/active

Every active alert in the country, without geometry, newest issued first. Needs a key (alerts:read).

parameter
event exact event name, case sensitive: Tornado Warning, not tornado warning
office issuing office, exact: KBRO, not BRO
severity CAP severity, exact: Extreme, Severe, Moderate or Minor
limit 1 to 2000. Default 500
offset rows to skip. Default 0

The filters match exactly, so a typo returns an empty list rather than an error. Pagination is limit and offset over a stable order (issue time, then id), with no total count: keep going until a page comes back shorter than limit. The active set is a few hundred alerts on a typical day, so limit=2000 usually takes it in one request, which also avoids the set changing between pages.

GET /v1/alerts/active/geojson

Every active alert that has a shape, as one FeatureCollection, newest issued first, with no area property. Takes event and severity with the same exact matching as above; no paging. Needs a key (alerts:read).

This is the whole national geometry set, often a few megabytes, so it has a tighter limit of its own: plan on 30 requests a minute at most, and past it you get 429 with a Retry-After. You should need far fewer. Responses carry Cache-Control: max-age=30 and an ETag, and a request with If-None-Match gets an empty 304 until the set actually changes. For live updates, MQTT costs nothing between alerts.

Errors

status when
401 a key-only route on the bare path (api_key_required), or a /v1 route without a valid key
404 no alert for that identifier or short id
410 /alerts/page/{short_id} for an alert past the 30 day retention window
422 a parameter out of range or missing, or a box with west >= east or south >= north
429 over a rate limit; wait for Retry-After

May we use Microsoft Clarity to see how pages are used (clicks, scrolling and replays of a visit, with anything you type hidden), plus a random ID linking that to crash reports? Only to find bugs and confusing screens. Never advertising, never sold.