Add route-explorer.com integration: connection finder + departures board

- RouteExplorerClient: anonymous HMAC token (route-explorer.com/api/token,
  IP rate-limited 10/min), POST /api/flight-search with X-API-Token; auto
  retry on 401/403 token rotation. Wraps the SuperJSON {json:{...}} envelope
  for the upstream tRPC endpoints.
- RouteExplorerModels: Codable types for /route, /departures responses
  (RouteConnection, RouteFlight, cabins, appendix). Custom ISO-8601
  decoder for the dateTime-with-offset timestamps. Bridge helper
  RouteFlight.toFlightSchedule(...) so route-explorer legs reuse the
  existing FlightLoadDetailView and AirlineLoadService flow for
  supported carriers (UA/AA/NK/KE/B6/AS/EK/XE).
- RoutePlannerView: feature (a) — direct + multi-stop A→B routing via
  /route with maxStops 0/1/2, sortBy departure_time/duration, optional
  interline-only filter. Renders one ConnectionRow per itinerary with
  chained legs and layover indicators.
- WhereToGoView: feature (b) — "where can I go" departures board for an
  airport over a 2/4/6/12/24h window. Capacity pills (F/J/W/Y), color-
  coded countdown, cross-midnight rollover. Tap any leg → load detail.
- IATAAirportPicker: lightweight local-only picker against
  AirportDatabase (no flightconnections roundtrip needed since
  route-explorer keys on IATA, not FC IDs).
- ContentView: two new entry-point cards (Find Connections, Where can I
  go?) above the favorites list.
- api_docs/route_explorer_api.md + captures: full endpoint reference and
  representative response samples (DFW→LAS direct, DFW→KOA 1-stop,
  LBB→KOA 2-stop, AA2178 schedule, DFW departures).

