NWS Alerts
Active weather alerts and warnings from the U.S. National Weather Service.
nws_alerts — the dataset name to pass to the Obscura API.
What one row means
One row is a single active National Weather Service CAP (Common Alerting Protocol) alert — e.g. a Tornado Warning, Small Craft Advisory, or Special Weather Statement for one issuing office/area — as returned by the current snapshot of api.weather.gov/alerts/active, upserted on the alert's stable id and refreshed in place on every subsequent poll while it remains active.
One row per one row per currently-or-recently-active NWS CAP alert, keyed by id (the alert's GeoJSON feature URI); no history of past alert states is retained, only the latest-seen values per id.
Point-in-time availability
Every Obscura dataset carries available_date: the calendar day the publisher made the row
available, day-of, with no session rounding. It is the one column a backtest filters on, and it means the
same thing on every dataset in the catalog.
For nws_alerts: snapshot: available_date = COALESCE(captured_date, synced_at::date), the date this poller captured the active-alerts feed. Equal to ingestion time on a live poll; on a bronze replay it is the original poll day, never the replay day.
Refresh cadence
Obscura refreshes nws_alerts every 6 hours — the most frequent scheduled job that re-collects or re-exports it. This is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when a row became public is recorded per row in available_date.
Schema — 21 columns
The full public column list for nws_alerts, with the meaning of every field. The same
schema is served unauthenticated at https://api.obscura.trade/v1/catalog/nws_alerts.
| Column | Type | Description |
|---|---|---|
| id | text · not null | Primary key: the NWS alert's full GeoJSON feature `id`, a URI like 'https://api.weather.gov/alerts/urn:oid:2.49.0.1.840.0.<uuid>.<rev>.<seq>'. Stable across a single alert's lifecycle; a corrected/updated CAP message on the same underlying hazard gets a new suffix. |
| event | text | NWS event/product name from CAP properties.event, e.g. 'Small Craft Advisory', 'Tornado Warning', 'Special Weather Statement'. |
| severity | text | Extreme / Severe / Moderate / Minor / Unknown. Mutable: refreshed on every re-poll of an existing alert id. |
| certainty | text | Observed / Likely / Possible / Unlikely / Unknown — NWS's confidence the described hazard will occur. Refreshed on every re-poll. |
| urgency | text | Immediate / Expected / Future / Past / Unknown — how soon responsive action is needed. Refreshed on every re-poll. |
| category | text | CAP category code, e.g. 'Met' (meteorological), 'Geo', 'Safety'. Set once at first insert; not updated on subsequent polls. |
| status | text | CAP properties.status: Actual / Exercise / System / Test / Draft — the live active feed mixes real alerts with keepalive Test messages, so this is the filter that keeps test traffic out of the real alert stream. Refreshed on re-poll. |
| message_type | text | CAP properties.messageType: Alert / Update / Cancel / Ack / Error — flags whether this message is a new alert or a cancellation/correction of an existing one. Refreshed on re-poll (an alert can flip to Cancel). |
| description | text | CAP properties.description: the full multi-paragraph free-text alert body — the substantive NLP text payload (`headline` is only a one-liner). Set once at first insert. |
| instruction | text | CAP properties.instruction: free-text protective-action guidance ("Turn around, don't drown…"). Set once at first insert. |
| response | text | CAP properties.response (responseType): Avoid / Shelter / Evacuate / Execute / Monitor / None — the categorical recommended action, not derivable from `event`. Refreshed on re-poll. |
| sender_name | text | CAP properties.senderName: the issuing NWS office, e.g. 'NWS Albuquerque NM'. An entity attribute otherwise only buried in `headline` free text. Set once at first insert. |
| area_desc | text | Free-text NWS area description, e.g. a county/zone name or coastal segment — the human-readable geographic scope of the alert. Set once at insert, not updated on re-poll. |
| headline | text | CAP headline: a human-readable one-line summary combining event, issuing office, and effective/expiry window as free text. Set once at insert, not updated on re-poll. |
| event_time | timestamp with time zone | The timestamp NWS designates as when the described hazard becomes/became effective (CAP properties.effective). A natural event timestamp — not the public-availability moment, which is `synced_at` / `available_date`. Set once at first insert of a given alert id. |
| expires | timestamp with time zone | CAP properties.expires — the timestamp after which this alert is no longer active/valid. Kept fresh via upsert since NWS commonly extends/shortens an alert's expiry across polls of the same id. |
| sent | timestamp with time zone | CAP properties.sent — the moment the CAP message was disseminated (issuance time). A better public-availability proxy than `synced_at` and distinct from `event_time` (effective). Refreshed on re-poll. |
| onset | timestamp with time zone | CAP properties.onset — the timestamp the described hazard is expected to begin, distinct from `event_time` (message-effective). Refreshed on re-poll. |
| ends | timestamp with time zone | CAP properties.ends — the timestamp the described hazard is expected to end, distinct from `expires` (message-validity end). Refreshed on re-poll. |
| captured_date | date | The UTC calendar day the active-alerts feed was POLLED — the day this alert became knowable to us. `Utc::now().date_naive()` on a live run, `BronzeObject::captured_day()` on a bronze replay, so re-parsing an old snapshot keeps its original date instead of stamping the replay day. Deliberately NOT `sent` (the CAP dissemination instant): adopting `sent` would move live values — an alert sent at 23:50 UTC and polled after midnight would shift a day earlier — so `sent` stays a natural column and this one carries our own capture provenance. NULL on rows written before migration 000175, where `available_date` falls back to `synced_at::date` — the same value for a live poll. |
| available_date | date | PUBLIC-availability date = `COALESCE(captured_date, synced_at::date)` — the date this poller observed the alert. DB-generated (STORED), read-only; the point-in-time column to filter/join on — NEVER `event_time`'s date. |
Access nws_alerts
Two delivery paths, one identifier. Both require an Obscura account and an active subscription; the catalog entry and the schema above are public.
import obscura
client = obscura.Client("obs_live_…")
df = client.query(
dataset="nws_alerts",
start="2024-01-01",
)
Create a free account Browse all 95 datasets
Frequently asked questions
What is in the nws_alerts dataset?
Active weather alerts and warnings from the U.S. National Weather Service. One row is a single active National Weather Service CAP (Common Alerting Protocol) alert — e.g. a Tornado Warning, Small Craft Advisory, or Special Weather Statement for one issuing office/area — as returned by the current snapshot of api.weather.gov/alerts/active, upserted on the alert's stable id and refreshed in place on every subsequent poll while it remains active.
How do I avoid look-ahead bias with nws_alerts?
Filter on nws_alerts.available_date, the day the publisher made the row public. For this dataset that date is derived as follows — snapshot: available_date = COALESCE(captured_date, synced_at::date), the date this poller captured the active-alerts feed. Equal to ingestion time on a live poll; on a bronze replay it is the original poll day, never the replay day. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.
In what formats can I get nws_alerts?
As a Parquet bulk export (POST https://api.obscura.trade/v1/download) or as JSON from the typed query API (POST https://api.obscura.trade/v1/query), both with dataset="nws_alerts". The column schema is public at https://api.obscura.trade/v1/catalog/nws_alerts.
How often is nws_alerts updated?
Obscura refreshes nws_alerts on a every 6 hours schedule — that is the most frequent scheduled job that re-collects or re-exports the table. It is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when the publisher makes a row available is described by the availability rule above, and is recorded per row in available_date.