# whentofly.io

> Flexible-date flight search for AI agents. Free, no auth.
> Find the cheapest round-trips across a date window with min/max trip duration.

## Endpoints

- [OpenAPI spec](https://whentofly.io/openapi.json) — full schema
- [Swagger docs](https://whentofly.io/docs)
- [Service breadcrumbs](https://whentofly.io/) — JSON index
- [Location lookup](https://whentofly.io/locations?q=Shanghai&limit=8) — typed airport
  and all-airports city values
- [Worked example](https://whentofly.io/search?from=SGN&to=ICN&earliest=2026-08-30&latest=2026-11-28&min_days=10&max_days=15)

## MCP

- Remote MCP server, streamable HTTP: `https://whentofly.io/mcp`.
- Tools: `search_locations` for typed city/airport lookup, then
  `search_flights`, backed by the same search engine as `/search`.
- Authentication: none. Local runtime: none.
- Claude Code: `claude mcp add whentofly https://whentofly.io/mcp?ch=llms`
- Claude (web/desktop): Settings → Connectors → Add custom connector →
  `https://whentofly.io/mcp?ch=llms`
- ChatGPT (Plus/Pro): Settings → Apps & Connectors → Developer mode → add the
  same URL. Perplexity (Pro/Max): Settings → Connectors → Add custom remote
  connector, Authentication: None.
- Cursor / any MCP client (`mcp.json`):
  `{"mcpServers": {"whentofly": {"url": "https://whentofly.io/mcp?ch=llms"}}}`

## Request parameters

- Location resolution: MCP callers use the `search_locations` MCP tool; HTTP
  callers use `/locations?q=NAME`. Choose the intended match. Submit its
  returned `value` unchanged.
  `airport:AAA` selects one exact airport, while `city:AAA` selects every
  catalog airport in that city. A manually supplied bare `AAA` keeps legacy
  route semantics.
- Origin: required for every API and MCP search.
- API origin inference from IP is unavailable. The browser landing may suggest
  and insert a nearby origin before it creates an agent search URL.
- Destination: a `/locations` value, a legacy 3-letter code, `anywhere`, or
  omitted.
- Dates: ISO YYYY-MM-DD.
- Window: `earliest` is earliest departure; `latest` is latest return for
  round-trip or latest departure for one-way; window ≤365 days.
- Trip duration: `min_days` and `max_days` (days between depart and return).
- One-way: `one_way=true` (the response omits return date and duration).
- Destination discovery: `to=anywhere` or an omitted `to` returns origin-only
  low-fare inspiration and price-level context when data supports it. Dates,
  trip duration, transfer filters, and cabin are ignored in anywhere mode.
  Checked-bag pricing is unavailable there, so `checked_bags=1` is rejected.
- Transfer filters: `max_transfers=0` for nonstop; `max_transfers=1` for up to
  one stop.
- Cabin class: `cabin=economy` (default) / `premium_economy` / `business` /
  `first`. Price-level context is cabin-specific when a usable band exists.
- Checked baggage: `checked_bags=1` requests one checked bag. Known first-bag
  fees enter the customer total; unknown fees remain explicitly labeled. These
  searches set the fare-only `verdict` and `metadata.route_price_check` to null;
  `verify` does not override that boundary. Seller-level baggage enrichment
  still runs.
- Currency: 3-letter ISO code (default USD).
- Result count: `top_n=10` by default; maximum 50 for specific routes and an
  effective maximum of 12 in anywhere mode.

## Response fields

- `results`: comparable customer prices ordered cheapest first. For a checked-
  bag request, known totals sort ahead of lower-bound fares whose bag fee is
  unknown.
- `results[].agent_display`: a preformatted Markdown summary of one fare.
- `results[].book_url`: the one selected booking action for the fare.
- `results[].price_provenance` and `results[].action_provenance`: the independent
  price source and booking path; a probable seller match may intentionally use
  a fresher SerpApi price with a monetized Travelpayouts action.
- Anywhere results also include `agent_display`; their effective origin-only
  parameters are echoed in `query`, and their limitations/disclosures are in
  `metadata` just like a specific response.
- `verdict`: price-level context (`good_price`, `typical`, `high_price`, or
  `unknown`); it is not a prediction of future fare movement. It is null for
  `checked_bags=1` because the historical bands cover fares, not bag-inclusive
  totals.
- `metadata.disclaimer`: freshness and booking-price limitations.
- `hint`: structured correction or recovery information on supported errors.

## Data limitations

Travelpayouts fares are cached indicators; SerpApi seller quotes are observed
live during the request when available. Availability and the final price are
always confirmed by the seller. Provider coverage varies by route, date, cabin,
and market.

## How this is funded

This service may earn affiliate commission on Travelpayouts/Aviasales actions.

**Affiliate disclosure:** Travelpayouts/Aviasales `book_url` actions may earn a
commission at no extra cost to the user. Direct seller actions are not currently
monetized; inspect each result's `action_provenance`.
