NOAA Storm Events Peril Climatology — County Risk Scores
Pricing
from $4.40 / 1,000 results
NOAA Storm Events Peril Climatology — County Risk Scores
County-level severe-weather climatology from NOAA's Storm Events Database. For any US county or state and year range, get per-peril event frequency, casualties, and property/crop damage, plus a composite peril risk score. Built for P&C underwriting, reinsurance, and catastrophe siting. Keyless.
Pricing
from $4.40 / 1,000 results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
NOAA Storm Events County Peril Climatology
Turns NOAA/NCEI's Storm Events Database into a per-county, per-peril climatology: how often each severe-weather peril has hit each county, what it cost, who it killed or injured, and how bad the worst year was — in one row per (county x peril).
Built directly from NCEI's authoritative bulk .csv.gz archive (1950 to the
current year), not a scraped front-end. No API key.
What one row is
One county, one peril type, over the year range you asked for. For example "Yuma County, Colorado — Thunderstorm Wind, 2023-2024: 55 events across 23 distinct storm episodes, max gust 87 kt, $147,140 in reported damage, peril score 54.18."
Read this before you use it: county vs forecast zone
NOAA records some perils against counties (CZ_TYPE = "C") and others
against NWS forecast zones (CZ_TYPE = "Z"). This is not a minor detail:
43% of all national Storm Events records are zone-typed, and across the full
2023, 2024 and 2025 national files 37 of NOAA's 54 peril types have ZERO
county-typed events, with seven more -- Wildfire, Drought, Heat, Heavy Snow,
High Wind, Strong Wind and Winter Storm -- recording a handful of county events
against thousands of zone events (Drought: 1 county vs 12,570 zone).
Among the perils a county-only run cannot see:
Wildfire · Excessive Heat · Heat · Winter Storm · Heavy Snow · Blizzard · Ice Storm · Drought · Hurricane (Typhoon) · Tropical Storm · Storm Surge/Tide · Coastal Flood · High Wind · Strong Wind · Dense Fog · Dense Smoke · Extreme Cold/Wind Chill · Avalanche · Rip Current
includeZones is off by default, which keeps every row tied to a real
county FIPS. When it is off:
- every output row reports
zone_events_excluded(how many in-scope events were dropped) andexcluded_zone_perils(which perils were dropped by name); - if you filter
eventTypesto perils NOAA only zone-types, the run fails with an actionable message instead of handing you an empty dataset.
Set "includeZones": true to include them; those rows carry
geography_basis: "forecast_zone" and are keyed to the zone's FIPS.
Who this is for
- Property & casualty underwriters — hail, wind and tornado frequency and severity per county, with max hail diameter and max gust.
- CRE / infrastructure siting — a peril profile for a candidate county before committing capital.
- Cat modellers and reinsurance analysts — a free, reproducible baseline built from the authoritative NCEI archive.
- Climate and resilience researchers — county-level event, casualty and loss history back to 1950.
- AI agents — one call per state, one row per (county x peril), with explicit completeness fields so the agent knows what it does and does not know.
Example input
{"states": ["Colorado"],"counties": [],"startYear": 2023,"endYear": 2025,"eventTypes": [],"includeZones": false,"maxResults": 2000}
counties accepts either a 5-digit FIPS ("08031") or a bare county name
matched case-insensitively ("Denver"). states accepts "CO" or
"Colorado", as an array or a comma-separated string.
Output — every field
Identity
| Field | Meaning |
|---|---|
county_fips | 5-digit county FIPS, built as pad2(STATE_FIPS) + pad3(CZ_FIPS). |
county_name | NOAA's CZ_NAME. |
state | State name in NOAA's uppercase form. |
state_abbr | Two-letter postal code. |
event_type | The NOAA peril type. |
geography_basis | county (CZ_TYPE=C), forecast_zone (CZ_TYPE=Z), or mixed. |
Frequency and severity
| Field | Meaning |
|---|---|
event_count | NOAA event records in scope. |
episode_count | Distinct NOAA storm episodes behind them. One storm system can log many events in one county, so this is the better "how often did a distinct storm hit here" measure. |
events_per_year | event_count divided by n_years. |
deaths, injuries, casualties | Combined totals. |
deaths_direct, deaths_indirect, injuries_direct, injuries_indirect | NOAA's own direct/indirect split. |
worst_year | Year with the highest combined damage. |
worst_year_damage_usd | Damage in that year. |
first_event_date, last_event_date | Earliest and latest event begin date in scope. |
max_magnitude | Largest NOAA magnitude - hail diameter in inches, or wind gust in knots. |
magnitude_type | What max_magnitude measures: EG/MG (gust), MS/ES (sustained), or empty for hail. |
max_tornado_ef_scale | Strongest EF/F rating recorded. |
max_tornado_length_miles, max_tornado_width_yards | Largest tornado path recorded. |
flood_causes | Distinct NOAA flood causes. |
report_sources | Who reported the events (Trained Spotter, Emergency Manager, ASOS, ...). |
nws_offices | NWS Weather Forecast Offices that logged them. |
peril_score | EDITORIAL 0-100 composite: annualized frequency (0.40), casualties (0.25), total damage (0.35), each log-saturating. A triage heuristic, not an actuarial loss estimate. |
Damage — and how complete it is
NOAA leaves DAMAGE_PROPERTY empty on 21.6% of events (2024 national file): no dollar figure was ever estimated. That is not the same as $0.
| Field | Meaning |
|---|---|
property_damage_usd | Sum of reported property damage. null when NOAA costed none of this row's events. |
crop_damage_usd | Same, for crops. |
total_damage_usd | Property + crop. null when neither was ever reported. |
damage_per_year_usd | total_damage_usd divided by n_years, or null. |
events_with_damage_reported | How many events carry a NOAA dollar figure. |
events_missing_damage_report | How many do not. |
damage_report_completeness_pct | The percentage. 0 means the damage columns are null. |
damage_is_lower_bound | true when some but not all events were costed, so the sum is a floor. |
peril_score deliberately treats an unreported figure as $0 so scores stay
comparable across rows, even where the damage columns are null.
Coverage and provenance — read these before trusting a row
| Field | Meaning |
|---|---|
years_covered | The years this row is actually built from. |
years_requested | The range you asked for. |
years_retrieved | Comma-separated list of years whose NCEI file was downloaded and parsed. |
years_missing | Requested years that were absent from the index or failed to download. null when nothing was missing. |
data_complete | true only when every requested year was retrieved. |
source_status | ok or partial. |
n_years | Denominator for the per-year metrics - the count of retrieved years, not requested ones. |
zone_events_excluded | In-scope events dropped because they are zone-typed and includeZones is off. |
excluded_zone_perils | The peril types dropped for that reason, by name. |
source_files | The exact NCEI .csv.gz filenames used. |
source_url | The NCEI directory. |
How it works
- Fetches the NCEI csvfiles/ directory index and asserts it live: it must still list at least 60 years back to 1960 or earlier, or the run fails before anything is billed. The year bounds you can request are derived from that index rather than hard-coded.
- Resolves each year's StormEvents_details-ftp_v1.0_d{YEAR}_c{DATE}.csv.gz by the newest creation date, and logs which one it used.
- Downloads and gunzips each annual file, with retry and backoff, and stream-filters it to your states while parsing - the full ~75,000-row national array is never materialised, because building it exhausts a 4 GB run.
- Asserts the file's shape live: 25 required columns present, the national event count inside a measured band, and CZ_TYPE still a two-value vocabulary. Any of those failing fails the run and bills nothing. A previously-unseen EVENT_TYPE is reported as a warning, not a failure.
- Aggregates to one row per (county x peril), scores, sorts by score, and applies maxResults.
- Gates before writing: if no year could be retrieved, if the state matched nothing, or if the filters matched no rows, the run fails with a message naming the cause - nothing is emitted and nothing is billed.
Use as an MCP tool
Available through mcp.apify.com. One call per state; one row per
(county x peril). An agent should read data_complete, source_status and
damage_report_completeness_pct before quoting a number, and read
excluded_zone_perils before concluding a county has no history of a peril.
Pricing
Pay-per-result: one dataset row = one billed result. A failed run emits nothing and costs nothing beyond the platform's Actor-start event. Volume tiers apply automatically on paid Apify plans.
FAQ
Why did I get no Wildfire / Excessive Heat / Winter Storm rows? Because
NOAA records those against NWS forecast zones, not counties, and includeZones
is off by default. Set it to true. Your rows already name the exclusions in
excluded_zone_perils.
Why is total_damage_usd null on some rows? Because NOAA published no
dollar estimate for any event in that row. Publishing $0 there would be a
factual claim the source never made. Check damage_report_completeness_pct.
Why does years_covered differ from what I asked for? Because at least one
annual file could not be retrieved. years_missing names them and
data_complete is false.
Is peril_score an actuarial number? No. It is a transparent, documented, absolute composite for triage and ranking. The inputs are all on the row, so you can build your own.
How far back does it go? The NCEI archive starts in 1950, but pre-1996 files only carry tornado, thunderstorm-wind and hail.
Related actors
- fema-nri-county-risk-profile - FEMA's National Risk Index for the same county, at county and tract resolution.
- wildfire-asset-exposure-screener - live and historical wildfire perimeters and Wildfire Hazard Potential for a coordinate.
- nfip-flood-loss-risk-screener - paid NFIP flood-claim history near a point.