Since 1966, NHTSA has required automakers to notify owners and remedy safety defects in their vehicles — and every one of those recall campaigns is recorded in the NHTSA recall database. With over 400,000 campaign records spanning nearly six decades, it's one of the most comprehensive longitudinal safety datasets in the world. Each record captures the affected makes, models, and model years; the recalled component system; the description of the defect; and the estimated number of vehicles affected.
In this tutorial we'll load the ClarityStorm NHTSA Vehicle Recalls dataset, build a brand-level recall risk profile, analyze the Takata airbag crisis in the data, and chart how recall volumes and lead times have changed over sixty years.
Dataset Overview
The ClarityStorm release normalizes the NHTSA recall feed into a clean tabular schema with one row per affected make-model-year per recall campaign. This makes aggregation by brand or model year straightforward without the multi-value expansion logic the raw NHTSA XML requires.
- 400K+ recall campaign-model rows, 1966–present
- Component hierarchy: system → component → part (aligned with NHTSA coding)
- Defect description text field for NLP analysis
- Estimated vehicles affected per campaign-model combination
- Recall initiation date and remedy-available date
- Manufacturer name, make, model, and model year columns
Loading and Profiling
import pandas as pd
recalls = pd.read_parquet("nhtsa_recalls.parquet")
print(f"Total records: {len(recalls):,}")
print(f"Date range: {recalls['recall_date'].min()} – {recalls['recall_date'].max()}")
print(f"Unique campaigns: {recalls['campaign_number'].nunique():,}")
print(f"Unique makes: {recalls['make'].nunique():,}")
# Total vehicles affected overall
print(f"\nTotal vehicles affected: {recalls['vehicles_affected'].sum():,.0f}")Brand-Level Recall Risk Profiles
Raw recall count is a misleading metric: a manufacturer with a large US market share will naturally accumulate more total campaigns than a niche importer. A fairer comparison divides total vehicles affected by an estimate of fleet size — or alternatively, compares average campaign size and average defect severity across brands. The dataset lets you build both.
# Brand summary: campaigns, vehicles affected, avg campaign size
brand_profile = (
recalls.groupby("make")
.agg(
campaigns=("campaign_number", "nunique"),
total_vehicles_affected=("vehicles_affected", "sum"),
avg_campaign_size=("vehicles_affected", "mean"),
median_campaign_size=("vehicles_affected", "median"),
)
.sort_values("total_vehicles_affected", ascending=False)
.head(20)
)
brand_profile["total_affected_M"] = (
brand_profile["total_vehicles_affected"] / 1e6
).round(2)
print(brand_profile[["campaigns", "total_affected_M", "avg_campaign_size"]].to_string())Component Breakdown: What Fails Most Often
Not every recall is equal. An airbag recall affecting 10 million vehicles is categorically different from a software campaign affecting 500. Aggregating by component system and weighting by vehicles affected shows where the real safety mass lies — and which systems have driven the largest volume of recalled vehicles over the dataset's history.
# Most recalled components (by vehicles affected)
component_totals = (
recalls.groupby("component_system")
.agg(
campaigns=("campaign_number", "nunique"),
total_vehicles=("vehicles_affected", "sum"),
)
.sort_values("total_vehicles", ascending=False)
.head(15)
.reset_index()
)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 6))
ax.barh(
component_totals["component_system"][::-1],
component_totals["total_vehicles"][::-1] / 1e6,
color="#0ea5e9",
alpha=0.8,
)
ax.set_xlabel("Vehicles Affected (millions)")
ax.set_title("NHTSA Recalls: Most-Affected Component Systems (All Time)")
plt.tight_layout()
plt.savefig("nhtsa_components.png", dpi=150)The Takata Airbag Crisis in the Data
The Takata airbag inflator defect produced the largest automotive recall in history — an estimated 67–100 million vehicles across dozens of manufacturers worldwide. In the NHTSA dataset, the Takata campaigns appear as a distinctive spike in 2013–2019 across nearly every major automaker. Isolating these campaigns illustrates how a single supplier failure can cascade across an entire industry.
# Identify Takata-related campaigns (text match on defect description)
takata = recalls[
recalls["defect_description"].str.contains("takata|inflator", case=False, na=False)
]
print(f"Takata-related records: {len(takata):,}")
print(f"Affected vehicles: {takata['vehicles_affected'].sum():,.0f}")
print(f"Manufacturers involved: {takata['make'].nunique()}")
# Timeline of Takata campaign initiations
takata["recall_year"] = pd.to_datetime(takata["recall_date"]).dt.year
takata_annual = (
takata.groupby("recall_year")["vehicles_affected"]
.sum()
.reset_index()
)
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 4))
plt.bar(takata_annual["recall_year"], takata_annual["vehicles_affected"] / 1e6,
color="#ef4444", alpha=0.8)
plt.title("Takata Airbag Recall Campaigns by Year (Vehicles Affected, millions)")
plt.xlabel("Year")
plt.ylabel("Millions of Vehicles")
plt.tight_layout()
plt.savefig("takata_timeline.png", dpi=150)Recall Volume and Lead-Time Trends
The pace of NHTSA recalls has accelerated significantly since the 1990s — partly reflecting a larger fleet and tighter regulatory scrutiny, but also the growing complexity of vehicle electronics, software, and globally sourced components. An interesting secondary metric is 'recall lead time': the gap between a model year's production year and its first recall date. Newer vehicles are recalled faster than in earlier decades, suggesting improved defect detection pipelines at both NHTSA and the manufacturers.
# Annual recall campaigns and total vehicles
recalls["recall_year"] = pd.to_datetime(recalls["recall_date"]).dt.year
annual = (
recalls.groupby("recall_year")
.agg(
unique_campaigns=("campaign_number", "nunique"),
total_vehicles=("vehicles_affected", "sum"),
)
.reset_index()
.query("recall_year >= 1966 and recall_year <= 2024")
)
fig, ax1 = plt.subplots(figsize=(14, 5))
ax1.bar(annual["recall_year"], annual["unique_campaigns"],
color="#0ea5e9", alpha=0.6, label="Campaigns")
ax2 = ax1.twinx()
ax2.plot(annual["recall_year"], annual["total_vehicles"] / 1e6,
color="#f59e0b", linewidth=2, label="Vehicles (M)")
ax1.set_xlabel("Year")
ax1.set_ylabel("Unique Campaigns", color="#0ea5e9")
ax2.set_ylabel("Vehicles Affected (millions)", color="#f59e0b")
ax1.set_title("NHTSA Recall Campaigns 1966–2024")
fig.legend(loc="upper left", bbox_to_anchor=(0.08, 0.92))
plt.tight_layout()
plt.savefig("nhtsa_recall_trend.png", dpi=150)Applications: Insurance, Resale, and Fleet Risk
The NHTSA recalls dataset has direct commercial applications. Insurers use recall history as a risk signal — vehicles with outstanding safety recalls have elevated claim frequencies. Used-car platforms query recall status to flag listings where the remedy hasn't been performed. Fleet operators monitor recall exposure across thousands of VINs to prioritize service scheduling. The dataset's campaign-level granularity, combined with make/model/year fields, enables all of these use cases without an additional VIN decode step.
The free sample contains 1,000 rows. The complete NHTSA Vehicle Recalls dataset covers 400K+ campaign-model records from 1966 to present, available as CSV and Parquet with a commercial license.