# Airline Flight Load API Specification Integration-ready API reference for querying flight loads, standby lists, and seat availability across 7 airlines. All endpoints confirmed working as of April 2026. --- ## Architecture Overview All airlines except Spirit require **Playwright** (headless browser) to bypass Akamai TLS fingerprinting. The pattern is: 1. Navigate to the airline's domain to establish a browser session 2. Use `page.evaluate(async () => { const r = await fetch(...); return r.json(); })` to call APIs from within the browser context Spirit works with plain HTTP requests (curl/fetch). --- ## UNITED AIRLINES ### Base Setup ``` Domain: https://www.united.com Auth: Anonymous token (no login required) Method: Playwright → navigate to united.com → page.evaluate(fetch) ``` ### Step 1: Get Token ``` GET /api/auth/anonymous-token Headers: none required ``` **Response:** ```json { "data": { "token": { "hash": "DAAAA...", "expiresAt": "2026-04-08T19:42:57Z" } } } ``` Token is valid ~30 minutes. Refresh as needed. ### Step 2: Search Flights by Route ``` GET /api/flightstatus/status/{flightNumber}/{date}?carrierCode=UA&useLegDestDate=true Path params: flightNumber: "2238" date: "2026-04-08" Headers: x-authorization-api: bearer {token.hash} Accept: application/json ``` **Response:** Flight status with departure/arrival times, gates, terminals, aircraft type, tail number, delays. ### Step 3: Get Flight Loads + Standby List ``` GET /api/flightstatus/upgradeListExtended?flightNumber={num}&flightDate={YYYY-MM-DD}&fromAirportCode={origin} Headers: x-authorization-api: bearer {token.hash} Accept: application/json ``` **Response:** ```json { "segment": { "airlineCode": "UA", "flightNumber": 2238, "flightDate": "20260408", "departureAirportCode": "EWR", "arrivalAirportCode": "LAX", "equipmentDescriptionLong": "Boeing 777-200ER", "departed": false }, "pbts": [ { "cabin": "Front", "capacity": 50, "authorized": 50, "booked": 50, "held": 0, "reserved": 0, "revenueStandby": 0, "waitList": 0, "jump": 0, "group": 0, "ps": 1, "sa": 5 }, { "cabin": "Middle", "capacity": 24, "authorized": 24, "booked": 16, "held": 0, "reserved": 0, "revenueStandby": 0, "waitList": 0, "jump": 0, "group": 0, "ps": 0, "sa": 0 }, { "cabin": "Rear", "capacity": 202, "authorized": 202, "booked": 164, "held": 0, "reserved": 0, "revenueStandby": 2, "waitList": 0, "jump": 0, "group": 0, "ps": 0, "sa": 4 } ], "checkInSummaries": [ { "cabin": "Front", "capacity": 50, "total": 50, "etktPassengersCheckedIn": 50, "revStandbyCheckedInWithoutSeats": 0, "nonRevStandbyCheckedInWithoutSeats": 0, "children": 0, "infants": 0, "bags": 0 } ], "numberOfCabins": 3, "front": { "cleared": [ { "currentCabin": "Front", "bookedCabin": "Rear", "firstName": "T", "lastName": "JEN", "passengerName": "T/JEN", "seatNumber": "1G", "clearanceType": "Upgrade", "skipped": false } ], "standby": [] } } ``` ### Field Reference | Field | Description | |---|---| | `pbts[].cabin` | "Front" (First/Polaris), "Middle" (Premium+), "Rear" (Economy) | | `pbts[].capacity` | Total seats in cabin | | `pbts[].booked` | Seats sold/assigned | | `pbts[].revenueStandby` | Revenue standby passengers | | `pbts[].sa` | Space available (non-rev standby) | | `pbts[].ps` | Positive space | | `pbts[].waitList` | Waitlisted passengers | | `front.cleared[]` | Passengers cleared for upgrade | | `front.standby[]` | Passengers on standby | ### Derived Values ``` availableSeats = capacity - booked loadFactor = booked / capacity ``` --- ## AMERICAN AIRLINES ### Base Setup ``` Domain: https://cdn.flyaa.aa.com Auth: None required Method: Playwright → set mobile UA headers → navigate to cdn.flyaa.aa.com → page.evaluate(fetch) Required context headers (set via page.context().setExtraHTTPHeaders): User-Agent: Android/2025.31 Pixel 7|14|1080|2400|1.0|AmericanAirlines x-clientid: MOBILE Accept: application/json Content-Type: application/json Device-ID: {any-uuid} ``` ### Step 1: Search Flights by Route ``` GET /apiv2/mobile-flifo/flightSchedules/v1.0?origin={ORIG}&destination={DEST}&departureDay={D}&departureMonth={M}&searchType=schedule&noOfFlightsToDisplay=20 ``` **Response:** ```json { "flightSchedules": { "flights": [ [{ "flightKey": "AA:3390:2026-04-09:DFW:0", "operatingCarrierCode": "AA", "operatingCarrierName": "AMERICAN EAGLE", "marketingCarrierCode": "AA", "flightNumber": "3390", "originAirportCode": "DFW", "originCity": "Dallas/ Fort Worth", "destinationAirportCode": "IAH", "destinationCity": "Houston", "departDate": "2026-04-09T07:01:00.000-05:00", "arrivalDate": "2026-04-09T08:20:00.000-05:00", "showUpgradeStandbyList": false, "allowFSN": false }] ] } } ``` ### Step 2: Get Waitlist + Available Seats ``` GET /api/mobile/loyalty/waitlist/v1.2?carrierCode=AA&flightNumber={NUM}&departureDate={YYYY-MM-DD}&originAirportCode={ORIG}&destinationAirportCode={DEST} Additional headers: x-referrer: fs ``` **Response:** ```json { "relevantList": "First", "footer": [ "If your upgrade has cleared or you clear the waitlist, please refresh your mobile boarding pass.", "The order of names may change as additional customers check in." ], "waitList": [ { "listName": "First", "seatsAvailableLabel": "Available seats", "seatsAvailableValue": 1, "seatsAvailableSemanticColor": "failure", "passengers": [ {"order": 1, "displayName": "BRI, K", "cleared": false, "seat": null, "highlighted": false}, {"order": 2, "displayName": "MAT, R", "cleared": false, "seat": null, "highlighted": false} ] }, { "listName": "Standby", "seatsAvailableLabel": "Available seats", "seatsAvailableValue": 45, "seatsAvailableSemanticColor": "success", "passengers": [ {"order": 1, "displayName": "MIT, R", "cleared": false, "seat": null, "highlighted": false}, {"order": 2, "displayName": "MAR, M", "cleared": false, "seat": null, "highlighted": false} ] } ] } ``` ### Field Reference | Field | Description | |---|---| | `waitList[].listName` | "First", "Standby", etc. | | `waitList[].seatsAvailableValue` | Number of open seats for that class | | `waitList[].seatsAvailableSemanticColor` | "success" (green, many), "warning" (yellow, few), "failure" (red, <=1) | | `waitList[].passengers[].displayName` | Passenger name (LAST, F) | | `waitList[].passengers[].order` | Position on list (1-based) | | `waitList[].passengers[].cleared` | true if cleared from list | | `waitList[].passengers[].seat` | Seat number if cleared | --- ## SPIRIT AIRLINES ### Base Setup ``` Domain: https://api.spirit.com Auth: APIM subscription key (no login required) Method: Plain HTTP (curl/fetch) — no Playwright needed ``` ### Step 1: Get Flight Status ``` POST https://api.spirit.com/customermobileprod/2.8.0/v3/GetFlightInfoBI Headers: Content-Type: application/json Ocp-Apim-Subscription-Key: c6567af50d544dfbb3bc5dd99c6bb177 Platform: Android Body: { "departureStation": "FLL", "arrivalStation": "ATL", "departureDate": "2026-04-08" } ``` **Response:** ```json { "getFlightInfoBIResult": [ { "flightNumber": "NK204", "journeyID": 1, "departureStationCode": "FLL", "arrivalStationCode": "ATL", "departureGate": "F4", "arrivalGate": "C4", "departureTerminal": "4", "arrivalTerminal": "N", "legStatus": "Arrived", "departureTime": "8:57am", "arrivalTime": "11:04am", "scheduledDeparture": "scheduled at 8:24am", "scheduledArrival": "scheduled at 10:22am", "departureCity": "Fort Lauderdale, FL", "arrivalCity": "Atlanta, GA", "flightStatusColor": "#FF9500", "totalDurationMinutes": 127, "departureDateTime": "2026-04-08T08:57:00", "arrivalDateTime": "2026-04-08T11:04:00" } ] } ``` ### Step 2: Get Station/Route Network ``` GET https://api.spirit.com/customermobileprod/2.8.0/v1/stations Headers: Ocp-Apim-Subscription-Key: c6567af50d544dfbb3bc5dd99c6bb177 ``` Returns all Spirit stations with airport codes, coordinates, city names, and which routes connect to each station. ### Notes - Spirit is a ULCC — no standby lists or upgrade waitlists - Seat-level availability requires a booking session + Akamai bypass (not publicly accessible) - The APIM key was decrypted from the native library in the Android APK --- ## KOREAN AIR ### Base Setup ``` Domain: https://www.koreanair.com Auth: None required for flight status; minimal headers Method: Playwright → navigate to koreanair.com → page.evaluate(fetch) ``` ### Step 1: Search Flights ``` POST /api/fs/scheduleFlightSearch/flight/status/app Headers: Content-Type: application/json Accept: application/json channel: app Body (by flight number): { "departureDate": "20260408", "departureLocationCode": "", "arrivalLocationCode": "", "flightNumber": "017", "searchOption": "FLTNUM" } Body (by route): { "departureDate": "20260408", "departureLocationCode": "ICN", "arrivalLocationCode": "LAX", "flightNumber": "", "searchOption": "ROUTE" } ``` **Response:** ```json { "scheduleInformation": { "flightInformation": { "flightDetailsList": [ { "departureAirport": "ICN", "arrivalAirport": "LAX", "departureDate": "20260408", "departureTime": "1430", "arrivalTime": "0940", "flightNumber": "017", "flyingTime": "1110", "status": {"code": "ARV", "codeByUI": "ARV"}, "cabinClassInfoList": [ {"cabinClassOfService": "1"}, {"cabinClassOfService": "2"}, {"cabinClassOfService": "3"} ], "scheduleFlightUIInfoMsOutVo": { "flightStatus": "arrived", "departureUIInfo": { "scheduledTime": "14:30", "actualTime": "14:27" }, "arrivalUIInfo": { "scheduledTime": "09:40", "actualTime": "09:47" } } } ] } } } ``` ### Step 2: Get Seat Count ``` POST /api/et/ibeSupport/flightSeatCount Headers: Content-Type: application/json Accept: application/json channel: pc Body: { "carrierCode": "KE", "flightNumber": "017", "departureAirport": "ICN", "arrivalAirport": "LAX", "departureDate": "20260409" } ``` **Response:** ```json { "seatCount": 0, "carrierCode": "KE", "flightNumber": "017" } ``` Note: `seatCount` returns available seats. Returns 0 for dates too far out — works best within 24-48 hours of departure. ### Step 3: Get Availability ``` POST /api/fs/scheduleFlightSearch/sdcAirMultiAvailability Headers: Content-Type: application/json Accept: application/json channel: pc Body: { "departureDate": "20260409", "departureLocationCode": "ICN", "arrivalLocationCode": "LAX", "flightNumber": "001", "searchOption": "FLTNUM" } ``` --- ## Playwright Integration Pattern All airlines except Spirit use the same Playwright wrapper pattern: ```javascript const { chromium } = require('playwright'); async function queryAirline(airlineDomain, setupHeaders, apiCall) { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext(); if (setupHeaders) { await context.setExtraHTTPHeaders(setupHeaders); } const page = await context.newPage(); await page.goto(airlineDomain); await page.waitForTimeout(5000); const result = await page.evaluate(apiCall); await browser.close(); return result; } // UNITED example: const unitedLoads = await queryAirline( 'https://www.united.com/en/us/flightstatus', null, async () => { const tokenResp = await fetch('/api/auth/anonymous-token'); const { data } = await tokenResp.json(); const token = data.token.hash; const resp = await fetch( '/api/flightstatus/upgradeListExtended?flightNumber=2238&flightDate=2026-04-08&fromAirportCode=EWR', { headers: { 'Accept': 'application/json', 'x-authorization-api': 'bearer ' + token } } ); return resp.json(); } ); // AMERICAN example: const aaLoads = await queryAirline( 'https://cdn.flyaa.aa.com/apiv2/mobile-flifo/flightSchedules/v1.0?origin=DFW&destination=IAH&departureDay=9&departureMonth=4&searchType=schedule&noOfFlightsToDisplay=20', { 'User-Agent': 'Android/2025.31 Pixel 7|14|1080|2400|1.0|AmericanAirlines', 'x-clientid': 'MOBILE', 'Accept': 'application/json', 'Device-ID': 'device-001' }, async () => { const resp = await fetch( 'https://cdn.flyaa.aa.com/api/mobile/loyalty/waitlist/v1.2?carrierCode=AA&flightNumber=2209&departureDate=2026-04-08&originAirportCode=DFW&destinationAirportCode=IAH', { headers: { 'Accept': 'application/json', 'x-referrer': 'fs' } } ); return resp.json(); } ); // KOREAN AIR example: const keStatus = await queryAirline( 'https://www.koreanair.com', null, async () => { const resp = await fetch('/api/fs/scheduleFlightSearch/flight/status/app', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'channel': 'app' }, body: JSON.stringify({ departureDate: '20260408', flightNumber: '017', searchOption: 'FLTNUM', departureLocationCode: '', arrivalLocationCode: '' }) }); return resp.json(); } ); ``` ### Spirit (plain HTTP, no Playwright): ```javascript const spiritStatus = await fetch('https://api.spirit.com/customermobileprod/2.8.0/v3/GetFlightInfoBI', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Ocp-Apim-Subscription-Key': 'c6567af50d544dfbb3bc5dd99c6bb177', 'Platform': 'Android' }, body: JSON.stringify({ departureStation: 'FLL', arrivalStation: 'ATL', departureDate: '2026-04-08' }) }).then(r => r.json()); ``` --- ## EMIRATES ### Base Setup ``` Domain: https://www.emirates.com Auth: None required for flight status Method: Plain HTTP (curl/fetch) — no Playwright needed ``` ### Step 1: Flight Status ``` GET https://www.emirates.com/service/flight-status?departureDate={YYYY-MM-DD}&flight={flightNumber} Headers: none required ``` **Response:** ```json { "results": [{ "airlineDesignator": "EK", "flightNumber": "0221", "flightId": "2026040700221DXB", "flightDate": "2026-04-07", "flightRoute": [{ "legNumber": "1", "originActualAirportCode": "DXB", "destinationActualAirportCode": "DFW", "originPlannedAirportCode": "DXB", "destinationPlannedAirportCode": "DFW", "statusCode": "ARVD", "flightPosition": 100, "totalTravelDuration": "17:30", "isIrregular": "false", "departureTime": { "schedule": "2026-04-08T01:10:00Z", "estimated": "2026-04-08T01:15:00Z", "actual": "2026-04-08T01:12:00Z" }, "arrivalTime": { "schedule": "2026-04-08T09:40:00Z", "estimated": "2026-04-08T09:34:00Z", "actual": "2026-04-08T09:32:00Z" }, "departureTerminal": "Terminal 3", "arrivalTerminal": "Terminal D", "flightOutageType": 0 }] }], "metaLinks": [] } ``` ### Step 2: Flight Load / Staff Standby (requires PNR) ``` Mobile API base: https://mobileapp.emirates.com/ GET /olci/v1/checkin/staffinformation/{pnr}/{lastName} ``` Returns `FlightLoadResponse` with: - `isStaffSubLoadTableAvl` - whether subload table is available - `staffPax.passengers[]` - staff passenger list with check-in status - `flights[]` - per-flight load data - Per passenger: `currentPriority`, `totalPriority`, `status`, `flightNumber` ### Notes - Flight status works from **plain curl** with zero auth — simplest of all airlines - Staff standby/flight load data requires PNR + last name (mobile app only) - The app has a full staff travel system: standby priority tracking, class downgrade acceptance, subload questionnaires - Internal backend leaked in response: `business-services-cache-bex-prod.dub.prd01.digitalattract.aws.emirates.prd` --- ## ALASKA AIRLINES ### Base Setup ``` Domain: https://www.alaskaair.com Mobile API: /1/guestservices/customermobile/ Auth: Requires booking confirmation code for load data Method: Playwright (website uses shadow DOM web components) ``` ### Step 1: Flight Status ``` GET /1/guestservices/customermobile/flights/status/{airlineCode}/{flightNumber}/{departureDate} Example: /1/guestservices/customermobile/flights/status/AS/1084/2026-04-08 ``` ### Step 2: Flight Status v2 (by route) ``` POST /1/guestservices/customermobile/mobileservices/reservation/flights/status Body: { "airlineCode": "AS", "flightNumber": "1084", "departureDate": "2026-04-08", "departureCityCode": "SEA", "arrivalCityCode": "LAX" } ``` Response includes `showPriorityList` boolean indicating if standby list is available for that flight. ### Step 3: Seat Availability / Remaining Seats (requires confirmation code) ``` POST /1/guestservices/customermobile/seats/SeatUpgradesByCabinRec/{confirmationCode} Body: { "adobeMarketingCloudVisitorID": "{visitor_id}" } ``` **Response:** ```json [ { "flightNumber": 1084, "origin": "SEA", "destination": "LAX", "cabinType": "First", "remainingSeats": 4, "upgradePrice": 149.00, "equipment": "Boeing 737-900ER" } ] ``` ### Notes - Flight status is public, but `remainingSeats` data requires a valid confirmation code - Mobile API paths start with `/1/guestservices/customermobile/` - Uses Ktor HTTP client (modern Kotlin) - Website uses deep shadow DOM -- Playwright automation is complex --- ## JETBLUE ### Base Setup ``` API Domain: https://az-api.jetblue.com Auth: API key (no login required) Method: Plain HTTP (curl/fetch) — no Playwright needed API Key: 49fc015f1ba44abf892d2b8961612378 ``` ### Step 1: Flight Status by Number ``` GET https://az-api.jetblue.com/flight-status/get-by-number?number={flightNumber}&date={YYYY-MM-DD} Headers: apikey: 49fc015f1ba44abf892d2b8961612378 Accept: application/json ``` **Response:** ```json { "flights": [{ "tripOrigin": "LAX", "tripDestination": "JFK", "isConnecting": false, "isThroughFlight": false, "legs": [{ "flightNo": "524", "flightStatus": "IN FLIGHT", "flightStatusGroup": "standardPostDeparture", "originAirport": "LAX", "originGate": "16", "originTerminal": "1", "actualDeparture": "2026-04-08T13:19:00-07:00", "scheduledDeparture": "2026-04-08T13:27:00-07:00", "doorCloseTime": "2026-04-08T13:12:00-07:00", "boardingTime": "2026-04-08T12:42:00-07:00", "destinationAirport": "JFK", "destinationGate": "518", "destinationTerminal": "5", "actualArrival": "2026-04-08T21:37:00-04:00", "scheduledArrival": "2026-04-08T21:55:00-04:00", "baggageClaim": "4", "equipmentType": "3NL", "tailNumber": "4074" }] }] } ``` ### Step 2: Priority List (Standby/Upgrade - requires check-in session) The app has `retrievePriorityList` which returns `PriorityListPassenger`: ```json { "shortLastName": "DOE", "shortFirstName": "J", "code": "SA", "order": 1, "hasSeat": false } ``` This requires an active check-in session (Cookie header). Accessible during check-in flow only. ### Step 3: Crystal Blue Seat Map ``` POST https://az-api.jetblue.com/mobile_seatmap Headers: Ocp-Apim-Subscription-Key: a5ee654e981b4577a58264fed9b1669c Content-Type: application/json ``` ### Notes - Flight status works from **plain curl** with just the API key - Priority list requires check-in session - Second APIM key `a5ee654e981b4577a58264fed9b1669c` used for seat map and logging --- ## FRONTIER AIRLINES APK not available for download from any third-party source. Frontier is a ULCC like Spirit -- minimal standby/upgrade features expected. Not yet analyzed. --- ## Rate Limits & Best Practices - **United**: Token expires in ~30min. Cache and refresh. No known rate limit. - **American**: No token needed. Akamai may throttle if too many requests from same browser session. Rotate browser contexts. - **Spirit**: APIM key is shared across all app users. No known rate limit but don't abuse. - **Korean Air**: No auth needed for status endpoints. `channel` header is required. - **All Playwright airlines**: Reuse browser context across multiple queries to avoid re-establishing sessions. Close and recreate if you get 403s.