Flight data APIs are built around airlines, because that's where the money and the questions are. Schedules, flight numbers, gates, delays, on-time performance — the whole vocabulary assumes a scheduled commercial operation.

Then someone tries to track a Cessna 172 doing circuits at a grass strip, and nearly every assumption breaks at once. There's no flight number. There's no schedule to be late against. The departure airport has no tower, no FIDS, and possibly no paved runway. The aircraft may be broadcasting on a frequency the feed doesn't listen to. And for a good part of the flight it's below the altitude where anyone can hear it.

This post is about what actually works, for flight schools, maintenance shops, aircraft owners, and anyone building for the light-aircraft end of aviation.

A tail number is the identity, not a flight number

The first structural difference. An airline flight has a callsign like BAW178 that maps to a commercial flight number, and that's what systems key on. A light aircraft typically broadcasts its own registration as the callsign — N12345, G-ABCD — because there is no flight number. The aircraft is the flight.

Any pipeline that parses a callsign by stripping a three-letter airline prefix will produce nonsense on N12345. Any lookup that tries to resolve a callsign to a scheduled flight will find nothing. The callsign versus flight number distinction matters everywhere, and in GA it's not a subtlety, it's the whole model.

Track on the ICAO24 hex address instead. It belongs to the airframe, it doesn't change with what the pilot typed, and it's the only stable key in the system.

import requests

BASE = "https://skylink-api.p.rapidapi.com"
H = {"X-RapidAPI-Key": KEY, "X-RapidAPI-Host": "skylink-api.p.rapidapi.com"}

# Filter the live feed by tail number
r = requests.get(f"{BASE}/adsb/aircraft", headers=H,
                 params={"registration": "N12345"})
for a in r.json().get("aircraft", []):
    print(f"{a['icao24']} {a.get('callsign')} "
          f"{a.get('latitude')},{a.get('longitude')} "
          f"{a.get('altitude')} ft  on_ground={a.get('is_on_ground')}")

US registrations map to hex codes arithmetically

A useful quirk. Most countries assign ICAO 24-bit addresses from their allocated block without a public formula, so going from a registration to a hex address needs a registry lookup. The United States is different: N-numbers map onto the US address block deterministically, which is why tools exist that convert N12345 to its hex offline with no lookup at all.

Practically this means US GA tracking has an escape hatch — if you have the tail number you can derive the hex without a registry round-trip. Outside the US you need the registry, which is what aircraft lookup is for, and what a tail number lookup resolves.

There is no schedule, so most endpoints don't apply

Worth stating bluntly, because people spend time looking for something that doesn't exist. A privately operated aircraft has no published schedule. There is no scheduled departure time, no estimated arrival, no gate, and no delay, because there's nothing to be delayed against. A flight plan may exist, but a filed VFR flight plan is a search-and-rescue instrument, not a public timetable.

So for GA work, schedules, flight status, and delay data are simply the wrong tools. What exists is position data, and everything you want has to be derived from it: departure time from the first airborne position, arrival from the last, duration from the difference, and the airport from proximity rather than from a field in a record.

That derivation is doable and it's real work. Detecting the transitions is its own problem, covered in detecting takeoffs and landings from ADS-B, and it's substantially harder at an untowered field where there's no independent source to check against.

A Cessna 152 light aircraft

The 978 MHz problem

This is the one that silently loses you aircraft, and most integrations never find out.

Outside the US, ADS-B means 1090 MHz Extended Squitter and that's the end of it. In the US there are two links. Aircraft operating below 18,000 feet may equip with UAT on 978 MHz instead of 1090ES, and a great many light aircraft did exactly that, because the equipment was cheaper and it came with free weather and traffic uplink.

UAT is a completely different protocol — different frequency, different modulation, different message structure. A receiver network built for 1090 does not hear it. Not "hears it poorly" — does not hear it at all.

The consequence for anyone tracking US general aviation: a meaningful slice of the fleet is invisible to a 1090-only feed, and the aircraft that are missing are disproportionately the light singles that GA products care about. If you're evaluating providers for GA work, this is the specific question to ask, and "we have global ADS-B coverage" is not an answer to it.

Worth knowing where your data comes from before you promise a customer you can see their aircraft.

Low and slow is where coverage runs out

ADS-B is line of sight. Reception range scales with altitude, and light aircraft spend their time at altitudes where the geometry is unfavourable.

An airliner at FL350 is receivable from a couple of hundred miles. A Cessna in the circuit at 1,000 feet AGL might be heard from twenty or thirty miles in flat open country with a good nearby receiver, and from almost nowhere in hilly terrain or an area with no volunteer receivers. Since GA flies from small fields in rural areas, and receiver density follows population, the coverage gap lines up badly with exactly where the aircraft are.

Three consequences for your product:

Circuits often don't render. An aircraft doing touch-and-goes may appear and disappear repeatedly as it turns and changes attitude, because antenna shielding by the airframe matters at low elevation angles.

Position loss is not a landing. At an untowered field with poor coverage, an aircraft descending below reception looks identical to one that landed. Don't infer arrival from track loss alone — check the last position's altitude, speed, and proximity to a field, and hedge accordingly.

Cross-country flights look like fragments. The aircraft appears in coverage, vanishes over a gap, reappears. Sessionising that into one flight rather than three is your job, and the reasoning is the same as in handling gaps, jumps, and bad positions.

A grass airstrip at a small rural airfield

Not everything is equipped, and some of it is deliberately hidden

Two more sources of absence.

The US ADS-B Out mandate applies in specified airspace, not everywhere. An aircraft that stays in uncontrolled airspace and never enters mandated airspace is not required to carry ADS-B Out, and plenty of older light aircraft don't. A vintage taildragger operating from a private strip may broadcast nothing at all, and no data provider can produce it.

Separately, owners can request that their aircraft be suppressed from public display, and GA owners take that up more than airlines do — the programmes are covered in why planes don't show on flight trackers. If a customer's aircraft is missing and it's definitely equipped, a privacy filter is a likely explanation before a coverage one.

Building something that works anyway

Given all of that, GA tracking still works, provided the product is honest about what it knows.

Key on the hex, never the callsign. Filter the live feed by registration or icao24 rather than trying to match text. Enrich with aircraft lookup once per airframe and cache it — a training fleet is a few dozen aircraft, so this is cheap.

Derive events rather than expecting them, and attach confidence. "Last seen 14:32 near Popham, 900 ft, descending" is an honest statement. "Landed 14:32" is a claim the data may not support.

Show coverage as a first-class concept. A flight school tracking its fleet is far better served by a map that greys out an aircraft as out of coverage than by one that leaves a stale dot sitting confidently on the last known position.

For fleet-level operations the adjacent problem is covered in fleet monitoring for corporate flight departments, which deals with the business-aviation end of the same data.

Getting started

The ADS-B endpoint accepts registration and icao24 filters directly, which is the whole integration for a small fleet, and aircraft lookup resolves registration details. Both are on the free trial.

One honest caveat to close on, since this post has been about limitations: for US light aircraft specifically, check whether the airframes you care about are on 1090 or 978 before you build. If they're on UAT, no amount of integration work on a 1090-derived feed will make them appear, and that's worth knowing on day one rather than after you've shipped.