Technical writing

NOAA Storm Events: The Federal Database Behind 50 Years of US Weather Disaster Data

· 18 min read· AI Analytics
NOAAStorm EventsWeather DisastersClimate RiskFederal Data

Every significant weather event in the United States — every tornado, hurricane landfall, flash flood, hailstorm, blizzard, wildfire, and heat wave — leaves a record in the NOAA Storm Events Database. Maintained by the National Centers for Environmental Information (NCEI), the database stretches back to January 1950 and now holds more than 2.1 million event records across 70-plus years. It is the authoritative source behind NOAA's billion-dollar disaster tallies, the academic literature on tornado climatology, and the loss models that underpin federal flood insurance pricing. The bulk data is freely available without registration, updated monthly, and parseable with standard Python tooling — but its quirks will destroy an analysis if you do not understand them first.

Scale and provenance

The Storm Events Database is compiled from reports filed by the 122 National Weather Service (NWS) Weather Forecast Offices (WFOs) that blanket the contiguous United States, Alaska, Hawaii, Puerto Rico, and the surrounding marine areas. When an NWS forecaster determines that a weather event meets the threshold for inclusion — it caused deaths, injuries, significant property or crop damage, or was of sufficient meteorological significance to warrant documentation — they file a Storm Data entry. That entry is reviewed at the regional level and transmitted to NCEI, which incorporates it into the public database with roughly a two-month lag.

The public interface is at ncdc.noaa.gov/stormevents/, where filtered downloads are available by event type, state, and date range. The complete database is available as gzipped annual CSV files at the NCEI bulk download server: www.ncei.noaa.gov/pub/data/swdi/stormevents/csvfiles/. No API key or account is required for bulk access. Three file types exist for each year: a details file containing the main event records, a fatalities file linking individual death records to events by EPISODE_ID and EVENT_ID, and a locations file providing sub-event geographic coordinates for events with precise spatial data.

Annual total property damage recorded in the database now regularly exceeds $100 billion. NOAA's separately maintained Billion-Dollar Disasters tracker recorded 28 individual events exceeding $1 billion in 2023 alone — a record — for an annual total of approximately $92.9 billion. The climate change signal is visible in the damage time series: both the frequency of individual large-loss events and the fraction of annual precipitation falling in extreme events have trended upward since the 1980s, though disentangling physical hazard change from growing property exposure requires careful normalization.

Event type taxonomy

The 48 official NWS event types are defined in NWS Directive 10-1605, the Storm Data Preparation instruction manual. The taxonomy has changed over the database's history: before 1996, only tornado, thunderstorm wind, and hail were systematically recorded. From 1996 onward, NWS standardized reporting across all event categories. Any trend analysis spanning the pre- and post-1996 periods must account for this structural break or it will misread reporting expansion as a physical increase in event frequency.

The taxonomy clusters into functional families with distinct analytical properties:

Convective events are the most numerous by count. Thunderstorm wind is the single largest category in the database measured by event count — hundreds of thousands of records across the full history. This family also includes tornado, hail, lightning, funnel cloud, and waterspout. Convective events are short-duration, have precise begin and end times, and often carry magnitude data: wind speed in knots or mph, hail diameter in inches, tornado F/EF rating.

Tropical events (hurricane, tropical storm, tropical depression, storm surge/tide) have the highest average property damage per event in the database by a large margin. A single major hurricane generates hundreds of county-level event records as the system moves inland, each reflecting the local impact. This means that any national damage aggregation across hurricane events requires understanding the EPISODE_ID field, which groups all county-zone impacts from a single weather system into one episode — the mechanism for computing storm-total damage without double-counting.

Flood events (flash flood, flood, coastal flood, lakeshore flood, storm surge/tide) are the most consequential for loss-of-life analysis. Flash floods combined with riverine floods kill more Americans in most years than any other event type. The Flash Flood and Flood categories are operationally distinct: flash floods develop within six hours of the causative precipitation and are primarily driven by convective rainfall over small drainage basins. The FLOOD_CAUSE field captures the mechanism: heavy rain (most common), ice jam, dam or levee break, snowmelt, groundwater, or ice melt.

