How to Read the FAA Aircraft Registry in Python
On this page
Problem statement
The FAA publishes its whole aircraft registry as a daily download: every US civil aircraft with an N-number, its make and model code, year built, type of aircraft and engine, the kind of owner, the owner's mailing city, state, ZIP code and county, and its transponder address. It is the most complete public list of where US aircraft are registered. The main file, MASTER.txt, looks like a CSV but pads every field with spaces, starts with a byte-order mark and ends every line with a comma. This page reads it cleanly with the 16 October 2026 file, decodes the type codes and puts the aircraft on a county map.
Quick answer
Read everything as text, strip the padding from names and values, and drop the empty column the trailing comma creates. The setup below wraps that in faa():
reg = faa("MASTER-no-names.txt")
print(f"{len(reg):,} records; valid registrations (status V): {(reg['STATUS CODE'] == 'V').sum():,}")
print(reg[["N-NUMBER", "YEAR MFR", "TYPE AIRCRAFT", "CITY", "STATE", "MODE S CODE HEX"]].head(3).to_string(index=False))
317,604 records; valid registrations (status V): 310,844
N-NUMBER YEAR MFR TYPE AIRCRAFT CITY STATE MODE S CODE HEX
100 1940 4 KETCHUM OK A004B3
10000 4 NASHVILLE TN A00725
10001 1928 4 LAKELAND FL A00726
The file holds 317,604 records, 310,844 of them valid registrations (status V). N100 is a 1940 aircraft registered in Ketchum, Oklahoma; N10000 has no year at all. The hexadecimal Mode S code is the address ADS-B receivers report.
Padding, codes and the US rows
1. What the raw file looks like
raw = Z.open("MASTER-no-names.txt").read(700).decode("utf-8")
print(repr(raw[:60]))
print(repr(raw.splitlines()[1][:90]))
print(f"header fields: {len(raw.splitlines()[0].split(','))}, last one: {raw.splitlines()[0].split(',')[-1]!r}")
'\ufeffN-NUMBER,SERIAL NUMBER,MFR MDL CODE,ENG MFR MDL,YEAR MFR,TY'
'100 ,5334 ,7100510,17003,1940,1,KETCHUM ,OK,74349 '
header fields: 27, last one: ''
The file starts with ο»Ώ, the UTF-8 byte-order mark, which glues itself to the first column name unless the file is read with utf-8-sig. Values are padded to fixed widths, and the header's 27th field is empty because every line ends with a comma.
2. Decode the type codes
t = reg["TYPE AIRCRAFT"].map(TYPE_AIRCRAFT).value_counts()
print(t.to_string())
r = reg["TYPE REGISTRANT"].map(REGISTRANT).value_counts()
print(r.head(5).to_string())
TYPE AIRCRAFT
fixed wing single engine 221615
fixed wing multi engine 49635
rotorcraft 28928
balloon 5275
glider 4879
hybrid lift 3806
powered parachute 1913
weight-shift 1058
gyroplane 427
blimp 49
other 19
TYPE REGISTRANT
individual 130078
LLC 87846
corporation 61436
co-owned 25802
government 5716
Most registered aircraft are fixed-wing single-engine aeroplanes, 221,615 of them. Rotorcraft come third with 28,928, and most of the 3,806 "hybrid lift" aircraft are delivery drones (see the page on aircraft types). Individuals own 130,078 aircraft and LLCs 87,846, more than corporations' 61,436.
3. Keep the US addresses
print(reg.COUNTRY.replace("", "(blank)").value_counts().head(5).to_string())
us = reg[reg.COUNTRY == "US"]
print(f"US addresses: {len(us):,}; states and territories: {us.STATE.nunique()}")
COUNTRY
US 311110
(blank) 5069
RQ 490
DE 366
AT 115
US addresses: 311,110; states and territories: 53
311,110 records have a US mailing address, across 53 state and territory codes. 5,069 have a blank country and 490 are in Puerto Rico (RQ is the FAA's country code for it); the rest have addresses abroad.
States, models and a county map
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: aircraft by state
print(us.STATE.value_counts().head(8).to_string())
STATE
TX 29883
CA 27569
FL 21718
DE 10951
WA 10423
AK 9145
GA 8786
OH 8526
Texas and California have the most registered aircraft, as their size suggests, but Delaware is fourth with 10,951, ahead of Washington and Alaska. That is an address effect, not a fleet based there (see the registration address page).
Example 2: makes and models
m = us.merge(ref[["CODE", "MFR", "MODEL", "NO-SEATS"]], left_on="MFR MDL CODE", right_on="CODE", how="left")
print(m.groupby(["MFR", "MODEL"]).size().nlargest(5).to_string())
MFR MODEL
PIPER PA-28-140 4201
CIRRUS DESIGN CORP SR22 3929
CESSNA 172M 3509
PIPER PA-28-180 3464
J3C-65 3172
Joining on the seven-character make-and-model code gives model names. The most common model string is the Piper PA-28-140 with 4,201 aircraft, then the Cirrus SR22, the Cessna 172M and the Piper PA-28-180. (The blank beside J3C-65 is pandas not repeating PIPER in a grouped index, not a missing name.)
Example 3: a county map
us = us.assign(GEOID=us.STATE.map(STATEFP) + us.COUNTY)
n = us.GEOID.value_counts()
low48 = counties[~counties.STUSPS.isin(["AK", "HI", "PR"])].to_crs(5070)
m = low48.merge(n.rename("aircraft"), left_on="GEOID", right_index=True, how="left")
print(f"counties drawn: {len(m):,}; with aircraft: {m.aircraft.notna().sum():,}; most: {m.loc[m.aircraft.idxmax(), 'NAMELSAD']}, {m.loc[m.aircraft.idxmax(), 'STUSPS']} ({m.aircraft.max():,.0f})")
ax = m.plot(column=np.log10(m.aircraft.fillna(0) + 1), cmap="Blues", figsize=(10, 6))
ax.set_axis_off()
counties drawn: 3,109; with aircraft: 3,076; most: New Castle County, DE (8,478)
Of 3,109 counties in the lower 48, 3,076 have at least one registered aircraft. New Castle County, Delaware, the county of Wilmington, has the most, 8,478, for the same address reason.
Internal links
- Registered aircraft per resident β the map scaled by population
- Join records to make and model β ACFTREF in full
- Status codes β what V and the other codes mean
- Registration addresses β why Delaware has so many aircraft
- County codes that no longer exist β the rows that miss the county map
FAQ
How do I download the FAA aircraft registry?
From the FAA's Releasable Aircraft Database page: one zip, ReleasableAircraft.zip, rebuilt every day. MASTER.txt holds one row per registered N-number; the FAA's server refuses some scripted downloads unless the request sends a browser User-Agent.