Every airline carries three identifiers, and they are used in different places by different systems. Your ticket says BA117. Air traffic control hears Speedbird 117. A flight tracking feed shows BAW117. Same flight, three notations, and code that assumes any two are interchangeable will eventually produce something wrong.

This is the reference: what each system is, why they differ, a table of major carriers you can use directly, and how to resolve any code programmatically.

The three identifiers

IATA code — two characters. Assigned by the International Air Transport Association and used for everything commercial: tickets, baggage tags, timetables, reservation systems. BA, AA, LH. Because there are only so many two-character combinations, IATA codes include digits (B6 for JetBlue, U2 for easyJet, W6 for Wizz Air) and — critically — they get reused when an airline ceases operating.

ICAO code — three letters. Assigned by the International Civil Aviation Organization and used operationally: flight plans, ATC systems, and the callsign field an ADS-B receiver decodes. BAW, AAL, DLH. Three letters give far more space, and ICAO codes are not recycled the way IATA codes are.

Telephony callsign — a spoken word. The radio designator a crew uses on the air. SPEEDBIRD for British Airways, CACTUS historically for US Airways, SHAMROCK for Aer Lingus. Often unrelated to the airline's public name, which is why "Speedbird 117" means nothing to a passenger holding a BA117 boarding pass.

The practical rule: use ICAO codes as your internal key. They're stable, not reused, and they're what appears in operational data. Store IATA alongside for display, and never key a database on it.

Why the distinction matters

Three concrete failure modes.

IATA code reuse. When a carrier folds, its two-character code can be reassigned. A historical dataset keyed on IATA can therefore attribute one airline's flights to a completely different company. If you're doing anything with history, this is a real correctness problem rather than a theoretical one.

Flight number versus callsign. BA117 is the commercial flight number. BAW117 is what gets broadcast. The mapping is often a simple prefix swap and often isn't — codeshares, positioning flights and charters break it. There's a fuller treatment in callsign versus flight number.

Codeshares. One aircraft can be sold under several flight numbers by several airlines. The ADS-B broadcast carries one callsign, belonging to the operating carrier, so a passenger searching their marketing flight number won't find it in position data. That's covered in codeshare flights explained.

Airline tail fins on an apron

Reference table: major airlines

A working sample of widely-used carriers. This is a slice for quick reference, not the complete register — there are several thousand airlines including cargo, regional and charter operators, and the authoritative set is ICAO Doc 8585.

AirlineIATAICAOCallsignCountry
American AirlinesAAAALAMERICANUnited States
Delta Air LinesDLDALDELTAUnited States
United AirlinesUAUALUNITEDUnited States
Southwest AirlinesWNSWASOUTHWESTUnited States
Alaska AirlinesASASAALASKAUnited States
JetBlue AirwaysB6JBUJETBLUEUnited States
Air CanadaACACAAIR CANADACanada
British AirwaysBABAWSPEEDBIRDUnited Kingdom
Virgin AtlanticVSVIRVIRGINUnited Kingdom
easyJetU2EZYEASYUnited Kingdom
RyanairFRRYRRYANAIRIreland
Aer LingusEIEINSHAMROCKIreland
LufthansaLHDLHLUFTHANSAGermany
Air FranceAFAFRAIRFRANSFrance
KLMKLKLMKLMNetherlands
IberiaIBIBEIBERIASpain
SWISSLXSWRSWISSSwitzerland
Austrian AirlinesOSAUAAUSTRIANAustria
SASSKSASSCANDINAVIANSweden
FinnairAYFINFINNAIRFinland
TAP Air PortugalTPTAPAIR PORTUGALPortugal
Wizz AirW6WZZWIZZ AIRHungary
Turkish AirlinesTKTHYTURKISHTurkey
EmiratesEKUAEEMIRATESUnited Arab Emirates
Etihad AirwaysEYETDETIHADUnited Arab Emirates
Qatar AirwaysQRQTRQATARIQatar
Singapore AirlinesSQSIASINGAPORESingapore
Cathay PacificCXCPACATHAYHong Kong
Japan AirlinesJLJALJAPANAIRJapan
All Nippon AirwaysNHANAALL NIPPONJapan
Korean AirKEKALKOREANAIRSouth Korea
Asiana AirlinesOZAARASIANASouth Korea
Air ChinaCACCAAIR CHINAChina
China EasternMUCESCHINA EASTERNChina
China SouthernCZCSNCHINA SOUTHERNChina
Air IndiaAIAICAIRINDIAIndia
QantasQFQFAQANTASAustralia
Air New ZealandNZANZNEW ZEALANDNew Zealand
Ethiopian AirlinesETETHETHIOPIANEthiopia
South African AirwaysSASAASPRINGBOKSouth Africa
AeroméxicoAMAMXAEROMEXICOMexico
AviancaAVAVAAVIANCAColombia