Winter events include blizzard, heavy snow, ice storm, winter storm, winter weather, cold/wind chill, extreme cold/wind chill, lake-effect snow, sleet, and frost/freeze. Lake-effect snow events are geographically concentrated in the Great Lakes region and represent a distinct climatological process from synoptic winter storms. Cold/wind chill and extreme cold/wind chill events carry mortality risk that the DEATHS_DIRECT and DEATHS_INDIRECT fields capture but that is often undercounted due to the difficulty of attributing cold-related deaths in medical examiner records.

Heat events (heat, excessive heat) are consistently among the deadliest weather types per capita but generate modest property damage figures because heat does not destroy structures. The distinction between “heat” and “excessive heat” reflects NWS threshold criteria specific to each WFO's climate region. The 1995 Chicago heat wave, the 2006 California heat wave, and the 2021 Pacific Northwest heat dome all appear in Storm Events with the death counts that NWS determined were directly attributable.

Marine events (marine thunderstorm wind, marine hail, marine strong wind, marine dense fog, marine lightning) cover NWS-designated marine zones rather than land counties. The CZ_TYPE field indicates whether a given event record is tied to a county (C), an NWS forecast zone (Z), or a marine zone (M). Marine zone events require different geographic handling in spatial analyses because they do not map cleanly to FIPS codes.

Other significant types include wildfire, dense fog, dust storm, dust devil, debris flow, volcanic ash, tsunami, seiche, astronomical low tide, avalanche, high wind, strong wind, rip current, and sneakerwave. Rip currents are notable for appearing in the database with a consistent fatality record; they kill more than 100 Americans in most years. Wildfire events in Storm Events reflect NWS fire weather impact reports rather than fire perimeter mapping — the National Interagency Fire Center is the authoritative source for acreage and perimeter data.

Database fields and data quality

The details file for each year contains the following key fields, grouped by analytical function:

FieldNotes
STATE / STATE_FIPSState name (uppercase) and 2-digit FIPS code. CZ_FIPS gives the county or zone FIPS within the state.
EVENT_TYPEOne of the 48 NWS canonical event type strings. Match exactly — capitalization and spacing matter.
CZ_TYPEC = county, Z = NWS forecast zone, M = marine zone. Governs how CZ_FIPS and CZ_NAME should be interpreted.
BEGIN_DATE_TIME / END_DATE_TIMELocal time at event location. Format varies: pre-2012 records commonly use DD-MON-YYHHMM; post-2012 often D/M/YYYY H:MM. Parse carefully.
INJURIES_DIRECT / INJURIES_INDIRECTDirect injuries caused immediately by the event; indirect injuries caused as a consequence. Injury counts are less reliable than death counts.
DEATHS_DIRECT / DEATHS_INDIRECTDirect deaths (immediate) and indirect deaths (consequence of event). Both fields needed for total mortality analysis.
DAMAGE_PROPERTY / DAMAGE_CROPSEncoded strings: “500K”, “1.2M”, “2B”. Require parsing before numeric use. See parsing function below.
SOURCEWho reported the event: Trained Spotter, Law Enforcement, Emergency Manager, Public, NWS Employee, CoCoRaHS, Mesonet, etc. Quality varies by source type.
MAGNITUDE / MAGNITUDE_TYPEIntensity value with qualifier: EG (estimated gust), ES (estimated sustained), MS (measured sustained), MG (measured gust). For hail: inches. For tornadoes: F/EF rating.
TOR_F_SCALETornado Fujita/Enhanced Fujita rating: EF0 through EF5 (post-2007), F0 through F5 (pre-2007). EF0 is most common; EF5 is extremely rare.
TOR_LENGTH / TOR_WIDTHTornado path length in miles and maximum width in yards. Length is more reliable than width for historical events.
BEGIN_LAT / BEGIN_LON / END_LAT / END_LONTouchdown and liftoff coordinates for tornadoes; begin and end point for other precisely located events. Decimal degrees. Many events lack coordinate data.
EPISODE_IDGroups all county/zone impact records from a single weather system into one episode. Essential for computing storm-total damages without double-counting across zones.
FLOOD_CAUSEFor flood and flash flood events: Heavy Rain, Ice Jam, Dam / Levee Break, Snowmelt, Groundwater, Ice Melt, Heavy Rain / Snowmelt, Heavy Rain / Burn Area.
CATEGORYHurricane category 1–5 (Saffir-Simpson scale) for hurricane and tropical storm event records.

