Flywizz

io.github.victorlane由 GitHub 账号 victorlane 发布v0.1.0更新于 Oct 24, 2026

Search Wizz Air flights from your AI agent. Unofficial.

已验证STDIO仅桌面位置与生活金融

概览

AI 生成的概览

一个非官方的本地 MCP 服务器,让助手用自然语言查询 Wizz Air 的航班、票价、航线和航班状态。

可以做什么
查询某条航线的票价和时刻表,包含每日最低价和所有出发时间,支持往返和折扣俱乐部价格。查找某月最便宜的一天、最便宜的周末,或目标日期前后的价格。探索航线网络:从某机场可到达的目的地、按国家分组的目的地,以及航线是否直飞。查询运营日期(含一次中转)以及某个航班号的实时状态。
适用场景
当你希望助手回答 Wizz Air 相关问题,例如某航线哪天最便宜、从某机场能飞往哪里、某航班是否运营,而不想自己写代码调用 SDK 时适用。
运行要求
以 stdio 本地进程运行,需从 PyPI 安装带 MCP 扩展的包(uv tool install 或 pipx),安装后提供 flywizz-mcp 命令。无需 API 密钥或账号。需要能访问 Wizz Air 的网络接口。
安装前请注意
非官方项目,与 Wizz Air 无关;结果依赖其非公开接口,可能失效。价格以出发机场所在货币计价,无报价的日期显示为无价格而非免费。受机器人防护限制的可用性接口未对外暴露。接口并非归你所有,请注意请求频率。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Flywizz,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

Flywizz SDK

[PyPI version] [Python versions] [CI] [CodeQL] [PyPI downloads] [License]

An open-source unofficial API wrapper to get flight data from Wizz Air.

[!TIP] MCP server for AI agents included. Plug Flywizz into Claude Desktop, Claude Code, or Cursor and search Wizz Air flights in natural language. Jump to the MCP Quickstart.

Contents

Installation

bash
pip install Flywizz

Or using uv:

bash
uv add Flywizz

Quick Start

python
from datetime import datetime, timedeltafrom flywizz import WizzAir, TimetableSearch
# Initialize the clientclient = WizzAir()
# Set up search parameterssearch = TimetableSearch(    origin="BUD",       # Budapest    destination="LTN",  # London Luton    date_from=datetime.now() + timedelta(days=30),    date_to=datetime.now() + timedelta(days=60),)
# One call gets the schedule and the pricesfor day in client.get_timetable(search):    if day.price is None:        continue    print(f"{day.departure_date.date()}: {day.price.amount} {day.price.currency}")    print(f"  departures: {', '.join(d.departure.strftime('%H:%M') for d in day.departures)}")

Each entry is one operating day: the cheapest fare that day, plus every departure time, so a single call answers both "when does it fly" and "what does it cost".

Three things to know first

Prices are in the departure station's currency

Wizz Air has no server-side currency override. BUD -> LTN quotes in HUF, LTN -> BUD quotes in GBP, WAW -> LTN in PLN. Body fields, query params, cookies and headers named currency are all ignored.

Read Station.currency_code from get_network() if you need to know which currency you'll get before you search, and convert client-side.

Not every operating day carries a price

timetableV2 and farechart attach a price object to every day, but on days they will not quote inline, the amount is 0 and price_type is either checkPrice (a fare exists, but only through search/search) or noData. Flywizz maps both to price=None, so an unpriced day is never mistaken for a free one. Timetable entries still expose the indicative fare through original_price:

python
for day in client.get_timetable(search):    if day.price is not None:        print(day.departure_date.date(), day.price.amount, day.price.currency)    elif day.original_price is not None:        print(day.departure_date.date(), "~", day.original_price.amount, "(check price)")

Search windows are capped, and the caps differ

Priced and unpriced surfaces disagree about how far ahead you may look. Both reject a wider window with 400 InvalidTimeDateRange and no further explanation, so Flywizz enforces each limit itself, in a message that names it.