Codes do change — airlines merge, rebrand and cease trading — so for anything operational, resolve at runtime rather than shipping a hardcoded copy of this table.

Resolving a code programmatically

One endpoint, one code:

import requests

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

# By ICAO (3 letters)
r = requests.get(f"{BASE}/airlines/search", headers=H, params={"icao": "AAL"})
print(r.json())

# By IATA (2 characters)
r = requests.get(f"{BASE}/airlines/search", headers=H, params={"iata": "AA"})
[
  {
    "id": 24,
    "name": "American Airlines",
    "alias": null,
    "iata": "AA",
    "icao": "AAL",
    "callsign": "AMERICAN",
    "country": "United States",
    "active": "Y",
    "logo": "https://media.skylinkapi.com/logos/AA.png"
  }
]

Three behaviours to code against, because they're not what you'd guess:

Send exactly one code. Passing both icao and iata returns 400. Pick the one you have.

An unknown code returns 200 with an empty array, not 404. This catches nearly everyone. Design for a missing-carrier placeholder rather than an error state:

def lookup_airline(code):
    param = "icao" if len(code) == 3 else "iata"
    r = requests.get(f"{BASE}/airlines/search", headers=H, params={param: code})
    r.raise_for_status()
    results = r.json()
    return results[0] if results else None      # [] means "not in the register"

active is a string, not a boolean. "Y" for operating, "N" for defunct. A truthiness check passes for both, since "N" is a non-empty string — a subtle bug that shows dead airlines as current.

airline = lookup_airline("PAA")
if airline and airline["active"] == "Y":        # not: if airline["active"]
    print(f"{airline['name']}{airline['callsign']}")

Also worth noting: alias, callsign, country, iata and icao can all be null on individual records. Small regional operators frequently have no IATA code at all, and cargo carriers often have no telephony designator.

An airport departure board

Going from a callsign to an airline

The common real-world case: a position feed hands you BAW117 and you want to display "British Airways". The ICAO callsign prefix is the first three letters, so the operation is a slice and a lookup — with caching, because airline records change on a timescale of years:

from functools import lru_cache

@lru_cache(maxsize=2048)
def airline_from_callsign(callsign):
    """BAW117 -> the British Airways record. Cache aggressively."""
    if not callsign or len(callsign) < 3:
        return None
    return lookup_airline(callsign[:3].upper())

a = airline_from_callsign("BAW117")
print(a["name"] if a else "unknown operator")

Two caveats. General aviation aircraft broadcast their registration as the callsign (N12345, G-ABCD), so the first three characters are not an airline code and the lookup returns an empty array — which is correct behaviour, not a failure. And business and charter operators often use registrations or unfamiliar designators, so expect a meaningful share of callsigns not to resolve to a scheduled carrier.

Our ADS-B endpoint already performs this join and returns an airline field on each record, so for live tracking you usually don't need to do it yourself.

When you want the whole register

Three options, depending on what you're building.

Resolve on demand via the API. Right for rendering flight cards and enriching live data. One call per code, cached. Available on the free trial.

Cache aggressively. Airline records change slowly — a merger or a rebrand, not daily churn. A lookup cached for a week is fine, and this is the difference between a handful of calls a day and thousands.

Buy the dataset for offline use. If you're populating a data warehouse, running analysis, or need the data with no per-request cost, the Worldwide Airline Database is a one-off commercially-licensed download in JSON, CSV and SQL — 5,900+ airline records covering name, ICAO and IATA codes, callsign, country and operational status, checked at 99% accuracy against ICAO Doc 8585. It's a paid product rather than a free download, and it makes sense when per-request lookups don't: offline processing, bulk joins, or anything where you need the full register rather than the codes you happen to encounter.

For how airline codes relate to airport codes — a related but separate system with its own rules — see ICAO vs IATA codes explained, and the airline database API post covers fleets and routes alongside the codes.