Data quality varies by event type and era. Damage estimates are approximations made by NWS meteorologists using a combination of field surveys, news accounts, public records, FEMA disaster declaration data, and insurance industry estimates. They are not actuarial figures and are especially uncertain for small, rural events where no formal damage survey was conducted. For large catastrophic events — major hurricanes, significant tornadoes — the NWS estimate is a rough order of magnitude; the NCEI Billion-Dollar Disasters database cross-references insurance industry sources to produce more accurate figures for those events.

The DAMAGE_PROPERTY encoding

The single most common stumbling block for new Storm Events users is the DAMAGE_PROPERTY and DAMAGE_CROPS fields. Rather than storing raw numeric dollar amounts, these fields use a compact encoded format: a numeric prefix followed immediately by a magnitude suffix. The suffix K means thousands, M means millions, and B means billions. A value of “500K” represents $500,000. A value of “1.2M” represents $1,200,000. A value of “2B” represents $2,000,000,000.

This encoding is not a native numeric type and cannot be summed or compared without a parsing step. The encoding also bakes in rounding: NWS estimators work in round figures, so the difference between a “$50K” and a “$60K” estimate reflects the estimator's precision, not actual damage variation. Summing thousands of rounded estimates produces aggregate totals that diverge from insurance-adjusted loss data. The Python example below demonstrates the canonical parsing function — it handles the K/M/B suffixes, the absence of any suffix for sub-thousand amounts, and missing or blank values.

Billion-Dollar Disasters tracker

NOAA separately maintains the Billion-Dollar Weather and Climate Disasters database at ncei.noaa.gov/access/billions. This is a distinct product from Storm Events: rather than NWS field estimates, it integrates insurance industry loss data from Munich Re, Swiss Re, and the Insurance Information Institute, government loss estimates from FEMA and USDA, and economic impact studies to produce CPI-adjusted total loss figures for every US weather and climate event since 1980 that caused at least $1 billion in damages (in 2023 dollars).

The 2023 count of 28 billion-dollar events is a record. The 1980–2023 cumulative total is 376 events accounting for $2.6 trillion in losses (2023 dollars). The event categories in the Billion-Dollar database are broader than Storm Events event types: drought, flooding, freeze, severe storm (the Storm Events tornado/hail/wind complex), tropical cyclone, wildfire, and winter storm. CPI adjustment allows year-over-year comparison in constant dollars, but the Billion-Dollar database is also criticized because it does not normalize for exposure growth: property values and population density in high-risk areas have increased dramatically since 1980, independent of any climate signal, inflating nominal loss figures over time.

The trend is nonetheless real even after normalization. Studies that control for property exposure using insured replacement cost indices find that the frequency of large-loss convective storm events has increased since the 1990s, particularly for severe hail events in the southern plains and for inland flooding events driven by extreme rainfall. The extreme precipitation signal is among the clearest climate attribution results in the observational record.

NCEI CDO API and bulk download

The Climate Data Online (CDO) REST API at www.ncei.noaa.gov/cdo-web/api/v2/ provides programmatic access to Storm Events and other NCEI datasets. Free API key registration is available at ncei.noaa.gov/cdo-web/token. The Storm Events query parameters includedatasetid=STORM_EVENTS, location identifiers using FIPS codes, date range parameters, and datatypeid for filtering by event type. The API returns paginated JSON with a maximum of 1,000 records per request.

For historical bulk analysis, the FTP/HTTP bulk download is more practical than the CDO API. Annual details files are named StormEvents_details-ftp_v1.0_d{YEAR}_c{creation_date}.csv.gz. The creation date suffix changes each time NCEI refreshes the file, so the filename for a given year must be discovered by scraping the directory listing at the time of download. The Python example below handles this automatically. Each gzipped CSV file decompresses to between 10 MB and 80 MB depending on the year's event volume.

Tornado climatology

