How to Measure the Age of the US General Aviation Fleet
On this page
Problem statement
How old are America's private aircraft? The FAA registry records the year each aircraft was built, so a median age is one subtraction away, but the column has blanks, zeros and, since drones started being registered, a large group of brand-new aircraft with no year at all. Which aircraft you count changes the answer by decades. This page measures the age of the registered fleet with the 16 October 2026 file, by kind of aircraft, by owner and by state, and shows what the missing years are.
Quick answer
Treat the year as a number, drop anything before 1903, and subtract from 2026:
v = reg[reg["STATUS CODE"] == "V"].copy()
v["year"] = pd.to_numeric(v["YEAR MFR"], errors="coerce").where(lambda y: y >= 1903)
v["age"] = 2026 - v.year
print(f"valid registrations: {len(v):,}; with a usable year: {v.year.notna().sum():,}; median year {v.year.median():.0f}, median age {v.age.median():.0f}")
valid registrations: 310,844; with a usable year: 243,254; median year 1979, median age 47
Of 310,844 valid registrations, 243,254 have a usable year. The median aircraft was built in 1979 and is 47 years old.
Years that are not years, and ages by kind
1. Blank and zero years
y = pd.to_numeric(v["YEAR MFR"], errors="coerce")
print(f"blank: {(v['YEAR MFR'] == '').sum():,}; zero: {(y == 0).sum():,}; earliest real year: {y[y > 0].min():.0f}")
blank = v[v["YEAR MFR"] == ""]
print((blank["TYPE AIRCRAFT"].map(TYPE_AIRCRAFT) + ", engine " + blank["TYPE ENGINE"]).value_counts().head(4).to_string())
blank: 67,442; zero: 148; earliest real year: 1909
fixed wing single engine, engine 1 32242
rotorcraft, engine 10 12439
hybrid lift, engine 10 3263
fixed wing single engine, engine 8 3133
67,442 valid registrations have a blank year and 148 a year of 0; the earliest real year is 1909. Nearly half the blanks are piston single-engine aeroplanes (engine code 1), and 12,439 are electric rotorcraft, the registered drones, which almost never carry a year, with 3,263 hybrid-lift drones besides. A median that ignores the blanks therefore leaves out most of the newest aircraft in the file.
2. Median age by kind of aircraft
v["kind"] = v["TYPE AIRCRAFT"].map(TYPE_AIRCRAFT).mask((v["TYPE AIRCRAFT"] == "6") & (v["TYPE ENGINE"] == "10"), "electric rotorcraft")
k = v.groupby("kind").agg(aircraft=("age", "size"), median_age=("age", "median"), dated=("age", "count"))
print(k[k.aircraft >= 1000].sort_values("median_age", ascending=False).to_string())
aircraft median_age dated
kind
fixed wing single engine 217878 50.0 179743
glider 4822 45.0 3734
fixed wing multi engine 48682 25.0 41543
powered parachute 1896 23.0 1505
balloon 5206 22.0 3863
rotorcraft 15027 21.0 11605
weight-shift 1042 20.0 833
electric rotorcraft 12510 2.0 71
hybrid lift 3300 2.0 19
Single-engine aeroplanes have a median age of 50, gliders 45; multi-engine aeroplanes and helicopters are about 25 and 21. The electric rotorcraft and hybrid-lift aircraft show a median of 2 years, but on 71 and 19 dated aircraft out of thousands.
3. Built per year, still registered
per_year = v.year.value_counts().sort_index()
print(per_year.loc[[1946, 1966, 1978, 1979, 1990, 2007, 2015, 2025]].to_string())
year
1946.0 8449
1966.0 5614
1978.0 6749
1979.0 6373
1990.0 1170
2007.0 4702
2015.0 2880
2025.0 3691
1946 alone left 8,449 aircraft still registered, more than any year since: the post-war boom in light aircraft. 1978 and 1979 left 6,749 and 6,373, 1990 only 1,170, after product-liability costs had cut light-aircraft production, and 2025 added 3,691.
Owners and states
The data used on this page
The FAA's Releasable Aircraft Database, public domain, downloaded on 16 October 2026 and saved on this site, because the FAA replaces it every day. The saved copy leaves out the owners' names and street addresses and keeps five-digit ZIP codes; every other column is as published. With it come the Census Bureau's Vintage 2025 county population estimates, 2024 county boundaries and 2024 ZCTA gazetteer.
from pathlib import Path
import zipfile
import geopandas as gpd
import numpy as np
import pandas as pd
import requests
Path("data").mkdir(exist_ok=True)
# FAA Releasable Aircraft Database, 16 October 2026: MASTER.txt without the NAME, STREET, STREET2 and OTHER NAMES
# columns and with five-digit ZIP codes (MASTER-no-names.txt), ACFTREF.txt and ENGINE.txt as published; Census
# Vintage 2025 county estimates, 2024 cartographic boundary counties and 2024 ZCTA gazetteer. Saved here unchanged
# otherwise, because the FAA replaces the file daily.
# Live: https://registry.faa.gov/database/ReleasableAircraft.zip (needs a browser User-Agent),
# https://www2.census.gov/geo/docs/maps-data/data/gazetteer/2024_Gazetteer/2024_Gaz_zcta_national.zip
ARCHIVE = "faa-aircraft-registry-2026-10-16.zip"
if not Path(f"data/{ARCHIVE}").exists():
Path(f"data/{ARCHIVE}").write_bytes(requests.get(f"https://data.spatialworkflow.io/{ARCHIVE}", timeout=600).content)
Z = zipfile.ZipFile(f"data/{ARCHIVE}")
def faa(name):
"""One registry file: comma-separated, every field padded with spaces, a byte-order mark and a trailing comma."""
d = pd.read_csv(Z.open(name), dtype=str, encoding="utf-8-sig", keep_default_na=False)
d.columns = d.columns.str.strip()
d = d.loc[:, [c for c in d.columns if c and not c.startswith("Unnamed")]]
return d.apply(lambda c: c.str.strip())
reg = faa("MASTER-no-names.txt")
ref = faa("ACFTREF.txt")
TYPE_AIRCRAFT = {"1": "glider", "2": "balloon", "3": "blimp", "4": "fixed wing single engine", "5": "fixed wing multi engine",
"6": "rotorcraft", "7": "weight-shift", "8": "powered parachute", "9": "gyroplane", "H": "hybrid lift", "O": "other"}
REGISTRANT = {"1": "individual", "2": "partnership", "3": "corporation", "4": "co-owned", "5": "government", "7": "LLC",
"8": "non-citizen corporation", "9": "non-citizen co-owned", "": "blank"}
Path("data/cb_2024_us_county_20m.zip").write_bytes(Z.read("cb_2024_us_county_20m.zip"))
counties = gpd.read_file("data/cb_2024_us_county_20m.zip")
STATEFP = counties.drop_duplicates("STUSPS").set_index("STUSPS").STATEFP
pop = pd.read_csv(Z.open("co-est2025-alldata.csv"), encoding="latin-1", dtype={"STATE": str, "COUNTY": str})
pop = pop[pop.SUMLEV == 50].assign(GEOID=lambda p: p.STATE + p.COUNTY)
print(f"FAA registry, 16 October 2026: {len(reg):,} aircraft, {len(ref):,} make-and-model codes")
FAA registry, 16 October 2026: 317,604 aircraft, 94,115 make-and-model codes
Example 1: by type of owner
v["owner"] = v["TYPE REGISTRANT"].map(REGISTRANT)
print(v.groupby("owner").age.agg(["count", "median"]).sort_values("median").to_string())
count median
owner
non-citizen co-owned 14 5.0
government 3668 20.0
non-citizen corporation 980 20.0
corporation 45788 29.0
LLC 68921 30.0
co-owned 22015 53.0
individual 99457 53.0
partnership 2411 58.0
Individuals' and co-owners' aircraft have a median age of 53, LLCs' 30 and corporations' 29: newer aircraft are more often owned through a company.
Example 2: by state
s = v[v.COUNTRY == "US"].groupby("STATE").age.agg(["count", "median"])
s = s[s["count"] >= 1000].sort_values("median")
print("youngest:", ", ".join(f"{k} {m:.0f}" for k, m in s["median"].head(4).items()))
print("oldest:", ", ".join(f"{k} {m:.0f}" for k, m in s["median"].tail(4).items()))
youngest: UT 18, DE 26, LA 36, IL 36
oldest: ME 51, MN 51, WA 51, AK 59
Utah's registered aircraft have the youngest median age, 18 years, then Delaware's 26; both states hold many company-registered aircraft at trust and agent addresses. Alaska's are the oldest, 59 years, followed by Maine, Minnesota and Washington at 51.
Internal links
- Read the FAA registry β the columns used here
- Make and model β which designs the old aircraft are
- Status codes β why this page keeps status V
- Registration addresses β what an LLC owner means for a state's figures