Downloading the FAA aircraft registry is the easy part.
The FAA publishes a releasable aircraft database as a downloadable archive and says the data is refreshed daily at about 11:30 p.m. Central. The archive currently contains seven file families: the registration master, aircraft reference data, engine reference data, document index, deregistered aircraft, reserved N-numbers, and dealer applicants. The FAA also distinguishes required fields from permissible fields, which means a blank value is not automatically a bad record. (FAA source)
That is enough to build a script that converts rows to JSON. It is not enough to build a production API.
When we built the Grizzly Bulls Aircraft Intelligence API, the difficult work was everything between "download a ZIP file" and "return a response another application can safely depend on." The useful architecture emerged from treating the registry as an upstream evidence source rather than treating its current file layout as the product contract.
The source is a bundle, not one clean table
A developer looking at the FAA download for the first time might focus on the master registration file. That file is important, but it is only one part of the source.
The current archive includes:
MASTER.txtACFTREF.txtENGINE.txtDOCINDEX.txtRESERVED.txtDEALER.txtDEREG.txt
The first production lesson was to validate the archive as a bundle. A missing file, duplicated file, incompatible header, malformed row, or catastrophic row-count change should stop publication before it reaches users.
That sounds conservative until you consider the alternative. If an upstream layout changes and your parser silently shifts one column, an API can continue returning perfectly valid JSON filled with wrong data. A hard failure is usually cheaper than a plausible-looking bad response.
We therefore separate three questions:
- Did we receive the expected source package?
- Can we parse and validate it under the source contract we understand?
- Is the resulting normalized snapshot safe to replace the last known-good public state?
Those are different checks and should fail independently.
CSV is not a schema contract
The FAA download is comma-delimited, but real production parsing still has edge cases: quoted values, commas inside quoted fields, padded cells, CRLF versus LF line endings, trailing empty export columns, optional blanks, and source headers that can change over time.
The FAA's own download documentation has changed before. Its public page notes, for example, a July 2024 layout change to the document-index file. (FAA source)
That is why our parser validates required headers before interpreting rows. Raw FAA field names stay at the adapter boundary. They do not become permanent property names in the public API.
This distinction matters more than it first appears. If a downstream customer integrates directly against a provider's column vocabulary, every provider change becomes the customer's migration problem. A managed API should absorb as much of that churn as possible.
Joining reference data is part of the product
The registration master contains codes that become much more useful after they are joined to aircraft and engine reference data. A production pipeline therefore needs to do more than parse files independently.
For the current product, Grizzly Bulls normalizes the master registration data together with aircraft and engine reference families and retains deregistration information for historical and privacy semantics. The other known source families are still validated even when they do not yet feed a public endpoint.
We also verify that reference joins remain healthy before promotion. A source can be syntactically valid and still be operationally broken if most make/model or engine codes suddenly stop resolving.
This is one place where a thin "CSV to JSON" wrapper and a data product begin to diverge. The wrapper serializes what arrived. The data product decides what relationships must remain true before a new snapshot becomes authoritative.
N-number lookup is not the same as aircraft identity
The next problem is identity.
An N-number is a U.S. registration identifier. It is useful for lookup, but it can change and can later be reassigned. We therefore do not treat the N-number as permanent physical-aircraft identity.
The source also includes an FAA UNIQUE ID, but that identifies a registry record and is not automatically proof of a durable airframe identity across registration changes.
For exact source-level airframe matching, our current pipeline uses the FAA manufacturer code plus manufacturer serial number when both are available. If those inputs are missing, identity remains unresolved rather than falling back to an owner name, N-number, Mode S code, model string, or fuzzy text match.
That may produce fewer "resolved" identities than an aggressive heuristic. It also avoids silently merging two aircraft because a convenient field happened to look similar.
The public API then exposes a separate opaque aircraft identifier only when the underlying identity has been resolved under that rule.
Promotion should be atomic
A production registry API should never expose half of a new snapshot.
Our ingestion path stages a candidate, validates it, and promotes it transactionally. The current state only advances after the complete candidate passes the required checks. If promotion fails, the previous validated snapshot remains public.
Conceptually, the flow looks like this:
1FAA archive
2 -> source/package validation
3 -> parsing and normalization
4 -> reference and identity checks
5 -> staging
6 -> one transactional promotion
7 -> reviewed public read modelThe important part is not the specific database technology. The important part is that "we downloaded a newer file" and "users should now see this file" are separate decisions.
We also treat an identical archive checksum as an idempotent no-op after validation. Reprocessing the same source should not manufacture another historical observation, another change event, or another backup just because a scheduled job ran twice.
Provenance belongs in the response model
A normalized value without provenance is harder to debug, audit, or explain later.
For accepted snapshots, we retain source metadata such as retrieval time, source URL, source-contract version, and archive checksum. The public API can then tell a caller when the underlying snapshot was retrieved instead of pretending that every returned field is current to the instant of the HTTP request.
This is especially important for a source that refreshes on a schedule. "The API responded at 2:15 p.m." is not the same statement as "the FAA source represented this state at 2:15 p.m."
The source retrieval timestamp is evidence about observation time. It is not automatically the legal effective time of a registration change.
History changes the ingestion design
If all you want is current lookup, replacing yesterday's table with today's table may seem sufficient.
It becomes insufficient the moment you want to answer:
- When did we first observe this registration state?
- When did that state stop appearing?
- Which non-personal fields changed between accepted snapshots?
- Did the N-number become associated with a different resolved aircraft?
- Did the public source stop releasing registrant information?
We retain compact observed versions instead of storing a duplicate copy for every unchanged day. If a normalized registration state is unchanged, its observed interval extends. A new version starts only when retained state changes.
Separately, the system records bounded change events such as status changes, aircraft-assignment changes, and privacy transitions. The event stream is deliberately PII-free.
That temporal model is the foundation of the registration history API, not an afterthought added to a current-state table.
Privacy has to reach backward into history
The most important production complication is that historical retention can create a privacy problem if it is implemented naively.
The FAA now allows eligible private aircraft owners to request that certain personal data, including names and addresses, be withheld from public dissemination under 49 U.S.C. § 44114(b). (FAA withholding guidance)
If today's public source stops releasing a registrant field, a history product should not defeat that decision by serving yesterday's retained copy.
Our rule is intentionally conservative: current FAA non-release governs public historical PII. Matching retained names, addresses, location fields, postal data, and aliases are scrubbed from public history when the current source no longer releases them. If information is later released again, a new observation can contain the newly public value, but the older scrubbed value is not reconstructed.
That means privacy is not just a filter on the latest response. It is part of the storage and publication model.
A useful API is the maintained system around the endpoint
The HTTP route is the visible part of the product, but most of the reliability comes from work the caller never sees:
- source-package validation;
- deterministic parsing;
- normalized provider-independent fields;
- reference joins;
- conservative identity resolution;
- atomic last-known-good publication;
- idempotent refreshes;
- provenance;
- compact temporal history;
- privacy-aware historical suppression;
- backups and recovery checks; and
- stable authentication, quota, and error behavior.
That is what a managed API is selling.
The FAA remains the upstream authority. The value of the Grizzly Bulls Aircraft Intelligence API is that an application can consume a versioned JSON contract without independently operating this entire source-maintenance pipeline.