The Storm Prediction Center (SPC) Tornado Database is the authoritative source on historical tornadoes in the United States, but Storm Events is the database that most analysts encounter first, and the tornado records in Storm Events are rich enough for most climatological work. The US averages between 1,200 and 1,500 tornadoes per year with substantial year-to-year variation — roughly 500 in quiet years and over 2,000 in active years like 2011.

The Enhanced Fujita (EF) scale, adopted in February 2007, rates tornadoes on damage indicators across 28 structure and vegetation types rather than wind speed directly. EF0 tornadoes (estimated winds 65–85 mph) are the most common, accounting for roughly half of all recorded tornadoes. EF5 tornadoes (estimated winds above 200 mph) are extremely rare; only a handful have been confirmed in the entire SPC record.

The April 25–28, 2011 Super Outbreak produced 362 tornadoes in three days, killing 324 people — the largest tornado outbreak since the 1974 Super Outbreak and the deadliest in modern records. The events were concentrated in Alabama, Mississippi, Tennessee, and Georgia, not the traditional Tornado Alley corridor. The 2013 Moore, Oklahoma EF5 tornado caused an estimated $2 billion in property damage (Storm Events figure) with 24 direct deaths, illustrating both the destructive potential of EF5 events and the imprecision of the damage encoding for catastrophic events — post-event engineering surveys and insurance data placed the total economic loss substantially higher than the NWS field estimate.

Plotting EF2+ tornado track data from Storm Events by decade reveals a geographic shift that has generated significant research attention. The Great Plains Tornado Alley — Oklahoma, Kansas, the Texas Panhandle, Nebraska — remains active. But “Dixie Alley,” encompassing Mississippi, Alabama, Tennessee, and adjacent states, has seen an increasing share of high-intensity tornado events since roughly 2000. Dixie Alley tornadoes are disproportionately deadly per event because they occur more frequently at night (when lead times remain adequate but public response is slower), in more forested terrain that limits visual detection, and in communities with higher proportions of manufactured housing that is structurally vulnerable even to EF1 events.

Hurricane damage data

Storm Events records the county-level impacts of each named tropical system as individual event records, linked by EPISODE_ID and EPISODE_NARRATIVE. Hurricane Harvey in 2017 generated event records for 36 Texas counties, with the Storm Events aggregate property damage figure for the system running to approximately $125 billion — though that figure, like all large hurricane damage figures in Storm Events, is an NWS estimate subsequently revised upward through insurance and economic impact analysis. Hurricane Ian in 2022 impacted 67 Florida counties and produced property damage figures making it one of the two costliest US hurricanes on record at approximately $112 billion.

The CATEGORY field for hurricane and tropical storm events encodes the Saffir-Simpson Hurricane Wind Scale category at the time of the county-level impact. A single storm may appear with different categories in different county records as it evolves during landfall. The storm surge vs. wind damage distinction is analytically significant beyond just meteorology: wind damage is typically covered by standard homeowners insurance while storm surge damage often requires a separate NFIP flood insurance policy. Storm Events does not decompose total property damage into wind and surge components — that distinction requires cross-referencing NFIP claims data.

NOAA publishes Accumulated Cyclone Energy (ACE) indices for each Atlantic hurricane season — a measure of aggregate season intensity that integrates wind speed over time for all named systems. Correlating annual ACE with Storm Events hurricane property damage totals by year shows, as expected, that high-ACE seasons drive disproportionate damage shares, but also that landfall geography matters as much as storm intensity: a high-ACE season with most activity remaining offshore produces far less recorded damage than a moderate-ACE season with multiple Gulf Coast landfalls.

Flood events and fatality analysis

Flash flooding is the deadliest weather event category in most years by total deaths, ahead of tornadoes, hurricanes, lightning, and heat in most annual NOAA Weather Fatalities tallies. The average annual flash flood death toll runs to approximately 88 fatalities. The lethality pattern is geographically and mechanistically specific: the majority of flash flood deaths involve vehicles attempting to cross water flowing over roadways. The “Turn Around, Don't Drown” public safety campaign emerged directly from this finding in Storm Events and AHPS (Advanced Hydrologic Prediction Service) data. Texas, Arizona, and New Mexico consistently rank highest in flash flood fatality counts, driven by their combination of intense convective rainfall, dry creek beds that fill within minutes of intense rain, and sparse road networks that make alternate routes unavailable at night.

