# 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: ` to `/api/flight-search` and `/api/weather`. ## `/api/flight-search` (POST) ```http POST /api/flight-search Content-Type: application/json X-API-Token: { "endpoint": "/", "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: DFW→SEA, full row shape (see below) */ }, { /* leg 2: SEA→KOA, 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: ``` 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...`.