Observed history, not a reconstructed legal ownership ledgerThe endpoint is GET https://api.grizzlybulls.com/v1/aircraft/{nNumber}/history. It returns retained public versions and change events from snapshots Grizzly Bulls actually accepted. Observation timestamps describe when the source snapshot was retrieved; they do not prove the exact legal or real-world effective time of a change.
How the temporal contract works
History is deliberately split into state intervals and events so callers can distinguish what was observed from what changed between accepted snapshots.
Observed versions
A version contains the normalized public state retained for an N-number. Its observedFrom.retrievedAt and observedThrough.retrievedAtboundaries identify the first and last accepted source snapshots where that version was observed. Unchanged state extends the observation interval instead of inventing a new version every day.
PII-free change events
Events use a bounded vocabulary such as status_changed, aircraft_assignment_changed, registrant_changed, and registrant_pii_withheld. Their changedFields list exposes reviewed public field groups, not old and new owner names or addresses.
Two independent pages
Versions and events paginate independently. Each side returns its own limit, offset, total, and hasMore, so an application can advance one stream without discarding the other.
Sanitized example: what an observed change means
This is an illustrative timeline, not a live aircraft record. The example uses only non-personal fields to show how versions and events relate.
1. Version observed
N123AB is observed with source status V in accepted snapshots retrieved September 20 through September 22. Those retrieval timestamps bracket the observation interval; they are not a legal registration period.
2. Change observed
The September 23 snapshot produces status_changed with changedFields: ["registration_status"]. Its observedAt is the snapshot retrieval time. A sourceEffectiveDate may be null and is never inferred from observedAt.
3. New version begins
A new retained version records source status A beginning with the September 23 observation. Later identical accepted snapshots extend observedThrough until another retained field changes.
Request bounded history from your server
Use the same Bearer API key across the Aircraft API. Browser CORS is not enabled, so these examples are intended for server-side use. Replace N_NUMBER with an accepted U.S. N-number.
cURL
curl --fail-with-body \ -H "Authorization: Bearer $GRIZZLY_BULLS_API_KEY" \ "https://api.grizzlybulls.com/v1/aircraft/N_NUMBER/history?versionsLimit=10&versionsOffset=0&eventsLimit=10&eventsOffset=0"
Python 3 · standard library
import json
import os
import urllib.parse
import urllib.request
query = urllib.parse.urlencode({
"versionsLimit": 10,
"versionsOffset": 0,
"eventsLimit": 10,
"eventsOffset": 0,
})
request = urllib.request.Request(
"https://api.grizzlybulls.com/v1/aircraft/N_NUMBER/history?" + query,
headers={"Authorization": f"Bearer {os.environ['GRIZZLY_BULLS_API_KEY']}"},
)
with urllib.request.urlopen(request) as response:
history = json.load(response)
print(history["data"]["nNumber"])
print(history["pagination"]["versions"])
for event in history["data"]["events"]:
print(event["observedAt"], event["eventType"], event["changedFields"])
JavaScript · Node.js 18+
const params = new URLSearchParams({
versionsLimit: "10",
versionsOffset: "0",
eventsLimit: "10",
eventsOffset: "0",
});
const response = await fetch(
"https://api.grizzlybulls.com/v1/aircraft/N_NUMBER/history?" + params,
{
headers: {
Authorization: "Bearer " + process.env.GRIZZLY_BULLS_API_KEY,
},
},
);
if (!response.ok) {
throw new Error("Aircraft history request failed: " + response.status);
}
const history = await response.json();
console.log(history.data.nNumber, history.pagination);
console.log(history.data.events);
Versions
versionsLimit defaults to 50 and accepts 1 through 100.versionsOffset defaults to 0 and is bounded at 10,000.pagination.versions.total and hasMore describe only versions.- Versions are returned newest observation interval first.
Events
eventsLimit defaults to 50 and accepts 1 through 100.eventsOffset defaults to 0 and is bounded at 10,000.pagination.events.total and hasMore describe only events.- Events are returned newest observation first.
Invalid N-number or pagination input returns 400 invalid_request before history usage is reserved. Once valid input reaches access control, the history_lookup operation is metered before the repository read.
What comes back
The public history contract is aircraft-public-v1. The OpenAPI contract remains the machine-readable authority for required fields, nullability, and enums.
Version fields
Each version includes the N-number and registration fields, the historical aircraft subset retained at that observation, currently releasable registrant fields, sourceProvider, and provenance at both observedFrom and observedThrough.
Event fields
Events include eventType, bounded changedFields, old and new source registration status where applicable, nullable sourceEffectiveDate, observedAt, previousObservedAt, and source provenance.
Not silently backfilled
Historical versions expose the fields actually retained for that observation. Grizzly Bulls does not join today's aircraft or engine reference descriptions onto old versions and present them as historical evidence.
Observation time versus effective date
What the API can establish
observedAt, observedFrom.retrievedAt, and observedThrough.retrievedAt come from accepted source-snapshot retrievals. They establish when Grizzly Bulls observed a public state or change in its source pipeline.
What the API does not infer
sourceEffectiveDate is nullable. Grizzly Bulls does not synthesize it from a retrieval timestamp, certificate date, last-activity date, or another nearby field. A daily snapshot can show that a state changed between observations without proving the exact legal instant that change took effect.
Current FAA releasability governs historical PII
Withholding scrubs retained personal fields
If the current public FAA source stops releasing registrant PII for an aircraft, Grizzly Bulls suppresses matching retained names, street addresses, locations, postal data, and aliases from public history. Historical retention is never used to bypass a current withholding decision.
Suppression is not undone retroactively
The history store treats that scrub as irreversible for older retained values. If the FAA later releases public registrant information again, a newer observation may contain newly released fields, but previously scrubbed historical values are not restored. A registrant_pii_withheld event can describe the transition without exposing the removed PII.
Aircraft registration data is not proof of beneficial economic ownership. History reflects public registry observations subject to the current publication and privacy boundary.
Errors, quotas & retries
400 invalid_request
Fix the N-number or bounded pagination parameters before retrying.
401 unauthenticated
The Bearer API key is missing or invalid.
404 not_found
No retained versions or events were found for that valid N-number.
429 rate_limited
Respect Retry-After and the X-Aircraft-Quota-* headers when a rate or monthly quota is reached.
503 service_unavailable
Retry temporary authority or read-model failures with bounded exponential backoff.
500 internal_error
A server failure is returned without exposing provider or database internals.
Related technical guides
What daily registry snapshots can prove
Go deeper on observation time, effective-date uncertainty, compact versions, PII-free events, and the limits of registry evidence.
Read the history evidence guide →How the source becomes a maintained API
See how daily validation, atomic publication, provenance, and privacy-aware history fit together before a snapshot becomes public.
Read the production pipeline guide →Start with a free key
Free includes 1,000 requests per month and 30 requests per minute. Current lookup, bounded discovery, and retained registration history use the same key.