The Storm Events fatalities file, linked to the details file by EVENT_ID, contains individual death records with FATALITY_TYPE (D = direct, I = indirect), FATALITY_DATE, FATALITY_AGE, FATALITY_SEX, FATALITY_LOCATION (vehicle/mobile home/permanent home/outside/other), and EVENT_YEARMONTH. Joining this file to the event details file and filtering to Flash Flood events reveals the vehicle-vs.-shelter death distribution that drives hazard signage placement decisions and low-water crossing improvement project prioritization.

The 2022 Eastern Kentucky flash flooding event — July 26–30, 2022, in the Cumberland Plateau drainage basins of Knott, Letcher, Perry, and Breathitt counties — killed 45 people and caused approximately $1.4 billion in property damage (Storm Events estimate). The event illustrates both the lethality of flash flooding in mountainous terrain with constrained river valleys and the limitations of Storm Events damage estimates for events of this scale, where the NWS field survey figure has been substantially exceeded by subsequent economic impact assessments.

Python: downloading and analyzing Storm Events

The script below downloads Storm Events annual details files for 2019–2023, parses the DAMAGE_PROPERTY encoding, and produces three summary tables: top event types by total property damage, deadliest event types by total deaths, and top states by storm property damage. The download requires no authentication. The gzip module handles the compressed CSV format. The filename discovery step uses a regex against the NCEI directory listing to find the current filename for each year — necessary because NCEI updates file creation date suffixes on each data refresh.

import gzip
import io
import re
import urllib.request
from collections import defaultdict

import pandas as pd

# ---------------------------------------------------------------
# NOAA Storm Events Database — Multi-Year Analysis
# Bulk download base URL (HTTP mirror of the NCEI FTP):
#   https://www.ncei.noaa.gov/pub/data/swdi/stormevents/csvfiles/
#
# Annual CSV files follow the naming pattern:
#   StormEvents_details-ftp_v1.0_d{YEAR}_c{creation_date}.csv.gz
#
# We use a directory listing to discover the current filename for
# each year (the creation_date suffix changes on each NCEI update).
#
# No API key required for bulk downloads.
# ---------------------------------------------------------------

BASE_URL = 'https://www.ncei.noaa.gov/pub/data/swdi/stormevents/csvfiles/'
YEARS    = range(2019, 2024)   # 2019 through 2023 inclusive


def get_details_filename(year: int) -> str:
    """Scrape the NCEI directory listing to find the current details filename."""
    with urllib.request.urlopen(BASE_URL, timeout=30) as resp:
        html = resp.read().decode('utf-8')
    # Match: StormEvents_details-ftp_v1.0_d{year}_c{date}.csv.gz
    pattern = rf'StormEvents_details-ftp_v1.0_d{year}_c[d]{{8}}.csv.gz'
    match = re.search(pattern, html)
    if not match:
        raise FileNotFoundError(f'No details file found for year {year}')
    return match.group(0)


def parse_damage(val: str) -> float:
    """
    Convert NOAA DAMAGE_PROPERTY / DAMAGE_CROPS encoded strings to float.

    Examples:
        '500K'   -> 500_000.0
        '1.2M'   -> 1_200_000.0
        '2B'     -> 2_000_000_000.0
        '0'      -> 0.0
        ''       -> 0.0
    """
    if not val or not isinstance(val, str):
        return 0.0
    val = val.strip().upper()
    if not val or val in ('0', '0.00'):
        return 0.0
    multipliers = {'K': 1_000, 'M': 1_000_000, 'B': 1_000_000_000}
    suffix = val[-1]
    if suffix in multipliers:
        try:
            return float(val[:-1]) * multipliers[suffix]
        except ValueError:
            return 0.0
    try:
        return float(val)
    except ValueError:
        return 0.0


def load_year(year: int) -> pd.DataFrame:
    """Download and parse one year of Storm Events details."""
    filename = get_details_filename(year)
    url = BASE_URL + filename
    print(f'  Downloading {filename} ...')
    with urllib.request.urlopen(url, timeout=120) as resp:
        compressed = resp.read()
    with gzip.open(io.BytesIO(compressed), 'rt', encoding='utf-8', errors='replace') as f:
        df = pd.read_csv(f, dtype=str, low_memory=False)
    df['YEAR_INT'] = year
    return df


