# air-routes.com API

Scheduled passenger **routes operating now**, with seasonality labels.
**Not** fares, **not** booking checkout, **not** date-specific schedules.
Provenance dumps and snapshot names are **not** part of the public contract.

Base URL: `https://air-routes.com` (local: `http://127.0.0.1:8422`)

All responses are JSON. No API key required.

| REST | MCP tool |
|------|----------|
| `GET /api/airports` | `resolve_airport` |
| `GET /api/connections` | `find_connections` |
| `GET /api/airport/{airport_code}/destinations` | `airport_destinations` |
| `GET /api/route/{route_id}` | `route_detail` |

MCP endpoint: `https://air-routes.com/mcp`

Also: [HTML](/developers) · [OpenAPI](/api/openapi.json) · [llms.txt](/llms.txt)

---

## 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]` is the 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 a route detail (`/api/route/{route_id}`) — one direct
flight from the queried origin. Use this endpoint to pull an entire hub’s
schedule in one request instead of N× route detail.

| 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` included | `[{"airline_code":"FR","airline":"Ryanair",…}]` |
| `booking` | 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` |

---

## 1. Resolve airports

`GET /api/airports` · MCP `resolve_airport`

| Input | Type | Required | Notes | Example |
|-------|------|----------|-------|---------|
| `q` | string | yes | city, airport name, or airport code; min length 1; max **15** matches | `"london"` |

**Response:** `{ "airports": Airport[] }`

```bash
curl 'https://air-routes.com/api/airports?q=london'
```

```json
{
  "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 (uppercased server-side) | `"CLJ"` |
| `to` | string | yes | — | destination airport code | `"AMS"` |
| `max_stops` | integer | no | 2 | 0…3 inclusive | `1` |
| `max_flying_min` | integer | no | 1440 | must be > 0 | `1440` |

Returns the fewest-stops tier that has any option — direct flights if any
exist, otherwise one-stop, otherwise two-stop (up to `max_stops`), **never a
mix**. Within that tier, options sort by `total_flying_min` (sum of leg block
times; no layover math). Layover tiers return at most 7 options; the direct
tier is not capped. Dropped routes are invisible.
Unknown airport → 404 with a hint to use `/api/airports`.
Airport names and coordinates are **not** embedded here — resolve airport codes with `GET /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, … }]` |

Public responses do **not** include a snapshot/dataset name.

```bash
curl 'https://air-routes.com/api/connections?from=CLJ&to=AMS&max_stops=1'
```

```json
{
  "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. Airport 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 destination is full public route detail (airlines
with `fly_days` / `dep_times`, `flying_min`, booking URL) so agents can plan from
one hub without N× `/api/route` calls.

```bash
curl 'https://air-routes.com/api/airport/OTP/destinations'
```

```json
{
  "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}`; uppercased; 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/`sources`.

```bash
curl 'https://air-routes.com/api/route/OTP-PMO'
```

```json
{
  "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" }
}
```
