fars-toolkit
Download, filter, and aggregate NHTSA FARS fatal-crash data with zero non-stdlib dependencies.
A small, focused Python library for indie data-journalism and research workflows. No pandas, no PyArrow, no Spark — just the standard library, the public NHTSA FARS CSV downloads, and a few dozen lines that handle the annoying parts (BOM bytes, varying folder layouts, the BODY_TYP join).
If you're doing serious modelling work, use pandas or polars. If you want to pull seven years of FARS, filter to crashes involving a specific vehicle class, and aggregate per-city or per-state, this is the lighter path.
Companion to the published research:
- Vision Zero Report Card 2026 — interactive 19-city report built on this exact pipeline.
- Dataset on Zenodo (DOI) — the row-level CSV produced by this code.
- Source repo on GitHub.
- HuggingFace mirror.
Install
pip install fars-toolkit
Requires Python ≥ 3.9. No dependencies outside the standard library.
Quick start
from fars_toolkit import (
download_year, load_year, truck_cases, by_city, by_state
)
# 1. Pull the 2024 zip (~32 MB) into ./data/ and extract.
download_year(2024, into='data/')
# 2. Locate the inner CSVs. Folder layout varies year to year; this handles
# the 2018-2024 vintages automatically.
accident, vehicle = load_year(2024, base='data/')
# 3. Find all crashes that involved a medium/heavy commercial truck.
# By default, body types 60-69 (the standard NHTSA truck definition).
cases = truck_cases(vehicle)
# 4. Aggregate fatalities by (state, city) FIPS code.
per_city = by_city(accident, cases)
print(per_city[(17, 1670)]) # truck-involved fatalities in Chicago, IL, 2024
# 5. Or aggregate by state.
per_state = by_state(accident, cases)
print(per_state[6]) # truck-involved fatalities in California, 2024
What it handles
NHTSA's FARS CSVs have a few rough edges that bit me when I built the Vision Zero Report Card and inspired this package:
- BOM bytes in 2021–2024 files. Reading naïvely with
utf-8fails on 2018 files; reading withlatin-1leaves a BOM visible ason the first column header, which silently breaks the dict key lookup.open_fars_csv()strips it transparently. - Folder layout varies. 2018–2019 zips extract their CSVs directly into the target folder. 2020+ wrap them in an inner
FARS{year}NationalCSV/subfolder.load_year()tries both. - Multi-table join. A "truck-involved crash" is defined on the vehicle table (one row per vehicle, where
BODY_TYPis in 60–69) but the fatality count lives on the accident table.truck_cases()+by_city()does that join cleanly without loading everything into memory.
Truck definition
By default, truck_cases() uses FARS BODY_TYP codes 60–69 — medium and heavy commercial trucks. This matches the truck-crash definition NHTSA and IIHS use in safety research; it excludes passenger pickups under 10,000 lb GVWR.
You can pass a custom set:
from fars_toolkit import truck_cases
# Only truck-tractors (semis):
semi_cases = truck_cases('vehicle.csv', body_types={68})
# Step vans and HAZMAT placards:
small_truck_cases = truck_cases('vehicle.csv', body_types={60, 61})
The package exposes BODY_TYP_LABELS if you want human-readable labels for each code.
CLI
The library ships a thin CLI for the common one-liners:
# Download a batch of years.
fars-toolkit download --year 2018 2019 2020 2021 2022 2023 2024 --into data/
# Top 20 cities by truck-involved fatalities in 2024.
fars-toolkit cities --year 2024 --base data/ --top 20
# Per-state truck-fatality totals for 2024.
fars-toolkit state-totals --year 2024 --base data/
API summary
| Function | What it does |
|---|---|
download_year(year, into=...) |
Fetch and extract a FARS National CSV zip. |
load_year(year, base=...) |
Locate the accident.csv + vehicle.csv paths inside an extracted folder. |
truck_cases(vehicle, body_types=...) |
Return the set of (state, st_case) crash IDs whose vehicle is a truck. |
by_city(accident, cases=...) |
Aggregate FATALS by (state, city) FIPS, optionally restricted to a case set. |
by_state(accident, cases=...) |
Aggregate FATALS by state FIPS. |
series_for_city(state, city, years, base=...) |
Build a year → fatalities series for one city across multiple years. |
open_fars_csv(path) |
Encoding-tolerant csv.DictReader opener, BOM-stripped. |
Caveats inherited from FARS
These are the caveats baked into the underlying NHTSA data; the package can't fix them, but you should know:
- City boundaries. FARS uses federal FIPS city codes. Crashes on through-routes at city limits sometimes attribute to the surrounding county instead of the city, so urban truck-deaths are likely undercounted for cities with major interstate-through-routes.
- 2024 is preliminary. The 2024 Annual Report File released April 2026 will be re-released as the Final File in 2027 and may revise counts slightly.
- Small annual counts are noisy. For cities with single-digit annual truck fatalities, year-over-year percentage swings reflect statistical noise as much as policy outcomes.
License
MIT.
Related
- Vision Zero Report Card 2026 — the interactive report built with this toolkit.
- Build notes on dev.to — the technical write-up that motivated this package.
- On getting a DOI for indie data projects — how the companion dataset got onto Zenodo.
Metadata
Release files for fars-toolkit 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fars_toolkit-0.1.0.tar.gz | 12.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fars_toolkit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.8 kB
Release files / fars_toolkit-0.1.0.tar.gz
| Download URL | fars_toolkit-0.1.0.tar.gz |
|---|---|
| Size | 12.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
af87d42aba69687c492ebeb14141d90a3430f0b89b6f2238c957254104baed1e
|
|
BLAKE2b-256 checksum How to use checksums |
b74090c1349a3a11314959ba1ebcd1713a246e1987760cd4325294b678a8d3c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / fars_toolkit-0.1.0-py3-none-any.whl
| Download URL | fars_toolkit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
649d2c299cca8e1ac7ee77b33028d901f5aff6298302e6406b4af9ab2825f0fb
|
|
BLAKE2b-256 checksum How to use checksums |
1043c72dc4ebb7cb4c204308e5cbb03e9f7c89cd565c0ace08d2d57c2129974b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|