# ---------------------------------------------------------------
# Load all years
# ---------------------------------------------------------------
frames = []
for yr in YEARS:
    print(f'Year {yr}:')
    frames.append(load_year(yr))

events = pd.concat(frames, ignore_index=True)
print(f'\nTotal records loaded: {len(events):,}')

# ---------------------------------------------------------------
# Parse damage fields
# ---------------------------------------------------------------
events['prop_dmg'] = events['DAMAGE_PROPERTY'].apply(parse_damage)
events['crop_dmg'] = events['DAMAGE_CROPS'].apply(parse_damage)
events['total_dmg'] = events['prop_dmg'] + events['crop_dmg']

events['deaths_direct']   = pd.to_numeric(events['DEATHS_DIRECT'],   errors='coerce').fillna(0)
events['deaths_indirect'] = pd.to_numeric(events['DEATHS_INDIRECT'], errors='coerce').fillna(0)
events['total_deaths']    = events['deaths_direct'] + events['deaths_indirect']

# ---------------------------------------------------------------
# Section 1: Property damage by event type (2019-2023)
# ---------------------------------------------------------------
by_type = (
    events.groupby('EVENT_TYPE')
    .agg(
        event_count   = ('EVENT_TYPE', 'count'),
        total_prop_dmg = ('prop_dmg', 'sum'),
        total_deaths  = ('total_deaths', 'sum'),
    )
    .assign(
        avg_dmg_per_event = lambda d: d['total_prop_dmg'] / d['event_count'],
        deaths_per_event  = lambda d: d['total_deaths']   / d['event_count'],
    )
    .sort_values('total_prop_dmg', ascending=False)
)

print('\n=== Top 10 Event Types by Total Property Damage (2019-2023) ===')
top10_dmg = by_type.head(10)[['event_count', 'total_prop_dmg', 'avg_dmg_per_event']]
top10_dmg.columns = ['Events', 'Total Prop Damage ($)', 'Avg per Event ($)']
print(top10_dmg.to_string(float_format='${:,.0f}'.format))

print('\n=== Top 10 Deadliest Event Types by Total Deaths (2019-2023) ===')
top10_dead = by_type.sort_values('total_deaths', ascending=False).head(10)
top10_dead = top10_dead[['event_count', 'total_deaths', 'deaths_per_event']]
top10_dead.columns = ['Events', 'Total Deaths', 'Deaths per Event']
print(top10_dead.to_string(float_format='{:.4f}'.format))

# ---------------------------------------------------------------
# Section 2: Top 10 states by total property damage (2019-2023)
# ---------------------------------------------------------------
by_state = (
    events.groupby('STATE')
    .agg(
        total_prop_dmg = ('prop_dmg', 'sum'),
        event_count   = ('EVENT_TYPE', 'count'),
        total_deaths  = ('total_deaths', 'sum'),
    )
    .sort_values('total_prop_dmg', ascending=False)
    .head(10)
)

print('\n=== Top 10 States by Total Storm Property Damage (2019-2023) ===')
by_state.columns = ['Total Prop Damage ($)', 'Events', 'Deaths']
print(by_state.to_string(float_format='${:,.0f}'.format))

The parse_damage function is the load-bearing piece for any Storm Events dollar analysis. The common failure modes are treating the entire DAMAGE_PROPERTY column as numeric (which fails on every non-zero value that carries a suffix) and forgetting that empty strings and literal “0” values both appear in the data and should map to 0.0 rather than raising an exception. The multiplier dictionary approach shown here handles the K/M/B cases cleanly and can be extended if NCEI ever introduces additional suffix conventions.

Running this script on 2019–2023 data (approximately 800,000 records total) will typically show tropical cyclone events and flood events near the top of the property damage ranking, with thunderstorm wind events dominating the event count ranking. The deaths-per-event metric consistently elevates heat events (extreme heat and heat) and rip current events above their positions in the property damage ranking — a reminder that the deadliest event types are not always the costliest.