python
from flywizz.misc import (    MAX_TIMETABLE_WINDOW_DAYS,      # 42, for timetableV2 and farechart    MAX_FLIGHT_DATES_WINDOW_DAYS,   # 62, for the schedule endpoints)

cheapest_weekend() splits the range internally, since its default of months_ahead=3 far exceeds either cap.

search/search is behind a bot gate

Four endpoints (search/search, booking/seatmap, booking/ancillaries, booking/passengers) sit behind Kasada and answer 429 with an empty body from any non-browser client. Flywizz raises BotGateError rather than trying to solve the challenge.

Everything else, including the priced timetableV2 and farechart surfaces, is open. That is enough for price tracking, route exploration and calendar search. If you need fare bundles and sell keys, drive a real browser session and pass its headers in:

python
from flywizz import WizzAir, WizzairTransport
client = WizzAir(WizzairTransport(kasada_headers={    "x-kpsdk-ct": "...",    "x-kpsdk-v": "...",    "x-kpsdk-h": "...",    "x-kpsdk-cd": "...",}))

Full details of the gate, the session handshake, and the whole route table are in docs/internal-api-spec.md.

API Reference

WizzAir Class

Constructor
python
WizzAir(transport: Optional[Transport] = None)

Creates a new Wizz Air client instance.

Parameters:

  • transport (Transport, optional): Inject a custom transport, e.g. a CachingTransport wrapping the default, a WizzairTransport with kasada_headers, or a fixture transport for tests. Defaults to a fresh WizzairTransport.

Example:

python
# Defaultsclient = WizzAir()
# With caching for the 650 KB network metadatafrom flywizz import CachingTransport, WizzairTransportclient = WizzAir(CachingTransport(WizzairTransport(), ttl=3600))
Methods
MethodEndpointWhat it gives you
get_network(language="en-gb")asset/mapEvery station, its coordinates, currency, and connections
get_destinations(origin, direct_only=True)derivedStations reachable from origin
explore_by_country(origin)derivedDestinations grouped by country code
validate_route(origin, destination)derivedDoes Wizz Air fly this route direct
get_flight_dates(origin, destination, date_from, date_to)search/flightDatesOperating days, no prices, very cheap
get_flight_dates_multi(origin, destinations, date_from, date_to)search/FlightDatesMultiArrivalOperating days for up to 5 destinations per request, batched
get_connecting_flight_dates(origin, destination, date_from, date_to)search/dohopFlightDatesOperating days including one-stop connections
get_timetable(params)search/timetableV2Cheapest fare per day, plus every departure
get_return_timetable(params)search/timetableV2Outbound and inbound in one call
get_fare_chart(params)asset/farechartPrice strip around a target date
get_availability(params)search/searchFare bundles and sell keys. Bot-gated
cheapest_in_month(origin, destination, month)derivedCheapest day in a calendar month
cheapest_weekend(origin, destination, months_ahead=3)derivedCheapest Fri-Sun or Fri-Mon return
explore_with_fares(origin, date_from, date_to, limit=None)derivedEvery destination with its cheapest fare
get_flight_status(carrier_code, flight_number, date=None)asset/flightinformationLive status for one flight number
get_currencies()asset/currenciesSupported ISO 4217 codes
get_countries()asset/countryCountries with EU / Schengen flags
get_cultures()asset/culturesSite languages and their currencies
get_service_fees(currencies=None)asset/serviceFeesPublished baggage, seat and change fees
get_wdc_prices()asset/wdcPriceDiscount Club tiers and minimum discounts

Every method exists on AsyncWizzAir with the same signature.

TimetableSearch Class

Parameters for a timetable search.

python
TimetableSearch(    origin: str,    destination: str,    date_from: datetime,    date_to: datetime,    return_date_from: Optional[datetime] = None,    return_date_to: Optional[datetime] = None,    adults: int = 1,    children: int = 0,    infants: int = 0,    price_type: str = "regular",)

Parameters:

  • origin (str): IATA code of the departure station (e.g. "BUD")
  • destination (str): IATA code of the arrival station (e.g. "LTN")
  • date_from (datetime): Start of the outbound departure window
  • date_to (datetime): End of the outbound departure window
  • return_date_from / return_date_to (datetime, optional): Inbound window. Both are required by get_return_timetable()
  • adults / children / infants (int): Passenger counts. At least one adult
  • price_type (str): "regular" or "wdc" for Wizz Discount Club pricing

FareChartSearch Class

Parameters for the price strip.

python
FareChartSearch(    origin: str,    destination: str,    date: datetime,    day_interval: int = 3,    adults: int = 1,    children: int = 0,    infants: int = 0,    price_type: str = "regular",)

day_interval is the half-window around date and must be at least 3, so the default returns seven days. Smaller values are rejected upstream with DayIntervalMustBeGreaterOrEqualTo3.

AvailabilitySearch Class

Parameters for the bot-gated availability call.

python
AvailabilitySearch(    origin: str,    destination: str,    departure_date: datetime,    return_date: Optional[datetime] = None,    wdc: bool = True,    is_flight_change: bool = False,    adults: int = 1,    children: int = 0,    infants: int = 0,)

Data Models

Price

Represents a money amount as Wizz Air reports it.

Attributes:

  • amount (float): The amount
  • currency (str): ISO 4217 code, always the departure station's currency
  • exchanged_amount (Optional[float]): The SPA's client-side conversion hook. Stays None for anonymous sessions
  • exchanged_currency (Optional[str]): Currency of exchanged_amount

TimetableEntry

One operating day for a route, with its cheapest fare.

Attributes:

  • departure_station (str), arrival_station (str): IATA codes
  • departure_date (datetime): The operating day
  • price (Optional[Price]): Cheapest fare that day, None if sold out
  • original_price (Optional[Price]): Pre-discount price
  • departures (list[Departure]): Every departure that day
  • price_type (Optional[str]): "price" when there was inventory
  • has_mac_flight (bool): The route includes a metropolitan-area alternative
  • applied_coupon_code (Optional[str])

Departure

Attributes:

  • departure (datetime): Departure time
  • is_cheapest_of_the_day (bool): This is the departure price refers to

FlightDate

One operating day from get_connecting_flight_dates().

Attributes:

  • date (datetime): The operating day
  • stops (int): 0 for a direct flight, 1 or more for a connection
  • is_direct (bool): Property, true when stops is 0

FareChartEntry

One day of the price strip.

Attributes:

  • departure_station (str), arrival_station (str): IATA codes
  • day (datetime): The day
  • price (Optional[Price]): Cheapest price that day
  • class_of_service (Optional[str]): Booking class the quote came from
  • price_type (Optional[str]), has_mac_flight (bool)

Station

An airport in Wizz Air's live network. Returned by the explore methods.

Attributes:

  • iata (str): IATA station code
  • name (str): Station name
  • country_code (str): Uppercase ISO2 country code (e.g. "HU", "GB")
  • country_name (str): Country name
  • currency_code (str): Local currency. Fares from here are priced in it
  • latitude (float), longitude (float): Coordinates
  • mac (Optional[str]): Metropolitan area code (e.g. "LON")
  • aliases (list[str]): Alternative names
  • categories (list[int]): Marketing categories assigned by Wizz Air
  • rank (Optional[int]), is_fake_station (bool)
  • connections (list[Connection]): Everywhere this station flies

Helper: destinations(direct_only=True) returns just the IATA codes.

Connection

Attributes:

  • iata (str): Destination station code
  • is_direct (bool): A direct Wizz Air flight. The flag you usually want
  • is_connected (bool): A self-transfer connection rather than a direct flight
  • is_domestic (bool), is_new (bool)
  • operation_start_date (Optional[datetime]): When the route opens

FlightStatus

A single operating flight from the flight-information endpoint.

Attributes:

  • flight_id (int), carrier_code (str), flight_number (int)
  • departure_airport (str), arrival_airport (str)
  • original_departure_airport / original_arrival_airport (Optional[str]): Differ from the actual airports when the flight was diverted
  • operation_day (Optional[datetime])
  • scheduled_departure / scheduled_arrival (Optional[datetime])
  • op_suffix (Optional[str])

DestinationFare

Returned by explore_with_fares(). Pairs a reachable destination with its cheapest sampled fare, if one came back from the price probe.

Attributes:

  • station (Station): The destination
  • price (Optional[Price]): Cheapest fare in the window, or None if the route is in the network but no priced inventory came back
  • departure_date (Optional[datetime]): The day that fare was on

Examples

Cheapest day in a month

python
from datetime import datetimefrom flywizz import WizzAir
client = WizzAir()cheapest = client.cheapest_in_month("BUD", "LTN", datetime(2026, 11, 1))
if cheapest:    print(f"{cheapest.departure_date.date()}: "          f"{cheapest.price.amount} {cheapest.price.currency}")

Is it cheaper a day either side?

python
from datetime import datetimefrom flywizz import WizzAir, FareChartSearch
client = WizzAir()strip = client.get_fare_chart(    FareChartSearch(origin="BUD", destination="LTN",                    date=datetime(2026, 11, 10), day_interval=3))
for day in strip:    price = f"{day.price.amount:.0f} {day.price.currency}" if day.price else "-"    print(f"{day.day.date()}  {price}")

Cheapest weekend in the next three months

python
from flywizz import WizzAir
client = WizzAir()weekend = client.cheapest_weekend("BUD", "LTN", months_ahead=3)
if weekend:    out, back = weekend    total = out.price.amount + back.price.amount    print(f"{out.departure_date.date()} -> {back.departure_date.date()}: "          f"{total} {out.price.currency}")

Discount Club pricing

python
from datetime import datetime, timedeltafrom flywizz import WizzAir, TimetableSearch
client = WizzAir()wdc = client.get_timetable(    TimetableSearch(        origin="BUD", destination="LTN",        date_from=datetime.now() + timedelta(days=30),        date_to=datetime.now() + timedelta(days=45),        price_type="wdc",    ))

Live flight status

python
from flywizz import WizzAir
client = WizzAir()for leg in client.get_flight_status("W6", "6201"):    print(f"{leg.operation_day.date()} {leg.departure_airport} -> {leg.arrival_airport}")

Carrier codes are the AOC prefix: W6 (Hungary), W4 (Malta), W9 (UK).

Error Handling

python
from flywizz import BotGateError, ValidationError, WizzairException
try:    entries = client.get_timetable(search)    if not entries:        print("No flights found for the given criteria")except ValidationError as e:    print(f"Wizz Air rejected the request: {e.codes}")except BotGateError:    print("This endpoint needs a browser session")except WizzairException as e:    print(f"Wizz Air API error: {e}")

ValidationError.codes carries Wizz Air's own validation codes, which name the fields it objected to. An empty list means the API answered with nothing matching; it never means a failure.

Checking many routes at once

get_flight_dates_multi() answers the schedule question for several destinations per request, which is five times cheaper than looping over get_flight_dates():

python
from datetime import datetime, timedeltafrom flywizz import WizzAir, WizzairTransport, CachingTransport
client = WizzAir(CachingTransport(WizzairTransport()))start = datetime.now() + timedelta(days=30)
destinations = [s.iata for s in client.get_destinations("BUD")]dates = client.get_flight_dates_multi("BUD", destinations, start, start + timedelta(days=30))
for iata, days in dates.items():    print(f"{iata}: {len(days)} operating days")

Every destination you ask for appears in the result. A route that does not operate in the window maps to an empty list rather than going missing, so there is no membership check to forget.

The endpoint answers for at most five destinations per request and reports an over-long list by returning nothing rather than an error. Flywizz batches for you, so the list you pass has no practical ceiling.

Fares are a different matter: there is no batched priced surface, so explore_with_fares() still costs one call per destination.

Flights that need a connection

validate_route() and get_flight_dates() cover direct service only, so a False from either does not mean you cannot fly the route. get_connecting_flight_dates() also answers for pairs that need a change of plane:

python
from datetime import datetime, timedeltafrom flywizz import WizzAir
client = WizzAir()start = datetime.now() + timedelta(days=30)
client.validate_route("KTW", "LIS")   # False - no direct flight
for day in client.get_connecting_flight_dates("KTW", "LIS", start, start + timedelta(days=30)):    print(day.date.date(), "direct" if day.is_direct else f"{day.stops} stop(s)")

On a route that flies direct only some days, both kinds come back interleaved, so you can see which days need a connection. Filter on is_direct when one will not do.

Explore Mode

Explore Mode answers the question "where can I actually fly from here?". It reads Wizz Air's live network metadata once and exposes the reachable destinations from any station, optionally grouped or joined with the cheapest fare in a date window.

All methods below are available on both WizzAir and AsyncWizzAir.

List every destination

python
for station in client.get_destinations("BUD"):    print(f"{station.iata} {station.name} ({station.country_code})")

Pass direct_only=False to include self-transfer connections.

Group destinations

python
by_country = client.explore_by_country("BUD")
print(f"BUD flies to {len(by_country)} countries")for country, stations in sorted(by_country.items()):    codes = ", ".join(s.iata for s in stations)    print(f"  {country}: {codes}")

Country codes are uppercase ISO2.

Check a single route

python
client.validate_route("BUD", "LTN")  # True

Destinations with their cheapest fare

explore_with_fares() joins the network destinations with a timetable probe, so each destination comes back with its cheapest Price (or None if no inventory was returned for that route in the window).

Wizz Air has no "anywhere" search, so this is one call per destination. Use limit while iterating and wrap the transport in CachingTransport.

python
from datetime import datetime, timedelta
start = datetime.now() + timedelta(days=30)end = start + timedelta(days=14)
results = client.explore_with_fares("BUD", start, end, limit=20)
priced = [d for d in results if d.price is not None]for d in sorted(priced, key=lambda d: d.price.amount)[:10]:    print(f"{d.station.iata} {d.station.name}: "          f"{d.price.amount} {d.price.currency}")

Prices across destinations are all in the origin's currency, so they are directly comparable.

Async usage

AsyncWizzAir mirrors every explore method, and explore_with_fares() fans out concurrently:

python
import asynciofrom datetime import datetime, timedeltafrom flywizz import AsyncWizzAir
async def main():    async with AsyncWizzAir() as client:        results = await client.explore_with_fares(            "BUD",            datetime.now() + timedelta(days=30),            datetime.now() + timedelta(days=45),            limit=20,            concurrency=5,        )        print(f"{sum(1 for r in results if r.price)} priced destinations")
asyncio.run(main())

If you call multiple explore methods in a row, wrap the transport in CachingTransport so the network metadata is fetched once and reused.

Use with Claude, Cursor, and other MCP clients

[!IMPORTANT] Two commands and you're done:

bash
uv tool install "Flywizz[mcp]"claude mcp add flywizz flywizz-mcp

Now your agent can search Wizz Air flights in natural language. No API keys, no accounts.

Flywizz ships an optional Model Context Protocol server so your agent can search Wizz Air fares from natural-language prompts like "what's the cheapest day in November to fly Budapest to London" or "where can I fly from Budapest in the first week of December".

Quickstart

1. Install Flywizz with the MCP extra:

bash
uv tool install "Flywizz[mcp]"

Or with pip:

bash
pipx install "Flywizz[mcp]"

This installs a flywizz-mcp console script on your PATH.

2. Add it to your agent:

Claude Code (one-liner):

bash
claude mcp add flywizz flywizz-mcp

Claude Desktop: open ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (or %APPDATA%\Claude\claude_desktop_config.json on Windows) and add:

json
{  "mcpServers": {    "flywizz": {      "command": "flywizz-mcp"    }  }}

Then restart Claude Desktop.

Cursor: Settings → MCP → Add new server, name flywizz, command flywizz-mcp.

3. Try it. Ask your agent:

"What's the cheapest day in November to fly from Budapest to London Luton?"

The agent should call cheapest_day with origin="BUD", destination="LTN", month="2026-11-01", then report the day and the price.

Currency

There is no currency setting, because Wizz Air has none. Every tool returns prices in the departure station's local currency and reports the code alongside the amount. Tell your agent to quote the currency it gets back rather than assuming euros.

Exposed tools

The server exposes five curated tools so the agent can pick reliably:

  • find_fares for "how much is BUD to LTN in November", with the full day-by-day breakdown and every departure time
  • cheapest_day for "what's the cheapest day this month to fly X to Y"
  • price_around for "is it cheaper a day either side of the 10th"
  • explore_destinations for "what countries can I reach from X"
  • flight_status for "when does W6 6201 operate"

No API keys, accounts, or rate-limit setup. The server reuses a single cached WizzAir client across calls, so the network metadata is fetched once per process.

The bot-gated search/search surface is deliberately not exposed: it cannot work from a headless process, and an agent tool that always fails is worse than no tool.

API characteristics

These properties of the upstream API determine what this SDK can and cannot offer. Flywizz handles each of them for you; knowing them explains why some calls behave the way they do.

Behaviour
Country codesUppercase ISO 3166-1 alpha-2. Lowercase codes are rejected.
AuthenticationAnonymous, but every request needs a session handshake and a CSRF token that rotates between calls.
CurrencyDetermined by the departure station and not negotiable. Convert client-side.
Cheap-fare searchThere is no "anywhere" endpoint. explore_with_fares() fans out one call per destination.
Priced surfacestimetableV2 and farechart are open; search/search is gated.
PaginationNone. Every response is complete, which is why asset/map is 650 KB.
Window limitsA timetable window may span at most 42 days; a schedule window, 62.
Bot protectionKasada, permanently, on four endpoints.

Caching

asset/map is 650 KB and changes rarely. Wrap the transport when you call it more than once:

python
from flywizz import WizzAir, WizzairTransport, CachingTransport
client = WizzAir(CachingTransport(WizzairTransport(), ttl=3600))

Asynchronous code requires the asynchronous wrapper. The two are not interchangeable: mixing them raises TypeError rather than caching a coroutine.

python
from flywizz import AsyncWizzAir, AsyncWizzairTransport, AsyncCachingTransport
client = AsyncWizzAir(AsyncCachingTransport(AsyncWizzairTransport(), ttl=3600))

Neither wrapper caches POSTs, so fares stay live. Call invalidate() to drop the cache early.

Rate Limiting

The SDK retries network errors and 5xx responses with exponential backoff, up to 4 attempts. It deliberately does not retry a 429: on this API that is the Kasada bot gate rather than backpressure, and retrying just adds load while still failing.

Wizz Air's API is anonymous, but it is not yours. Be a good citizen: cache the network metadata, keep explore_with_fares() fan-out modest, and don't poll fares faster than the prices actually change.

Contributing

This is an open-source project. Contributions are welcome — see CONTRIBUTING.md. Agent-facing notes on the architecture live in AGENTS.md, and everything known about the upstream API is in docs/internal-api-spec.md.

Disclaimer

This is an unofficial API wrapper and is not affiliated with Wizz Air. It performs read-only requests against the public endpoints the airline's own website uses. Use at your own risk and ensure you comply with Wizz Air's terms of service.

来源:README.md,提交 b3d6ec7

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.1.0最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分