# AviaSeek - full description for AI agents (llms-full.txt) > AviaSeek is a global flight metasearch engine for independent travellers. It aggregates prices from airlines and agencies, compares options and shows the best flights. > The site implements WebMCP (Model Context Protocol for the Web): in supporting browsers (Chrome Origin Trial, HTTPS) tools are available via `document.modelContext`. ## 0.0. Catalog data (live) 873 cities, 916 airports, 61 airlines, 439 routes in the catalog, 15 help articles. - **`searchFlights` contract version:** `1.2.0` - **Markdown page versions:** any page gets a `.md` suffix (`/index.md`, `/cities.md`, `/cities/moskva-mow.md`, `/routes/moskva-mow/sankt-peterburg-led.md`) - the same data without HTML markup. - **Serving bots:** AI crawlers receive server-side HTML with headings, text and JSON-LD (WebSite/WebPage/City/Airport/Airline/FAQPage/BreadcrumbList/ItemList). ## 0. Server-side MCP endpoint (for cloud agents) AviaSeek provides a **real server-side MCP server** over the Model Context Protocol (Streamable HTTP) - available to cloud agents (DeepSeek, Claude, ChatGPT, Cursor) directly, without a browser. - **URL:** `https://aviaseek.com/en/mcp` - **Transport:** Streamable HTTP (JSON-RPC 2.0), `POST` with the header `Accept: application/json, text/event-stream`. - **Methods:** `initialize`, `tools/list`, `tools/call`. - **Tool:** `searchFlights` (schema below, section 1). - **Discovery:** `https://aviaseek.com/en/.well-known/mcp.json`. - **Access:** public, rate limit up to 30 requests/min per IP (`MCP_RATE_LIMIT_PER_MIN`). Example `tools/call`: ```json {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"searchFlights","arguments":{"origin":"MOW","destination":"LED","departure_date":"2026-09-05"}}} ``` ## 0.5. Alice skill (voice) AviaSeek is available as a skill for Yandex Alice: "Alice, ask AviaSeek to find flights from Moscow to St. Petersburg for tomorrow". - **Webhook:** `https://aviaseek.com/en/alice` (POST, Yandex Dialogs protocol; response limit 4.5 s). - **The search is asynchronous (split-turn):** "find flights from X to Y for a date" starts a search with the aggregator; it continues on the phrase/button "check the result" (state in `application_state`). - **Output:** speaker-only devices - top-3 read aloud (price, airline, time); screen devices - an `ItemsList` card (up to 5 options), tapping an option opens the detailed ticket page. - **City/date recognition:** Yandex entities (`YANDEX.GEO`, `YANDEX.DATETIME`) + a custom parser and the AviaSeek city catalog. ## 1. WebMCP tools ### searchFlights Flight search by route and dates. Returns a list of flights with prices, airlines, travel time, baggage conditions and a booking link. **Description for agents:** search Moscow->Saint Petersburg flights on 05.09, round trip, 2 adults, economy. Returns options with prices, airlines and booking links via AviaSeek. ## 1.1. Output contract and filtering (searchFlights, v1.1) ### Output parameters (besides the required origin/destination/departure_date) | Parameter | Values | Default | What it does | |---|---|---|---| | `limit` | 1..100 | 20 | how many options to return (after filtering and sorting) | | `offset` | >= 0 | 0 | next options (pagination) | | `sort` | `price_asc`, `duration_asc`, `departure_asc`, `arrival_asc`, `transfers_asc`, `recommended` | `price_asc` | output order | | `filters.direct_only` | boolean | false | direct flights only | | `filters.max_transfers` | 0..3 | - | no more than N stops | | `filters.transfers` | `[0]`, `[0,1]` | - | exact stop sets (like the checkboxes on the site) | | `filters.max_price` | number (RUB) | - | up to N RUB | | `filters.max_duration_min` | minutes | - | maximum duration | | `filters.airlines` | codes, e.g. `["DP"]` | - | at least one of the airlines in the ticket | | `filters.with_baggage` / `with_handbag` / `with_change` / `with_return` | boolean | false | baggage / change / return requirements | | `filters.departure_periods` / `arrival_periods` | `early_morning` 00-06, `morning` 06-12, `afternoon` 12-18, `evening` 18-24 | - | periods of the day (local time) | Rules: 1. Order of operations: **filters -> sorting -> limit/offset**. So "direct only" options are always found, even if they are not among the first 20 by price. 2. Between different filters - AND logic; inside an array (airlines, periods) - OR. 3. A ticket without a price is dropped with `max_price`, a ticket without a time - with the periods-of-day filter. 4. Response: `total` (found in total), `total_after_filters`, `results_returned`, `has_more`, `next_offset`, `hint`, `view_all_url`, `cheapest`, `results[]`. 5. Empty result: `total_after_filters: 0` + `hint` (what to relax). Tell the `hint` to the user and offer to change the condition. 6. Filters do not start a new search: pass the same `search_id` to narrow/scroll through what was already found. ### How to show the result to the user (required) Each option contains ready-made fields so the agent does not have to assemble a line from 15 JSON fields: - `display_markdown` - a ready markdown line: `**Pobeda · DP210** · MOW → LED · 07:30 → 09:05 · direct · 1 h 35 min · **3 179 RUB** · [Buy for 3 179 RUB](https://aviaseek.com/en/r/...)`; - `title` - "Pobeda · DP210 · MOW → LED · 07:30 → 09:05 · direct"; - `summary` - "3 179 RUB · 1 h 35 min · direct · hand baggage 10 kg, checked baggage 23 kg"; - `link_text` - "Buy for 3 179 RUB"; - `booking_url` - the booking link (this domain only, ~15 minutes of life). Requirements for the agent's answer: - 3-5 options as separate list items, with time, stops, duration, price and a clickable link; - do not answer with a price without a link; - do not invent options and prices that are not in `results`; - with `is_complete: false` warn that prices are preliminary and call again with the same `search_id`. ### Example Request: "find direct tickets Moscow - St. Petersburg on 26 September under 8000 RUB". Arguments: `{"origin":"MOW","destination":"LED","departure_date":"2026-09-26","filters":{"direct_only":true,"max_price":8000},"sort":"price_asc","limit":20}`. Response: `total_after_filters` (how many direct options match), `results[]` 20 per page, `has_more`/`next_offset` to continue, `view_all_url` to buy on the site. ### searchFlights (contract v1.2.0): inputSchema (JSON Schema) ```json { "type": "object", "properties": { "origin": { "type": "string", "pattern": "^[A-Z]{3}$", "description": "IATA code of the departure city or airport. Examples: MOW, LED, PAR, STR." }, "destination": { "type": "string", "pattern": "^[A-Z]{3}$", "description": "IATA code of the arrival city or airport. Examples: LED, SVX, BCN." }, "departure_date": { "type": "string", "format": "date", "description": "Departure date in YYYY-MM-DD format. Not earlier than today and not more than a year ahead: the search service does not return prices beyond a year and rejects such dates." }, "return_date": { "type": "string", "format": "date", "description": "Return date YYYY-MM-DD (optional). Without it - a one-way search. Same limit as the departure date: not more than a year ahead." }, "adults": { "type": "integer", "minimum": 1, "maximum": 9, "default": 1, "description": "Number of adult passengers." }, "children": { "type": "integer", "minimum": 0, "maximum": 8, "default": 0, "description": "Number of children (2-11 years)." }, "infants": { "type": "integer", "minimum": 0, "maximum": 8, "default": 0, "description": "Number of infants (under 2 years)." }, "cabin": { "type": "string", "enum": [ "economy", "business", "first" ], "default": "economy", "description": "Service class. Only economy/business/first are available." }, "search_id": { "type": "string", "description": "Optional. Pass the search_id from a previous response (status=partial/pending, is_complete=false) to load more of the same search or apply other filters without starting anew." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20, "description": "How many options to return AFTER filtering and sorting. Default 20, maximum 100. Do not raise it without an explicit user request." }, "offset": { "type": "integer", "minimum": 0, "default": 0, "description": "Offset for pagination: 20 - return the next options after the first 20." }, "sort": { "type": "string", "enum": [ "price_asc", "duration_asc", "departure_asc", "arrival_asc", "transfers_asc", "recommended" ], "default": "price_asc", "description": "Sorting: price_asc - cheapest first (default); duration_asc - shortest travel; departure_asc - earliest departure; arrival_asc - earliest arrival; transfers_asc - fewest stops; recommended - the AviaSeek site order." }, "filters": { "type": "object", "additionalProperties": false, "description": "Output filters (like the filter panel on the AviaSeek site). Between different filters - AND logic, inside an array - OR. Applied BEFORE sorting and limit. The user asks for \"direct only\" - pass direct_only; \"under 10,000 RUB\" - max_price; \"in the morning\" - departure_periods; \"no more than one stop\" - max_transfers.", "properties": { "direct_only": { "type": "boolean", "default": false, "description": "true - direct flights only (no stops). Equivalent to max_transfers=0." }, "max_transfers": { "type": "integer", "minimum": 0, "maximum": 3, "description": "No more than N stops: 0 - direct only, 1 - direct and one stop." }, "transfers": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 3 }, "description": "Exact stop counts, like the site filter: [0] - direct only, [0,1] - direct and one stop." }, "max_price": { "type": "number", "minimum": 0, "description": "Maximum ticket price in RUB." }, "max_duration_min": { "type": "integer", "minimum": 0, "description": "Maximum flight duration in minutes." }, "airlines": { "type": "array", "items": { "type": "string", "pattern": "^[A-Z0-9]{2}$" }, "description": "Airline IATA codes (e.g. DP, SU). A ticket matches if at least one of them is in the itinerary." }, "with_baggage": { "type": "boolean", "description": "Only tickets with included checked baggage." }, "with_handbag": { "type": "boolean", "description": "Only tickets with included hand baggage." }, "with_change": { "type": "boolean", "description": "Only tickets that can be changed." }, "with_return": { "type": "boolean", "description": "Only tickets that can be refunded." }, "departure_periods": { "type": "array", "items": { "type": "string", "enum": [ "early_morning", "morning", "afternoon", "evening" ] }, "description": "Departure periods of the day: early_morning 00:00-06:00, morning 06:00-12:00, afternoon 12:00-18:00, evening 18:00-24:00 (local time)." }, "arrival_periods": { "type": "array", "items": { "type": "string", "enum": [ "early_morning", "morning", "afternoon", "evening" ] }, "description": "Arrival periods of the day (same boundaries as for departure)." } } } }, "required": [ "origin", "destination", "departure_date" ] } ``` ### searchFlights (contract v1.2.0): outputSchema (JSON Schema) ```json { "type": "object", "description": "Flight search result. Show the user 3-5 options as a list: each with airline and flight number, departure and arrival time, number of stops, duration, price with currency and a clickable purchase link. Do not answer with a price without a link. Do not invent options that are not in results.", "properties": { "status": { "type": "string", "enum": [ "success", "partial", "pending", "error" ], "description": "success - the result is complete; partial - not all found, call again with the same search_id; pending - no results yet, retry in a few seconds; error - execution error." }, "search_id": { "type": "string", "description": "Search session identifier. Pass it in the next call to load more results or apply other filters without a new search." }, "is_complete": { "type": "boolean", "description": "true - final complete result; false - it is worth repeating the call with the same search_id." }, "direction": { "type": "object", "properties": { "origin": { "type": "string" }, "destination": { "type": "string" }, "date": { "type": "string" }, "return_date": { "type": [ "string", "null" ] } } }, "passengers": { "type": "object", "properties": { "adults": { "type": "integer" }, "children": { "type": "integer" }, "infants": { "type": "integer" } } }, "cabin": { "type": "string" }, "limit": { "type": "integer", "description": "Page size applied to the output." }, "offset": { "type": "integer", "description": "Applied offset." }, "sort": { "type": "string", "description": "Applied sorting." }, "filters_applied": { "type": [ "object", "null" ], "description": "Normalized filters actually applied to the output (echo of the request)." }, "total": { "type": "integer", "description": "Total number of tickets found (before filtering)." }, "total_after_filters": { "type": "integer", "description": "Number of tickets remaining after filtering." }, "results_returned": { "type": "integer", "description": "Number of options in this response (= results.length)." }, "has_more": { "type": "boolean", "description": "true - there are more options after this page (request offset = next_offset)." }, "next_offset": { "type": [ "integer", "null" ], "description": "Offset value for the next page, or null if there are no more options." }, "hint": { "type": [ "string", "null" ], "description": "Hint for an empty result: what to relax. If hint is not null, pass it to the user and offer to change the condition, rather than answering \"no tickets\"." }, "view_all_url": { "type": "string", "description": "Link to the results page on the AviaSeek site. Show it to the user so they can view all options and buy." }, "cheapest": { "type": [ "object", "null" ], "description": "The cheapest option among the filtered ones (for a short summary).", "properties": { "price": { "type": "number" }, "currency": { "type": "string" }, "airline_name": { "type": "string" }, "flight_number": { "type": "string" } } }, "error": { "type": [ "string", "null" ], "description": "Error text if status=error." }, "results": { "type": "array", "description": "Flight options after filtering, sorting and slicing. Show each option as a separate item using display_markdown - it already contains a ready line with the price and purchase link.", "items": { "type": "object", "properties": { "id": { "type": "string" }, "airline_code": { "type": "string" }, "airline_name": { "type": "string" }, "flight_number": { "type": "string" }, "origin": { "type": "string" }, "destination": { "type": "string" }, "departure_at": { "type": [ "string", "null" ] }, "arrival_at": { "type": [ "string", "null" ] }, "price": { "type": [ "number", "null" ], "description": "Price in the currency field. Pass it as is, do not convert or round." }, "currency": { "type": "string" }, "transfers": { "type": "integer" }, "duration_minutes": { "type": [ "number", "null" ] }, "is_round_trip": { "type": "boolean" }, "return_departure_at": { "type": [ "string", "null" ] }, "return_arrival_at": { "type": [ "string", "null" ] }, "baggage": { "type": "object", "properties": { "handbag": { "type": [ "integer", "null" ] }, "baggage": { "type": [ "integer", "null" ] }, "change": { "type": [ "boolean", "null" ] }, "return": { "type": [ "boolean", "null" ] } } }, "booking_url": { "type": "string", "description": "Purchase link via AviaSeek. You MUST show it to the user as a clickable link of the form: [Buy for 3179 RUB](booking_url). The link is valid for a limited time (~15 minutes) - open it immediately." }, "agent_name": { "type": [ "string", "null" ], "description": "Name of the seller (agent) the ticket is bought from. Do not confuse the seller with the airline: the ticket is often sold by an agency. null - the reference did not arrive, name the seller neutrally." }, "title": { "type": "string", "description": "Ready option title: airline, flight number, route, time, stops." }, "summary": { "type": "string", "description": "Ready short line: price, duration, stops, baggage." }, "link_text": { "type": "string", "description": "Ready purchase link text, e.g. \"Buy for 3179 RUB\"." }, "display_markdown": { "type": "string", "description": "Ready markdown line of the option with the price and purchase link. Use it as is." } }, "required": [ "id", "airline_name", "flight_number", "origin", "destination", "price", "currency", "transfers", "booking_url", "title", "summary", "link_text", "display_markdown" ] } } }, "required": [ "status", "search_id", "is_complete", "direction", "passengers", "cabin", "limit", "offset", "sort", "total", "total_after_filters", "results_returned", "has_more", "results" ] } ``` > **Note on is_complete=false:** a search with the aggregator takes time. If `status=partial` or `pending`, call again with the same `search_id` after a few seconds to load the rest. > **booking_url** is a link to the purchase page; it is valid for a limited time (~15 minutes). Open it immediately, do not wait. #### Example call ```js // The date is within the search window: not earlier than today and not more than a year ahead (no prices beyond a year). const departureDate = new Date(Date.now() + 30 * 86400000).toISOString().slice(0, 10); await document.modelContext.executeTool('searchFlights', { origin: 'MOW', destination: 'LED', departure_date: departureDate, adults: 1, cabin: 'economy' }); ``` ## 1.9. Search without MCP: URL search, HTML form and HTTP API If the agent has no MCP client (for example a browser agent or a chat without MCP support), the search runs through ordinary links - no JavaScript required: 1. **Readable URL search (recommended):** `https://aviaseek.com/en/search?from=MOW&to=LED&date=tomorrow` -> a `302` response to `https://aviaseek.com/en/results?flightSearch=MOW0920LEDy100`, where prices and purchase are shown. | Parameter | Values | |---|---| | `from` (aliases: `origin`, `departure`) | IATA city or airport code, slug (`moskva-mow`), name ("Moscow") | | `to` (aliases: `destination`, `arrival`, `where`) | same | | `date` (aliases: `departure_date`, `departure_at`, `when`) | `YYYY-MM-DD`, `DD.MM.YYYY`, `MMDD`, `today`, `tomorrow`; not earlier than today and not more than a year ahead | | `return` (aliases: `return_date`, `back`) | same (for round trips); same window - not more than a year ahead | | `adults` (alias: `passengers`, `pax`) / `children` / `infants` | numbers | | `cabin` (aliases: `class`, `trip_class`) | `economy`, `business`, `first` | Without a date (`/search?from=MOW&to=LED`) -> a `302` to the route page with a price calendar. Without parameters -> a `200` help page with an ordinary `GET /search` HTML form that works without JavaScript (useful for both people and agents that can only send requests). 4. **JSON for agents without MCP - one request, ready booking links (recommended):** `GET https://aviaseek.com/en/api/flights?from=MOW&to=LED&date=tomorrow&limit=5` `200` response: ```json { "status": "success", "is_complete": true, "search_id": "…", "origin": "MOW", "destination": "LED", "departure_date": "2026-09-20", "total": 128, "total_after_filters": 128, "results_returned": 5, "has_more": true, "next_offset": 5, "cheapest": { "price": 3133, "currency": "RUB", "airline_name": "Rossiya", "flight_number": "FV…" }, "view_all_url": "https://aviaseek.com/en/results?flightSearch=MOW0920LEDy100", "results": [ { "airline_name": "Rossiya", "flight_number": "FV…", "departure_at": "…", "arrival_at": "…", "transfers": 0, "duration_minutes": 85, "price": 3133, "currency": "RUB", "title": "Rossiya · FV… · MOW → LED · 14:00 → 15:25 · direct", "summary": "3 133 RUB · 1 h 25 min · direct", "display_markdown": "**Rossiya · FV…** · MOW → LED · 14:00 → 15:25 · direct · 1 h 25 min · **3 133 RUB** · [Buy for 3 133 RUB](https://aviaseek.com/en/r//)", "booking_url": "https://aviaseek.com/en/r//" } ] } ``` Parameters: `from`/`to`/`date`/`return` (like `/search`), `limit` (up to 50), `offset`, `sort` (`price_asc` by default), `direct=1`, `max_price`, `max_transfers`, `transfers=0,1`, `airlines=DP,S7`, `with_baggage=1`, `with_handbag=1`, `departure_periods=morning,evening`. If the route or date is missing - `200` with `status: need_route|need_date` and a sample link. **`booking_url` is a required output element**: it is the purchase link via AviaSeek (`/r//`), valid for about 15 minutes; show it as a clickable link. With `is_complete: false` repeat the request - prices are still being collected. 5. **Direct results link:** `https://aviaseek.com/en/results?flightSearch=MOW0920LEDy100`. If the page is opened by an agent or a bot, the server runs the search itself and returns **HTML with an options list and clickable booking links** - a path for assistants that have neither MCP nor an HTTP-request tool (they can only read pages). People get the SPA at the same address. `flightSearch` format: `AAAMMDDCCC[MMDD][ycf][adults][children][infants]` (the same value as in the `view_all_url` field of the MCP response). Example: `MOW0920LEDy100` = Moscow -> Saint Petersburg, 20 September, economy, 1 adult. 3. **The site's internal HTTP API** (public, no key; used by the `/results` page): `POST /api/search/start` with the body `{"directions":[{"origin":"MOW","destination":"LED","date":"2026-09-20"}],"passengers":{"adults":1,"children":0,"infants":0},"trip_class":0}` -> `{"search_id":"...","last_update_timestamp":0}`; then `GET /api/search/results?search_id=` returns a snapshot of offers. For round trips two `directions[]` elements are passed. ## 2. Key site sections - Search: https://aviaseek.com/en/ - Results: https://aviaseek.com/en/results - City catalog: https://aviaseek.com/en/cities - City page: https://aviaseek.com/en/cities/ - Route catalog: https://aviaseek.com/en/directions - Routes from a city: https://aviaseek.com/en/directions/ - Route: https://aviaseek.com/en/routes// - Airport catalog: https://aviaseek.com/en/airports - Airline catalog: https://aviaseek.com/en/airlines - FAQ: https://aviaseek.com/en/faq ## 3. Popular destinations (IATA codes) From Moscow (MOW): LED (Saint Petersburg), AER (Sochi), KZN (Kazan), SVX (Yekaterinburg), KRR (Krasnodar), ROV (Rostov-on-Don), ALA (Almaty), NQZ (Astana), IST (Istanbul), LON (London), PAR (Paris), BER (Berlin). Other codes: MOW (Moscow), LED (Saint Petersburg), AER (Sochi), KZN (Kazan), SVX (Yekaterinburg), KRR (Krasnodar), ROV (Rostov-on-Don), UFA (Ufa), KGD (Kaliningrad), VVO (Vladivostok), IKT (Irkutsk), KJA (Krasnoyarsk), OMS (Omsk), TJM (Tyumen), CEK (Chelyabinsk), VOG (Volgograd), ARH (Arkhangelsk), KUF (Samara), PEE (Perm), OVB (Novosibirsk), KHV (Khabarovsk), VOZ (Voronezh), IST (Istanbul), LON (London), PAR (Paris), BER (Berlin), BCN (Barcelona), AMS (Amsterdam), FRA (Frankfurt), VIE (Vienna), PRG (Prague), DXB (Dubai), BKK (Bangkok), DPS (Bali), NYO/ARN (Stockholm). The full catalog of routes and cities is at https://aviaseek.com/en/directions and https://aviaseek.com/en/cities. ## 4. How the search works 1. The agent calls `searchFlights` with city IATA codes and dates. 2. AviaSeek starts a search with the aggregator and gets a `search_id`. 3. Polling until complete (cap ~30 seconds). If it does not finish - `partial`/`pending`, a repeated call loads the rest. 4. Response: a summary (total, cheapest) + a `results[]` array with prices, airlines, times and `booking_url`. ## 5. Limitations - Only economy/business/first classes (no premium class). - Up to 9 adults. - Booking links (booking_url) live ~15 minutes.