Cross-references and related datasets

Storm Events property damage estimates for flood events have a natural cross-reference in the NFIP claims database maintained by FEMA. NFIP paid claims represent actual indemnified losses under federal flood insurance policies — a bottom-up financial figure rather than a top-down NWS field estimate. The two figures diverge systematically in areas with low flood insurance take-up rates: uninsured flood losses appear in Storm Events DAMAGE_PROPERTY but not in NFIP claims, while insurance-capped losses in high-value properties appear in NFIP claims but may be understated in Storm Events damage estimates that reflect NWS survey precision rather than policy limits. Joining Storm Events flood event records to OpenFEMA NFIP claims by county FIPS and event date produces a paired dataset that quantifies the insured vs. uninsured loss split at the county level for individual flood events.

The NOAA Advanced Hydrologic Prediction Service (AHPS) provides real-time and historical flood stage data at approximately 13,000 stream gauges across the CONUS. AHPS stage data joined to Storm Events flood records by gauge location and event date provides the physical context — what stage the stream reached at what gauge — for the damages and deaths recorded in Storm Events. This combination is the analytical foundation for studies of flood damage at different recurrence intervals and for vulnerability assessments that relate stream gauge exceedance probabilities to observed impacts.

The SPC Tornado Archive provides wind speed estimates and rating metadata for historical tornadoes that extend the Storm Events TOR_F_SCALE data with additional survey detail, including the specific damage indicators used to assign each rating. For EF2+ tornadoes the SPC records are generally more complete and more precisely georeferenced than the Storm Events records, making the SPC archive preferable for climatological tornado research. Storm Events tornado records are most useful when the research requires joining tornado data to the full Storm Events damage and casualty framework — for example, estimating fatality risk as a function of warning lead time or housing type using the deaths and injuries fields alongside the TOR fields.

The Census Bureau's American Community Survey and the Social Vulnerability Index (CDC/ATSDR SVI) provide the demographic and housing stock data needed to normalize Storm Events fatality and damage figures for population exposure. Storm Events records raw counts of deaths and economic damage; translating those counts into risk rates (deaths per million residents, damage per $1,000 of residential property value) requires joining to census denominators at the county level. The manufactured housing share of the housing stock — available from ACS Table B25024 — is a particularly important covariate for tornado fatality analysis given the structural vulnerability of manufactured housing at all EF rating levels.

Limitations

Four limitations deserve explicit acknowledgment before working with Storm Events at scale.

Damage estimates are NWS field survey estimates, not insurance or engineering figures. For small rural events, the NWS estimate may be based on a single phone call to an emergency manager. For large catastrophic events, the figure is an order-of-magnitude approximation made before most of the damage has been assessed. Do not cite Storm Events property damage figures as financial accounting without cross-referencing insurance-industry or FEMA estimate sources for events of significant scale.

Event type definitions in NWS Instruction 10-1605 have been revised multiple times across the database's history. Type names that appear identical in different eras may reflect different definitional thresholds. Long-term trend analysis for specific event types should verify that the type definition was stable across the period under study by consulting the directive revision history.

The pre-1996 data is structurally incomplete. Only tornado, thunderstorm wind, and hail were systematically recorded before 1996. Analyses that attempt to characterize long-term trends in flash flood deaths, heat wave mortality, or winter storm damage using pre-1996 Storm Events data will produce severely biased baselines.

Detection technology changes affect low-intensity event records over time. The nationwide WSR-88D Doppler radar deployment in the early 1990s substantially improved detection of weak tornadoes (EF0/F0). The Storm Events tornado count shows a step-change increase in EF0 tornadoes coinciding with improved radar coverage that reflects detection improvement, not a physical increase in EF0 frequency. Trend analyses of low-intensity convective events must account for this detection artifact.

For incident-level federal crime data with similar bulk download structure and data quality considerations: Incident-level crime: using FBI NIBRS data to analyze offense patterns, victim demographics, and clearance rates →

For FEMA's National Flood Insurance Program claims data and how NFIP loss records complement Storm Events flood damage estimates: NFIP Flood Insurance Data: The Federal Program Behind $20 Billion in Flood Claims and the National Flood Hazard Layer →