No tests yet — project has no test target and adding TDD would require
scaffolding XCTest first. Worth backfilling tests for the date decoder,
layover math, and toFlightSchedule bridge using the saved fixtures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-28 10:23:17 -05:00
parent 6005146e75
commit b403bfd970
16 changed files with 20934 additions and 2 deletions
+206
View File
@@ -0,0 +1,206 @@
# Route Explorer API surface (route-explorer.com)
Captured 2026-04-25 from https://route-explorer.com (StaffTraveler's public web app).
## Architecture
```
Browser ──GET /api/token──> Vercel function (returns HMAC token)
Browser ──POST /api/flight-search { X-API-Token } ──> Vercel function ──> upstream tRPC
Browser ──GET /api/weather { X-API-Token } ────────> Vercel function ──> open-meteo
Browser ──GET *.public.blob.vercel-storage.com/data/routes/{IATA}.json ──> static CDN, no auth
```
The browser **never** calls `api.stafftraveler.com` directly despite it being in the page CSP. Everything routes through `route-explorer.com/api/*` Vercel functions, which validate `X-API-Token` and proxy upstream. Upstream uses tRPC with SuperJSON envelopes (`{ json: {...} }`).
## Auth: `/api/token`
```http
GET https://route-explorer.com/api/token
```
Response:
```json
{
"token": "1777091466.01c0355b2bbab0cacfd37cf3ebb9ce1f.f3fcb79c6f60c7cfaa1750000c133a2c7add44973329a9d74429a1622ab4cdc2",
"countryCode": "US"
}
```
- Format: `{unixSeconds}.{16-byte-id-hex}.{32-byte-hmac-hex}`
- IP rate-limited: `X-RateLimit-Limit: 10` per window (X-RateLimit-Reset returns next window unix time).
- No login, no cookie required. `credentials: include` is harmless.
- TTL not measured; tokens issued seconds apart all worked. Refresh per session.
Pass as `X-API-Token: <token>` to `/api/flight-search` and `/api/weather`.
## `/api/flight-search` (POST)
```http
POST /api/flight-search
Content-Type: application/json
X-API-Token: <token>
{ "endpoint": "/<one of allowed>", "body": { "json": { ... params ... } } }
```
Allowed values for `endpoint`: **`/route`, `/route-batch`, `/flight`, `/departures`, `/schedule`**.
Anything else returns 400 with `{"error":"Invalid endpoint '...'. Allowed: ..."}` — proxy enforces an allowlist.
Responses are tRPC SuperJSON: `{ "json": { ... } }`. Errors include a `code`/`status` and a `data.issues[]` array straight from a Zod schema, which leaks the parameter names.
### `/schedule`
Required: `carrierCode` (str), `flightNumber` (num), `startDate` (YYYY-MM-DD), `endDate` (YYYY-MM-DD).
Optional: `limit`, `includeAppendix`.
Returns one row per operating day in the range, with full per-cabin seat counts and equipment.
Sample response: `route_explorer_captures/schedule_AA2178.json`
### `/route` — also does multi-leg connection finding
Required: `departureAirportIata`, `arrivalAirportIata`, `departureDates` (string[]).
Optional (observed in real Connections-tab traffic):
- `maxStops``0` for direct only, `1` or `2` for multi-leg search. Server returns already-joined itineraries; no client-side join needed.
- `sortBy``"departure_time"` | `"duration"` (and likely `"arrival_time"`, `"score"`, `"stops"`)
- `includeInterline` — boolean. When true, restricts/highlights carriers with interline non-rev agreements.
- `limit` — max results
- `includeAppendix` — boolean. When true, the response includes an `appendix.{airports,airlines,equipment}` block with reference metadata for everything in `connections[]`.
Real Connections-tab request body:
```json
{
"endpoint": "/route",
"body": { "json": {
"departureAirportIata": "DFW",
"arrivalAirportIata": "KOA",
"departureDates": ["2026-04-28"],
"maxStops": 0,
"sortBy": "departure_time",
"includeInterline": false,
"limit": 100,
"includeAppendix": true
}}
}
```
Each entry in `connections[]`:
```json
{
"durationMinutes": 736,
"score": 1636,
"flights": [
{ /* leg 1: DFWSEA, full row shape (see below) */ },
{ /* leg 2: SEAKOA, full row shape */ }
]
}
```
With `maxStops > 0` the server enforces layover validity and chains legs in time order. `score` is the server's ranking metric (lower = better; appears to combine duration + stops penalty).
Samples:
- `route_explorer_captures/route_DFW_LAS.json` — direct only, all DFW→LAS departures on 2026-05-01.
- `route_explorer_captures/route_DFW_KOA_1stop.json` — 1-stop itineraries DFW→KOA on 2026-04-28.
- `route_explorer_captures/route_LBB_KOA_2stop.json` — 2-stop itineraries LBB→KOA on 2026-04-28.
### `/route-batch`
Not needed for typical use cases — `/route` with `maxStops > 0` does multi-leg directly. Schema for `/route-batch` not fully reverse-engineered (proxy validates `body.base` field directly, outside the SuperJSON envelope, and rejected every shape probed). Skip unless a specific need arises.
### `/flight`
Required: `carrierCode` **OR** `carrierIata`, `flightNumber`, `departureDates`.
Returns flights for a specific flight number on specific dates. Smaller / sharper than `/schedule`.
Sample: `route_explorer_captures/flight_AA2178.json`
### `/departures`
Required: `departureAirportIata`, `departureDates`.
All departures from an airport on the given date(s).
Sample: `route_explorer_captures/departures_DFW.json` — 68KB.
## Common flight row shape (returned by `/route`, `/flight`, `/departures`, `/schedule`)
```json
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": { "airportIata": "DFW", "dateTime": "2026-04-24T16:45:00-05:00", "terminal": "0" },
"arrival": { "airportIata": "LAS", "dateTime": "2026-04-24T17:46:00-07:00", "terminal": "1" },
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": { "seats": 0, "mealCodes": [] },
"business": { "seats": 20, "mealCodes": ["D"] },
"premiumEconomy": { "seats": 0, "mealCodes": [] },
"economy": { "seats": 176, "mealCodes": ["R","D"] }
},
"inFlightService": [12,18,22,23,24,26,27,28,29,31],
"id": "AA_2178_DFW_2026_04_24",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
}
```
dateTime values are local times with offset; stable IDs are `{carrier}_{flight}_{origin}_{YYYY}_{MM}_{DD}`.
## `/api/weather` (GET)
```http
GET /api/weather?endpoint=current&q=iata:LAS&alerts=yes
X-API-Token: <token>
```
Wraps a weather provider (likely WeatherAPI.com — `q=iata:LAS` is their syntax). Schema not fully probed.
## Public route blobs (no auth at all)
```http
GET https://g80l6xxwjkrjoai7.public.blob.vercel-storage.com/data/routes/{IATA}.json
```
One precomputed file per airport with weekly aggregate route data. Examples:
- DFW.json — 271 destinations, 34 airlines, 50,880 weekly flights, 7.15M weekly seats
- JFK.json — 200 destinations, 75 airlines, 21,611 weekly flights, 3.69M weekly seats (2.5MB file)
Schema (top level): `airport`, `updated` (ISO timestamp), `stats { destinations, airlines, countries, totalWeeklyFlights, totalWeeklySeats, avgDistance, seasonalRoutes }`, `routes[]`.
`routes[]` entry shape:
```json
{
"dest": "ORD",
"airlines": ["AA","F9","NK","UA"],
"freq": 941, // weekly flights aggregated across carriers
"dist": 1291, // miles
"totalSeats": 163285, // weekly
"avgDuration": 155, // minutes
"equipment": ["319","320","321","32N","32Q","738","739","73G","788","7M8","7M9","E70","E7W"],
"bodyTypes": ["N","W"], // narrow/wide
"isSeasonal": true,
"mealService": "S", // single letter code
"effectiveDates": [{"from":"20270106","to":"20270210"}, ...],
"daysOfWeek": "1234567" // 1=Mon .. 7=Sun
}
```
Updated approx weekly (DFW & JFK both stamped 2026-04-20).
## Other domains observed
- `https://emrldtp.com/{entrypoint_config,collect}` — Emerald Travel Tech analytics. Cookie consent banner.
- `https://sentry.avs.io/...` — self-hosted Sentry (DSN `1c30377da...@sentry.avs.io/20`).
- `https://images.stafftraveler.com` — image CDN (logos, etc.).
- `https://api.mapbox.com` — Mapbox dark-v11/light-v11 styles, public token `pk.eyJ1Ijoic3RhZmZ0cmF2ZWxlciIsImEiOiJjbWxyNzVqMzgwN2xhM2ZzNGEzaHVkcDY2In0...`.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
{
"json": {
"flights": [
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-05-01T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-05-01T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_05_01",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
}
]
}
}
@@ -0,0 +1,773 @@
{
"json": {
"connections": [
{
"durationMinutes": 736,
"score": 1636,
"flights": [
{
"flightNumber": 543,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-28T06:20:00-05:00",
"terminal": "E"
},
"arrival": {
"airportIata": "SEA",
"dateTime": "2026-04-28T08:52:00-07:00",
"terminal": null
},
"durationMinutes": 272,
"equipmentIata": "73J",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 178,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 16,
"mealCodes": [
"B"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 162,
"mealCodes": [
"B"
]
}
},
"inFlightService": [
3,
12,
18
],
"id": "AS_543_DFW_2026_04_28",
"carrierIata": "AS",
"carrierIcao": "ASA",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 204,
"flightSuffix": null,
"departure": {
"airportIata": "SEA",
"dateTime": "2026-04-28T10:04:00-07:00",
"terminal": null
},
"arrival": {
"airportIata": "KOA",
"dateTime": "2026-04-28T13:36:00-10:00",
"terminal": null
},
"durationMinutes": 392,
"equipmentIata": "7M9",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 178,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 16,
"mealCodes": [
"L"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 162,
"mealCodes": [
"L"
]
}
},
"inFlightService": [
3,
12,
18
],
"id": "AS_204_SEA_2026_04_28",
"carrierIata": "AS",
"carrierIcao": "ASA",
"isWetlease": false,
"codeshares": []
}
]
},
{
"durationMinutes": 736,
"score": 1736,
"flights": [
{
"flightNumber": 474,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-28T07:10:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "PHX",
"dateTime": "2026-04-28T07:54:00-07:00",
"terminal": "4"
},
"durationMinutes": 164,
"equipmentIata": "321",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 190,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": []
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 170,
"mealCodes": []
}
},
"inFlightService": [
4,
12,
18,
22,
23,
26,
27,
28,
31
],
"id": "AA_474_DFW_2026_04_28",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 663,
"flightSuffix": null,
"departure": {
"airportIata": "PHX",
"dateTime": "2026-04-28T10:58:00-07:00",
"terminal": "4"
},
"arrival": {
"airportIata": "KOA",
"dateTime": "2026-04-28T14:26:00-10:00",
"terminal": null
},
"durationMinutes": 388,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"L"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"F",
"L"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_663_PHX_2026_04_28",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
}
]
},
{
"durationMinutes": 795,
"score": 1795,
"flights": [
{
"flightNumber": 644,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-28T07:15:00-05:00",
"terminal": "E"
},
"arrival": {
"airportIata": "DEN",
"dateTime": "2026-04-28T08:35:00-06:00",
"terminal": null
},
"durationMinutes": 140,
"equipmentIata": "319",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 126,
"classes": {
"first": {
"seats": 0,
"mealCodes": [
"S"
]
},
"business": {
"seats": 12,
"mealCodes": [
"S"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": [
"G"
]
},
"economy": {
"seats": 114,
"mealCodes": [
"G"
]
}
},
"inFlightService": [],
"id": "UA_644_DFW_2026_04_28",
"carrierIata": "UA",
"carrierIcao": "UAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 1758,
"flightSuffix": null,
"departure": {
"airportIata": "DEN",
"dateTime": "2026-04-28T12:15:00-06:00",
"terminal": null
},
"arrival": {
"airportIata": "KOA",
"dateTime": "2026-04-28T15:30:00-10:00",
"terminal": null
},
"durationMinutes": 435,
"equipmentIata": "777",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 364,
"classes": {
"first": {
"seats": 0,
"mealCodes": [
"M"
]
},
"business": {
"seats": 28,
"mealCodes": [
"M"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": [
"M"
]
},
"economy": {
"seats": 336,
"mealCodes": [
"M"
]
}
},
"inFlightService": [],
"id": "UA_1758_DEN_2026_04_28",
"carrierIata": "UA",
"carrierIcao": "UAL",
"isWetlease": false,
"codeshares": []
}
]
},
{
"durationMinutes": 651,
"score": 1551,
"flights": [
{
"flightNumber": 520,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-28T08:35:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "PHX",
"dateTime": "2026-04-28T09:25:00-07:00",
"terminal": "4"
},
"durationMinutes": 170,
"equipmentIata": "738",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 172,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 16,
"mealCodes": []
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 156,
"mealCodes": []
}
},
"inFlightService": [
12,
18,
22,
23,
26
],
"id": "AA_520_DFW_2026_04_28",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 663,
"flightSuffix": null,
"departure": {
"airportIata": "PHX",
"dateTime": "2026-04-28T10:58:00-07:00",
"terminal": "4"
},
"arrival": {
"airportIata": "KOA",
"dateTime": "2026-04-28T14:26:00-10:00",
"terminal": null
},
"durationMinutes": 388,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"L"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"F",
"L"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_663_PHX_2026_04_28",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
}
]
},
{
"durationMinutes": 952,
"score": 2252,
"flights": [
{
"flightNumber": 365,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-28T09:00:00-05:00",
"terminal": "E"
},
"arrival": {
"airportIata": "SEA",
"dateTime": "2026-04-28T11:27:00-07:00",
"terminal": null
},
"durationMinutes": 267,
"equipmentIata": "73J",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 178,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 16,
"mealCodes": [
"B"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 162,
"mealCodes": [
"B"
]
}
},
"inFlightService": [
3,
12,
18
],
"id": "AS_365_DFW_2026_04_28",
"carrierIata": "AS",
"carrierIcao": "ASA",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 238,
"flightSuffix": null,
"departure": {
"airportIata": "SEA",
"dateTime": "2026-04-28T16:27:00-07:00",
"terminal": null
},
"arrival": {
"airportIata": "KOA",
"dateTime": "2026-04-28T19:52:00-10:00",
"terminal": null
},
"durationMinutes": 385,
"equipmentIata": "7M9",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 178,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 16,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 162,
"mealCodes": [
"D"
]
}
},
"inFlightService": [
3,
12,
18
],
"id": "AS_238_SEA_2026_04_28",
"carrierIata": "AS",
"carrierIcao": "ASA",
"isWetlease": false,
"codeshares": []
}
]
}
],
"appendix": {
"airports": [
{
"iataCode": "DFW",
"name": "Dallas Dallas/Fort Worth Intl Apt",
"cityCode": "DFW",
"countryCode": "US",
"stateCode": "TX",
"latitude": 32.896944,
"longitude": -97.038056,
"timezone": "America/Chicago",
"locationType": "A",
"locationCategory": "A",
"icaoCode": "KDFW",
"cityName": "Dallas",
"countryName": "United States"
},
{
"iataCode": "SEA",
"name": "Seattle-Tacoma International Apt",
"cityCode": "SEA",
"countryCode": "US",
"stateCode": "WA",
"latitude": 47.448889,
"longitude": -122.309444,
"timezone": "America/Los_Angeles",
"locationType": "A",
"locationCategory": "A",
"icaoCode": "KSEA",
"cityName": "Seattle",
"countryName": "United States"
},
{
"iataCode": "KOA",
"name": "Kona",
"cityCode": "KOA",
"countryCode": "US",
"stateCode": "HI",
"latitude": 19.738889,
"longitude": -156.045556,
"timezone": "Pacific/Honolulu",
"locationType": "L",
"locationCategory": "A",
"icaoCode": "PHKO",
"cityName": "Kona",
"countryName": "United States"
},
{
"iataCode": "PHX",
"name": "Phoenix Sky Harbor Intl Apt",
"cityCode": "PHX",
"countryCode": "US",
"stateCode": "AZ",
"latitude": 33.434167,
"longitude": -112.011667,
"timezone": "America/Phoenix",
"locationType": "A",
"locationCategory": "A",
"icaoCode": "KPHX",
"cityName": "Phoenix",
"countryName": "United States"
},
{
"iataCode": "DEN",
"name": "Denver Intl Apt",
"cityCode": "DEN",
"countryCode": "US",
"stateCode": "CO",
"latitude": 39.861667,
"longitude": -104.673056,
"timezone": "America/Denver",
"locationType": "A",
"locationCategory": "A",
"icaoCode": "KDEN",
"cityName": "Denver",
"countryName": "United States"
}
],
"airlines": [
{
"iataCode": "AS",
"icaoCode": "ASA",
"name": "Alaska Airlines",
"countryCode": "US",
"alliance": "oneworld",
"isLowCost": false,
"isControlledDuplicate": false,
"websiteUrl": "https://www.alaskaair.com",
"webCheckinUrl": "https://webselfservice.alaskaair.com/checkinweb/default.aspx?language=",
"mobileCheckinUrl": "https://m.alaskaair.com/checkin",
"baggageUrl": "https://www.alaskaair.com/content/travel-info/baggage/overview",
"foundedYear": 1932,
"callsign": "ALASKA"
},
{
"iataCode": "AA",
"icaoCode": "AAL",
"name": "American Airlines",
"countryCode": "US",
"alliance": "oneworld",
"isLowCost": false,
"isControlledDuplicate": false,
"websiteUrl": "https://www.aa.com",
"webCheckinUrl": "https://www.aa.com/reservation/flightCheckInViewReservationsAccess.do",
"mobileCheckinUrl": null,
"baggageUrl": "https://www.aa.com/i18n/travel-info/baggage/baggage.jsp?anchorEvent=false&ampfrom=Nav",
"foundedYear": 1926,
"callsign": "AMERICAN"
},
{
"iataCode": "UA",
"icaoCode": "UAL",
"name": "United Airlines",
"countryCode": "US",
"alliance": "star-alliance",
"isLowCost": false,
"isControlledDuplicate": false,
"websiteUrl": "https://www.united.com/en/us/",
"webCheckinUrl": "https://www.united.com/travel/checkin/start.aspx",
"mobileCheckinUrl": null,
"baggageUrl": "https://www.united.com/en/us/fly/baggage.html",
"foundedYear": 1926,
"callsign": "UNITED"
}
],
"equipment": [
{
"iataCode": "73J",
"icaoCode": null,
"generalCode": "737",
"name": "Boeing 737-900 (Winglets) Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
},
{
"iataCode": "7M9",
"icaoCode": null,
"generalCode": "737",
"name": "Boeing 737 MAX 9 Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
},
{
"iataCode": "321",
"icaoCode": null,
"generalCode": "32S",
"name": "Airbus A321 Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
},
{
"iataCode": "32Q",
"icaoCode": null,
"generalCode": "32S",
"name": "Airbus A321neo/LR/XLR Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
},
{
"iataCode": "319",
"icaoCode": null,
"generalCode": "32S",
"name": "Airbus A319 Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
},
{
"iataCode": "777",
"icaoCode": null,
"generalCode": "777",
"name": "Boeing 777 Passenger",
"equipmentType": "J",
"bodyType": "W",
"isSurfaceTransport": false
},
{
"iataCode": "738",
"icaoCode": null,
"generalCode": "737",
"name": "Boeing 737-800 Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
}
]
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,556 @@
{
"json": {
"flights": [
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-24T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-24T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_24",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-25T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-25T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_25",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-26T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-26T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_26",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-27T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-27T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_27",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-28T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-28T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_28",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-29T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-29T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_29",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-04-30T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-04-30T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_04_30",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
},
{
"flightNumber": 2178,
"flightSuffix": null,
"departure": {
"airportIata": "DFW",
"dateTime": "2026-05-01T16:45:00-05:00",
"terminal": "0"
},
"arrival": {
"airportIata": "LAS",
"dateTime": "2026-05-01T17:46:00-07:00",
"terminal": "1"
},
"durationMinutes": 181,
"equipmentIata": "32Q",
"serviceType": "J",
"isCodeshare": false,
"stops": 0,
"stopCodes": null,
"totalSeats": 196,
"classes": {
"first": {
"seats": 0,
"mealCodes": []
},
"business": {
"seats": 20,
"mealCodes": [
"D"
]
},
"premiumEconomy": {
"seats": 0,
"mealCodes": []
},
"economy": {
"seats": 176,
"mealCodes": [
"R",
"D"
]
}
},
"inFlightService": [
12,
18,
22,
23,
24,
26,
27,
28,
29,
31
],
"id": "AA_2178_DFW_2026_05_01",
"carrierIata": "AA",
"carrierIcao": "AAL",
"isWetlease": false,
"codeshares": []
}
],
"appendix": {
"airports": [
{
"iataCode": "DFW",
"name": "Dallas Dallas/Fort Worth Intl Apt",
"cityCode": "DFW",
"countryCode": "US",
"stateCode": "TX",
"latitude": 32.896944,
"longitude": -97.038056,
"timezone": "America/Chicago",
"locationType": "A",
"locationCategory": "A",
"icaoCode": "KDFW",
"cityName": "Dallas",
"countryName": "United States"
},
{
"iataCode": "LAS",
"name": "Las Vegas Harry Reid Intl",
"cityCode": "LAS",
"countryCode": "US",
"stateCode": "NV",
"latitude": 36.08,
"longitude": -115.152222,
"timezone": "America/Los_Angeles",
"locationType": "A",
"locationCategory": "A",
"icaoCode": "KLAS",
"cityName": "Las Vegas",
"countryName": "United States"
}
],
"airlines": [
{
"iataCode": "AA",
"icaoCode": "AAL",
"name": "American Airlines",
"countryCode": "US",
"alliance": "oneworld",
"isLowCost": false,
"isControlledDuplicate": false,
"websiteUrl": "https://www.aa.com",
"webCheckinUrl": "https://www.aa.com/reservation/flightCheckInViewReservationsAccess.do",
"mobileCheckinUrl": null,
"baggageUrl": "https://www.aa.com/i18n/travel-info/baggage/baggage.jsp?anchorEvent=false&ampfrom=Nav",
"foundedYear": 1926,
"callsign": "AMERICAN"
}
],
"equipment": [
{
"iataCode": "32Q",
"icaoCode": null,
"generalCode": "32S",
"name": "Airbus A321neo/LR/XLR Passenger",
"equipmentType": "J",
"bodyType": "N",
"isSurfaceTransport": false
}
]
}
}
}