Public API
Scheduled passenger routes operating now, with seasonality labels. Not fares, not booking checkout, not date-specific schedules. Public JSON does not include evidence lists or snapshot names.
Shared types
Airport codes: fields like airport_code, from, and to use the familiar 3-letter airport code (officially the IATA code — most travelers never need that name).
Airport
| Field | Type | Notes | Example |
|---|---|---|---|
airport_code | string | 3-letter airport code (IATA), uppercase | "LHR" |
name | string | airport name | "London Heathrow Airport" |
city | string | city name | "London" |
country | string | as stored in the snapshot | "United Kingdom" |
lat | number|null | WGS84 | 51.47 |
lon | number|null | WGS84 | -0.46 |
importance | integer | 0–5 hub weight | 5 |
Booking
| Field | Type | Notes | Example |
|---|---|---|---|
url | string | canonical route page | "https://air-routes.com/r/OTP-BCN" |
Airline
Used on connection legs and on route detail (one shape everywhere).
| Field | Type | Notes | Example |
|---|---|---|---|
airline_code | string | 2-letter airline code (IATA) | "W6" |
airline | string | airline name | "Wizz Air" |
fly_days | string[] | days of week the flight operates; subset of Mon…Sun | ["Wed", "Sat"] |
dep_times | string[] | local HH:MM aligned with fly_days (dep_times[i] = departure on fly_days[i]); same length; empty when unknown | ["09:40", "18:15"] |
seasonal_note | string|null | free-text seasonal caveat when present | null |
Leg
| Field | Type | Notes | Example |
|---|---|---|---|
route_id | string | FROM-TO | "CLJ-IST" |
from | string | airport code | "CLJ" |
to | string | airport code | "IST" |
flying_min | integer | estimated block minutes | 105 |
seasonality_label | string|null | human seasonality label | "ends October" |
airlines | Airline[] | carriers on this leg | [{"airline_code":"TK","airline":"Turkish Airlines",…}] |
booking | Booking | canonical page for this leg | {"url":"https://…/r/CLJ-IST"} |
Option
| Field | Type | Notes | Example |
|---|---|---|---|
total_flying_min | integer | sum of leg times (no layover math) | 425 |
stops | integer | legs.length - 1 | 1 |
legs | Leg[] | ordered origin → destination | [{…},{…}] |
Destination
Same public fields as route detail — one direct flight from the queried origin
(bulk hub schedule; prefer over N× /api/route).
| Field | Type | Notes | Example |
|---|---|---|---|
route_id | string | from-to | "OTP-PMO" |
from | string | origin airport code | "OTP" |
to | string | destination airport code | "PMO" |
status | string | lifecycle status | "active" |
seasonality_label | string|null | "ends October" | |
distance_km | number|null | great-circle km | 1284.5 |
flying_min | integer | 150 | |
airlines | Airline[] | fly_days + dep_times | [{"airline_code":"FR","airline":"Ryanair",…}] |
booking | Booking | {"url":"https://air-routes.com/r/OTP-PMO"} |
1. Resolve airports
GET /api/airports
· MCP resolve_airport
| Input | Type | Required | Notes | Example |
|---|---|---|---|---|
q | string | yes | city, name, or airport code; min length 1; max 15 matches | "london" |
Response: { "airports": Airport[] }
curl 'https://air-routes.com/api/airports?q=london'
{
"airports": [
{
"airport_code": "LHR",
"name": "London Heathrow Airport",
"city": "London",
"country": "United Kingdom",
"lat": 51.47,
"lon": -0.46,
"importance": 5
},
{
"airport_code": "LGW",
"name": "London Gatwick Airport",
"city": "London",
"country": "United Kingdom",
"lat": 51.15,
"lon": -0.19,
"importance": 4
}
]
}
2. Find connections
GET /api/connections
· MCP find_connections
| Input | Type | Required | Default | Notes | Example |
|---|---|---|---|---|---|
from | string | yes | — | origin airport code | "CLJ" |
to | string | yes | — | destination airport code | "AMS" |
max_stops | integer | no | 2 | 0…3 | 1 |
max_flying_min | integer | no | 1440 | must be > 0 | 1440 |
Returns the fewest-stops tier that has any option — direct if available, else
one-stop, else two-stop (up to max_stops), never a mix. Within that tier,
options sort by total_flying_min (no layover math); layover tiers return at
most 7 options, the direct tier is uncapped. Unknown airport → 404.
Airport names and coordinates are not embedded — resolve airport codes with /api/airports?q=….
| Response field | Type | Notes | Example |
|---|---|---|---|
from | string | echoed airport code | "CLJ" |
to | string | echoed airport code | "AMS" |
options | Option[] | may be empty | [{ "total_flying_min": 425, … }] |
curl 'https://air-routes.com/api/connections?from=CLJ&to=AMS&max_stops=1'
{
"from": "CLJ",
"to": "AMS",
"options": [
{
"total_flying_min": 425,
"stops": 1,
"legs": [
{
"route_id": "CLJ-IST",
"from": "CLJ",
"to": "IST",
"flying_min": 105,
"seasonality_label": null,
"airlines": [
{
"airline_code": "TK",
"airline": "Turkish Airlines",
"fly_days": ["Mon", "Thu", "Sat"],
"dep_times": ["07:10", "14:15", "07:10"],
"seasonal_note": null
}
],
"booking": { "url": "https://air-routes.com/r/CLJ-IST" }
},
{
"route_id": "IST-AMS",
"from": "IST",
"to": "AMS",
"flying_min": 230,
"seasonality_label": null,
"airlines": [
{
"airline_code": "TK",
"airline": "Turkish Airlines",
"fly_days": ["Mon", "Thu", "Sat"],
"dep_times": ["07:10", "14:15", "07:10"],
"seasonal_note": null
}
],
"booking": { "url": "https://air-routes.com/r/IST-AMS" }
}
]
}
]
}
3. Destinations (direct only)
GET /api/airport/{airport_code}/destinations
· MCP airport_destinations
| Input | Type | Required | Notes | Example |
|---|---|---|---|---|
airport_code | string | yes | path; 3-letter airport code (IATA); uppercased; unknown → 404 | "OTP" |
Response: { "from": string, "destinations": Destination[] }
Dropped routes excluded. Each row is full public route detail (airlines with
fly_days / dep_times, flying_min, booking) — one hub call instead of N×
/api/route.
curl 'https://air-routes.com/api/airport/OTP/destinations'
{
"from": "OTP",
"destinations": [
{
"route_id": "OTP-BCN",
"from": "OTP",
"to": "BCN",
"status": "active",
"seasonality_label": null,
"distance_km": 1970.0,
"flying_min": 195,
"airlines": [
{
"airline_code": "W6",
"airline": "Wizz Air",
"fly_days": ["Mon", "Wed", "Fri"],
"dep_times": ["06:15", "12:40", "06:15"],
"seasonal_note": null
}
],
"booking": { "url": "https://air-routes.com/r/OTP-BCN" }
},
{
"route_id": "OTP-PMO",
"from": "OTP",
"to": "PMO",
"status": "active",
"seasonality_label": "ends October",
"distance_km": 1284.5,
"flying_min": 150,
"airlines": [
{
"airline_code": "FR",
"airline": "Ryanair",
"fly_days": ["Wed", "Sat"],
"dep_times": ["09:40", "18:15"],
"seasonal_note": null
}
],
"booking": { "url": "https://air-routes.com/r/OTP-PMO" }
}
]
}
4. Route detail
GET /api/route/{route_id}
· MCP route_detail
| Input | Type | Required | Notes | Example |
|---|---|---|---|---|
route_id | string | yes | FROM-TO; unknown → 404 | "OTP-PMO" |
| Field | Type | Notes | Example |
|---|---|---|---|
route_id | string | "OTP-PMO" | |
from | string | airport code | "OTP" |
to | string | airport code | "PMO" |
status | string | lifecycle status | "active" |
seasonality_label | string|null | "ends October" | |
distance_km | number|null | great-circle km | 1284.5 |
flying_min | integer | 150 | |
airlines | Airline[] | [{"airline_code":"FR","airline":"Ryanair",…}] | |
booking | Booking | {"url":"https://air-routes.com/r/OTP-PMO"} |
Public responses do not include evidence lists.
curl 'https://air-routes.com/api/route/OTP-PMO'
{
"route_id": "OTP-PMO",
"from": "OTP",
"to": "PMO",
"status": "active",
"seasonality_label": "ends October",
"distance_km": 1284.5,
"flying_min": 150,
"airlines": [
{
"airline_code": "FR",
"airline": "Ryanair",
"fly_days": ["Wed", "Sat"],
"dep_times": ["09:40", "18:15"],
"seasonal_note": null
}
],
"booking": { "url": "https://air-routes.com/r/OTP-PMO" }
}
Errors
| Status | Body | When | Example |
|---|---|---|---|
| 404 | {"detail": string} | unknown airport or route | {"detail":"unknown airport XXX; try /api/airports?q=…"} |
| 422 | validation error | missing/invalid query params | missing from |
| 429 | {"detail": string} + Retry-After | only if limits are re-enabled later | Retry-After: 12 |