{
  "markdown": "# Flyan SDK\n\n[![PyPI version](https://img.shields.io/pypi/v/Flyan.svg)](https://pypi.org/project/Flyan/)\n[![Python versions](https://img.shields.io/pypi/pyversions/Flyan.svg)](https://pypi.org/project/Flyan/)\n[![CI](https://github.com/victorlane/Flyan/actions/workflows/ci.yml/badge.svg)](https://github.com/victorlane/Flyan/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/victorlane/Flyan/actions/workflows/codeql.yml/badge.svg)](https://github.com/victorlane/Flyan/actions/workflows/codeql.yml)\n[![PyPI downloads](https://img.shields.io/pypi/dm/Flyan.svg)](https://pypi.org/project/Flyan/)\n[![License](https://img.shields.io/github/license/victorlane/Flyan.svg)](https://github.com/victorlane/Flyan/blob/master/LICENSE)\n\nAn open-source unofficial API wrapper to get flight data from Ryanair.\n\n<!-- mcp-name: io.github.victorlane/flyan-mcp -->\n\n> [!TIP]\n> **New: MCP server for AI agents.** Plug Flyan into Claude Desktop,\n> Claude Code, or Cursor and search Ryanair flights in natural language.\n> Jump to the [MCP Quickstart](#use-with-claude-cursor-and-other-mcp-clients).\n\n## Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [API Reference](#api-reference)\n- [Data Models](#data-models)\n- [Examples](#examples)\n- [Explore Mode](#explore-mode)\n- [**Use with Claude, Cursor, and other MCP clients**](#use-with-claude-cursor-and-other-mcp-clients)\n- [Supported Airports](#supported-airports)\n- [Supported Currencies](#supported-currencies)\n- [Rate Limiting](#rate-limiting)\n- [Contributing](#contributing)\n- [Disclaimer](#disclaimer)\n\n## Installation\n\n```bash\npip install Flyan\n```\n\nOr using uv:\n\n```bash\nuv add Flyan\n```\n\n## Quick Start\n\n```python\nfrom datetime import datetime\nfrom flyan import RyanAir, FlightSearchParams\n\n# Initialize the client\nclient = RyanAir(currency=\"EUR\")\n\n# Set up search parameters\nsearch_params = FlightSearchParams(\n    from_airport=\"DUB\",  # Dublin\n    to_airport=\"BCN\",    # Barcelona\n    from_date=datetime(2025, 8, 15),\n    to_date=datetime(2025, 8, 20),\n    max_price=200\n)\n\n# Search for one-way flights\nflights = client.get_oneways(search_params)\n\n# Display results\nfor flight in flights:\n    print(f\"Flight {flight.flight_number}: {flight.departure_airport.name} → {flight.arrival_airport.name}\")\n    print(f\"Departure: {flight.departure_date}\")\n    print(f\"Price: {flight.price} {flight.currency}\")\n    print(\"---\")\n```\n\n## API Reference\n\n### RyanAir Class\n\n#### Constructor\n\n```python\nRyanAir(currency: str = \"EUR\")\n```\n\nCreates a new RyanAir client instance.\n\n**Parameters:**\n\n- `currency` (str, optional): Preferred currency for pricing. Defaults to \"EUR\". Must be a valid currency code from the supported currencies list.\n\n**Example:**\n\n```python\n# Default EUR currency\nclient = RyanAir()\n\n# Specific currency\nclient = RyanAir(currency=\"USD\")\n```\n\n#### Methods\n\n##### `get_oneways(params: FlightSearchParams) -> list[Flight]`\n\nSearch for one-way flights.\n\n**Parameters:**\n\n- `params` (FlightSearchParams): Search parameters\n\n**Returns:**\n\n- `list[Flight]`: List of available flights\n\n### FlightSearchParams Class\n\nParameters for searching flights.\n\n```python\nFlightSearchParams(\n    from_airport: str,\n    from_date: datetime,\n    to_date: datetime,\n    destination_country: Optional[str] = None,\n    max_price: Optional[int] = None,\n    to_airport: Optional[str] = None,\n    departure_time_from: Optional[str] = \"00:00\",\n    departure_time_to: Optional[str] = \"23:59\"\n)\n```\n\n**Parameters:**\n\n- `from_airport` (str): IATA code of departure airport (e.g., \"DUB\")\n- `from_date` (datetime): Earliest departure date\n- `to_date` (datetime): Latest departure date\n- `destination_country` (str, optional): Country code for destination\n- `max_price` (int, optional): Maximum price filter\n- `to_airport` (str, optional): IATA code of arrival airport\n- `departure_time_from` (str, optional): Earliest departure time (HH:MM format)\n- `departure_time_to` (str, optional): Latest departure time (HH:MM format)\n\n**Example:**\n\n```python\nfrom datetime import datetime\n\nparams = FlightSearchParams(\n    from_airport=\"DUB\",\n    from_date=datetime(2025, 8, 15),\n    to_date=datetime(2025, 8, 20),\n    to_airport=\"BCN\",\n    max_price=150,\n    departure_time_from=\"08:00\",\n    departure_time_to=\"18:00\"\n)\n```\n\n### ReturnFlightSearchParams Class\n\nExtended parameters for return flight searches.\n\n```python\nReturnFlightSearchParams(\n    # All FlightSearchParams fields plus:\n    return_date_from: datetime,\n    return_date_to: datetime,\n    inbound_departure_time_from: Optional[str] = \"00:00\",\n    inbound_departure_time_to: Optional[str] = \"23:59\"\n)\n```\n\n## Data Models\n\n### Flight\n\nRepresents a single flight.\n\n**Attributes:**\n\n- `departure_airport` (Airport): Departure airport information\n- `arrival_airport` (Airport): Arrival airport information\n- `departure_date` (datetime): Departure date and time\n- `arrival_date` (datetime): Arrival date and time\n- `price` (float): Flight price\n- `currency` (str): Price currency\n- `flight_key` (str): Unique flight identifier\n- `flight_number` (str): Flight number\n- `previous_price` (Optional[str | float]): Previous price if available\n\n### Airport\n\nRepresents airport information.\n\n**Attributes:**\n\n- `country_name` (str): Country name\n- `iata_code` (str): IATA airport code\n- `name` (str): Airport name\n- `seo_name` (str): SEO-friendly name\n- `city_name` (str): City name\n- `city_code` (str): City code\n- `city_country_code` (str): Country code\n\n### ReturnFlight\n\nRepresents a return flight booking.\n\n**Attributes:**\n\n- `outbound` (Flight): Outbound flight\n- `inbound` (Flight): Return flight\n- `summary_price` (float): Total price for both flights\n- `summary_currency` (str): Currency for total price\n- `previous_price` (str | float): Previous total price if available\n\n### NetworkAirport\n\nRepresents an airport in Ryanair's live network. Returned by the explore methods.\n\n**Attributes:**\n\n- `iata_code` (str): IATA airport code\n- `name` (str): Airport name\n- `seo_name` (str): SEO-friendly name\n- `country_code` (str): Lowercase ISO2 country code (e.g. \"ie\", \"es\")\n- `city_code` (str): City code (e.g. \"LONDON\", \"DUBLIN\")\n- `region_code` (Optional[str]): Region code (e.g. \"SCOTLAND\", \"ANDALUSIA\")\n- `currency_code` (str): Local currency code\n- `time_zone` (str): IANA timezone (e.g. \"Europe/Dublin\")\n- `base` (bool): True if this is a Ryanair base\n- `latitude` (float), `longitude` (float): Coordinates\n- `routes` (list[str]): Raw route strings (year-round)\n- `seasonal_routes` (list[str]): Raw route strings (seasonal-only)\n- `categories` (list[str]): Marketing categories assigned by Ryanair\n- `aliases` (list[str]): Alternative names\n\nHelpers: `airport_routes()`, `country_routes()`, `seasonal_airport_routes()`,\n`typed_routes()`, `typed_seasonal_routes()`.\n\n### DestinationFare\n\nReturned by `explore_with_fares()`. Pairs a reachable destination with its\ncheapest sampled fare, if one was returned by the price probe.\n\n**Attributes:**\n\n- `airport` (NetworkAirport): The destination airport\n- `fare` (Optional[Flight]): The cheapest sampled fare in the window, or `None`\n  if the route is in the network but no priced inventory came back (no flights\n  in the window, sold out, etc.)\n\n## Examples\n\n### Search by Country\n\n```python\n# Search flights to any airport in Spain\nparams = FlightSearchParams(\n    from_airport=\"DUB\",\n    destination_country=\"ES\",\n    from_date=datetime(2025, 9, 1),\n    to_date=datetime(2025, 9, 7)\n)\n\nflights = client.get_oneways(params)\n```\n\n### Filter by Time and Price\n\n```python\n# Morning flights under €100\nparams = FlightSearchParams(\n    from_airport=\"STN\",  # London Stansted\n    to_airport=\"DUB\",    # Dublin\n    from_date=datetime(2025, 8, 1),\n    to_date=datetime(2025, 8, 5),\n    max_price=100,\n    departure_time_from=\"06:00\",\n    departure_time_to=\"12:00\"\n)\n\nflights = client.get_oneways(params)\n```\n\n### Error Handling\n\n```python\nfrom flyan import RyanairException\n\ntry:\n    flights = client.get_oneways(params)\n    if not flights:\n        print(\"No flights found for the given criteria\")\nexcept RyanairException as e:\n    print(f\"Ryanair API error: {e}\")\nexcept Exception as e:\n    print(f\"Unexpected error: {e}\")\n```\n\n## Explore Mode\n\nExplore Mode answers the question \"where can I actually fly from here?\". It\nreads Ryanair's live network metadata once and exposes the reachable\ndestinations from any airport, optionally grouped, filtered, or joined with\nthe cheapest fare in a date window.\n\nAll methods below are available on both `RyanAir` and `AsyncRyanAir`.\n\n### List every destination\n\n```python\ndestinations = client.get_destinations(\"DUB\")\n\nfor airport in destinations:\n    print(f\"{airport.iata_code} {airport.name} ({airport.country_code})\")\n```\n\n### Filter by country, region or city\n\n```python\n# All Scottish airports DUB flies to\nin_scotland = client.get_destinations_in_region(\"DUB\", \"SCOTLAND\")\n\n# All London airports DUB flies to (LGW, LTN, STN)\nin_london = client.get_destinations_in_city(\"DUB\", \"LONDON\")\n\n# All Spanish airports DUB flies to\nin_spain = client.get_destinations_in_country(\"DUB\", \"es\")\n```\n\nCountry codes are lowercase ISO2. Region and city codes come from the live\nnetwork (uppercase, e.g. `SCOTLAND`, `ANDALUSIA`, `COSTA_DE_SOL`, `LONDON`,\n`MILAN`).\n\n### Group destinations\n\n```python\n# {country_code: [airports]}\nby_country = client.explore_by_country(\"DUB\")\n\nprint(f\"DUB flies to {len(by_country)} countries\")\nfor country, airports in sorted(by_country.items()):\n    codes = \", \".join(a.iata_code for a in airports)\n    print(f\"  {country}: {codes}\")\n```\n\n```python\n# {region_code: [airports]}\nby_region = client.explore_by_region(\"DUB\")\n```\n\nAirports without a `region_code` are collected under the empty-string key,\nso callers can decide whether to surface or drop them.\n\n### Seasonal-only destinations\n\n```python\nseasonal = client.get_seasonal_destinations(\"DUB\")\n```\n\nRyanair's `seasonalRoutes` list is sparsely populated upstream, so this often\nreturns `[]` outside of summer/winter schedule transitions. The method is\nprovided so callers do not need to peek at the raw route strings.\n\n### Destinations with their cheapest fare\n\n`explore_with_fares()` joins the network destinations with a `oneWayFares`\nprobe, so each destination comes back with its cheapest sampled `Flight` (or\n`None` if no fare was returned for that route in the window). It costs one\nnetwork call plus one fare call.\n\n```python\nfrom datetime import datetime, timedelta\n\nstart = datetime.now() + timedelta(days=14)\nend = start + timedelta(days=7)\n\nresults = client.explore_with_fares(\"DUB\", start, end, max_price=100)\n\npriced = [d for d in results if d.fare is not None]\ncheapest_first = sorted(priced, key=lambda d: d.fare.price)\n\nfor d in cheapest_first[:10]:\n    print(f\"{d.airport.iata_code} {d.airport.name}: \"\n          f\"{d.fare.price} {d.fare.currency}\")\n```\n\n### Async usage\n\n`AsyncRyanAir` mirrors every explore method:\n\n```python\nimport asyncio\nfrom flyan import AsyncRyanAir\n\nasync def main():\n    async with AsyncRyanAir() as client:\n        by_country = await client.explore_by_country(\"DUB\")\n        print(f\"{len(by_country)} countries reachable from DUB\")\n\nasyncio.run(main())\n```\n\nIf you call multiple explore methods in a row, wrap the transport in\n`CachingTransport` so the network metadata is fetched once and reused.\n\n## Use with Claude, Cursor, and other MCP clients\n\n> [!IMPORTANT]\n> Two commands and you're done:\n>\n> ```bash\n> uv tool install \"Flyan[mcp]\"\n> claude mcp add flyan flyan-mcp\n> ```\n>\n> Now your agent can search Ryanair flights in natural language. No API\n> keys, no accounts.\n\nFlyan ships an optional Model Context Protocol server so your agent can\nsearch Ryanair fares from natural-language prompts like *\"find me a cheap\nflight from Dublin to Spain in August under €150\"* or *\"what's the cheapest\nday in July to fly DUB to BCN\"*.\n\n### Quickstart\n\n**1. Install Flyan with the MCP extra:**\n\n```bash\nuv tool install \"Flyan[mcp]\"\n```\n\nOr with pip:\n\n```bash\npipx install \"Flyan[mcp]\"\n```\n\nThis installs a `flyan-mcp` console script on your PATH.\n\n**2. Add it to your agent:**\n\n**Claude Code** (one-liner):\n\n```bash\nclaude mcp add flyan flyan-mcp\n```\n\n**Claude Desktop**: open `~/Library/Application Support/Claude/claude_desktop_config.json`\non macOS (or `%APPDATA%\\Claude\\claude_desktop_config.json` on Windows) and add:\n\n```json\n{\n  \"mcpServers\": {\n    \"flyan\": {\n      \"command\": \"flyan-mcp\"\n    }\n  }\n}\n```\n\nThen restart Claude Desktop.\n\n**Cursor**: Settings → MCP → Add new server, name `flyan`, command `flyan-mcp`.\n\n### Currency\n\nThe server returns prices in EUR by default. To get them in another\ncurrency, set the `FLYAN_CURRENCY` env var to any [supported ISO 4217 code](#supported-currencies)\nbefore launching `flyan-mcp`:\n\n```bash\nFLYAN_CURRENCY=GBP flyan-mcp\n```\n\nOr pass it through your agent's MCP config:\n\n```json\n{\n  \"mcpServers\": {\n    \"flyan\": {\n      \"command\": \"flyan-mcp\",\n      \"env\": { \"FLYAN_CURRENCY\": \"GBP\" }\n    }\n  }\n}\n```\n\nUnknown or unsupported codes silently fall back to EUR.\n\n**3. Try it.** Ask your agent:\n\n> \"Find me a one-way from Dublin to anywhere in Spain in the first week of\n> August under €150.\"\n\nThe agent should call `find_flights` with `destination_country=\"es\"`, then\nsummarize the cheapest options.\n\n### Exposed tools\n\nThe server exposes four curated tools so the agent can pick reliably:\n\n- `find_flights` for one-way searches with optional country, IATA, or price filters\n- `find_anywhere_under` for \"where can I go for under £X\" prompts\n- `explore_destinations` for \"what countries can I reach from X\"\n- `cheapest_per_day` for \"what's the cheapest day this month to fly X to Y\"\n\nNo API keys, accounts, or rate-limit setup. Ryanair's API is anonymous and\nthe server reuses a single `RyanAir` client across calls.\n\n## Supported Airports\n\nThe SDK supports all airports in Ryanair's network. Airport codes must be valid 3-letter IATA codes. The live list is fetched from Ryanair's aggregate endpoint via `client.get_network()`; iterate `network.airports` for the full set.\n\nPopular airports include:\n\n- **DUB** - Dublin\n- **STN** - London Stansted\n- **BCN** - Barcelona\n- **MAD** - Madrid\n- **FCO** - Rome Fiumicino\n- **BRU** - Brussels\n- **AMS** - Amsterdam\n\n## Supported Currencies\n\nThe SDK supports multiple currencies. Some popular ones include:\n\n- **EUR** - Euro\n- **USD** - US Dollar\n- **GBP** - British Pound\n- **CHF** - Swiss Franc\n\nSee `currencies.json` for the complete list.\n\n## Rate Limiting\n\nThe SDK includes automatic retry logic with exponential backoff to handle rate limiting and temporary API issues. It will retry failed requests up to 5 times before giving up.\n\n## Contributing\n\nThis is an open-source project. Contributions are welcome!\n\n## Disclaimer\n\nThis is an unofficial API wrapper and is not affiliated with Ryanair. Use at your own risk and ensure you comply with Ryanair's terms of service.\n",
  "bytes": 14866,
  "sha": "b574cb56597c30ca1c85affff66ff7974d94ce8233986910b96bb1909627934a",
  "repo_slug": "victorlane/flyan",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_victorlane_flyan_mcp_cd32fb47/readme"
}