Flywizz SDK
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.
Sibling project: Flyan, the same idea for Ryanair. Same layout, same seams. The APIs are not the same, though — see Wizz Air vs Ryanair.
Contents
- Installation
- Quick Start
- Two things to know first
- API Reference
- Data Models
- Examples
- Explore Mode
- Use with Claude, Cursor, and other MCP clients
- Wizz Air vs Ryanair
- Caching
- Rate Limiting
- Contributing
- Disclaimer
Installation
pip install Flywizz
Or using uv:
uv add Flywizz
Quick Start
from datetime import datetime, timedelta
from flywizz import WizzAir, TimetableSearch
# Initialize the client
client = WizzAir()
# Set up search parameters
search = 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 prices
for 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".
Two 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.
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:
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
WizzAir(transport: Optional[Transport] = None)
Creates a new Wizz Air client instance.
Parameters:
transport(Transport, optional): Inject a custom transport, e.g. aCachingTransportwrapping the default, aWizzairTransportwithkasada_headers, or a fixture transport for tests. Defaults to a freshWizzairTransport.
Example:
# Defaults
client = WizzAir()
# With caching for the 650 KB network metadata
from flywizz import CachingTransport, WizzairTransport
client = WizzAir(CachingTransport(WizzairTransport(), ttl=3600))
Methods
| Method | Endpoint | What it gives you |
|---|---|---|
get_network(language="en-gb") |
asset/map |
Every station, its coordinates, currency, and connections |
get_destinations(origin, direct_only=True) |
derived | Stations reachable from origin |
explore_by_country(origin) |
derived | Destinations grouped by country code |
validate_route(origin, destination) |
derived | Does Wizz Air fly this route direct |
get_flight_dates(origin, destination, date_from, date_to) |
search/flightDates |
Operating days, no prices, very cheap |
get_timetable(params) |
search/timetableV2 |
Cheapest fare per day, plus every departure |
get_return_timetable(params) |
search/timetableV2 |
Outbound and inbound in one call |
get_fare_chart(params) |
asset/farechart |
Price strip around a target date |
get_availability(params) |
search/search |
Fare bundles and sell keys. Bot-gated |
cheapest_in_month(origin, destination, month) |
derived | Cheapest day in a calendar month |
cheapest_weekend(origin, destination, months_ahead=3) |
derived | Cheapest Fri-Sun or Fri-Mon return |
explore_with_fares(origin, date_from, date_to, limit=None) |
derived | Every destination with its cheapest fare |
get_flight_status(carrier_code, flight_number, date=None) |
asset/flightinformation |
Live status for one flight number |
get_currencies() |
asset/currencies |
Supported ISO 4217 codes |
get_countries() |
asset/country |
Countries with EU / Schengen flags |
get_cultures() |
asset/cultures |
Site languages and their currencies |
get_service_fees(currencies=None) |
asset/serviceFees |
Published baggage, seat and change fees |
get_wdc_prices() |
asset/wdcPrice |
Discount Club tiers and minimum discounts |
Every method exists on AsyncWizzAir with the same signature.
TimetableSearch Class
Parameters for a timetable search.
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 windowdate_to(datetime): End of the outbound departure windowreturn_date_from/return_date_to(datetime, optional): Inbound window. Both are required byget_return_timetable()adults/children/infants(int): Passenger counts. At least one adultprice_type(str):"regular"or"wdc"for Wizz Discount Club pricing
FareChartSearch Class
Parameters for the price strip.
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.
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 amountcurrency(str): ISO 4217 code, always the departure station's currencyexchanged_amount(Optional[float]): The SPA's client-side conversion hook. StaysNonefor anonymous sessionsexchanged_currency(Optional[str]): Currency ofexchanged_amount
TimetableEntry
One operating day for a route, with its cheapest fare.
Attributes:
departure_station(str),arrival_station(str): IATA codesdeparture_date(datetime): The operating dayprice(Optional[Price]): Cheapest fare that day,Noneif sold outoriginal_price(Optional[Price]): Pre-discount pricedepartures(list[Departure]): Every departure that dayprice_type(Optional[str]):"price"when there was inventoryhas_mac_flight(bool): The route includes a metropolitan-area alternativeapplied_coupon_code(Optional[str])
Departure
Attributes:
departure(datetime): Departure timeis_cheapest_of_the_day(bool): This is the departurepricerefers to
FareChartEntry
One day of the price strip.
Attributes:
departure_station(str),arrival_station(str): IATA codesday(datetime): The dayprice(Optional[Price]): Cheapest price that dayclass_of_service(Optional[str]): Booking class the quote came fromprice_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 codename(str): Station namecountry_code(str): Uppercase ISO2 country code (e.g."HU","GB")country_name(str): Country namecurrency_code(str): Local currency. Fares from here are priced in itlatitude(float),longitude(float): Coordinatesmac(Optional[str]): Metropolitan area code (e.g."LON")aliases(list[str]): Alternative namescategories(list[int]): Marketing categories assigned by Wizz Airrank(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 codeis_direct(bool): A direct Wizz Air flight. The flag you usually wantis_connected(bool): A self-transfer connection rather than a direct flightis_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 divertedoperation_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 destinationprice(Optional[Price]): Cheapest fare in the window, orNoneif the route is in the network but no priced inventory came backdeparture_date(Optional[datetime]): The day that fare was on
Examples
Cheapest day in a month
from datetime import datetime
from 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?
from datetime import datetime
from 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
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
from datetime import datetime, timedelta
from 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
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
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.
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
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
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
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.
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:
import asyncio
from datetime import datetime, timedelta
from 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:
uv tool install "Flywizz[mcp]" claude mcp add flywizz flywizz-mcpNow 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:
uv tool install "Flywizz[mcp]"
Or with pip:
pipx install "Flywizz[mcp]"
This installs a flywizz-mcp console script on your PATH.
2. Add it to your agent:
Claude Code (one-liner):
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:
{
"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_faresfor "how much is BUD to LTN in November", with the full day-by-day breakdown and every departure timecheapest_dayfor "what's the cheapest day this month to fly X to Y"price_aroundfor "is it cheaper a day either side of the 10th"explore_destinationsfor "what countries can I reach from X"flight_statusfor "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.
Wizz Air vs Ryanair
If you're coming from Flyan, these are the differences that will bite you:
| Ryanair (Flyan) | Wizz Air (Flywizz) | |
|---|---|---|
| Country codes | lowercase iso2 | uppercase ISO2 |
| Auth | anonymous | session handshake + rotating CSRF token |
| Currency | currency query param |
fixed to the departure station |
| Cheap-fare search | oneWayFares with "anywhere" |
no anywhere search; fan out per route |
| Priced surface | one endpoint | timetableV2 (open) vs search/search (gated) |
| Pagination | nextPage |
none |
| Bot protection | WAF, occasional cold 403 | Kasada on four endpoints, permanent |
Caching
asset/map is 650 KB and changes rarely. Wrap the transport when you call it
more than once:
from flywizz import WizzAir, WizzairTransport, CachingTransport
client = WizzAir(CachingTransport(WizzairTransport(), ttl=3600))
CachingTransport never 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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flywizz-0.1.0.tar.gz.
File metadata
- Download URL: flywizz-0.1.0.tar.gz
- Upload date:
- Size: 36.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d11f903f62486a37dbf1966d21b238a2c29e404f0538bdcaec5fbecff809bad
|
|
| MD5 |
1b861f0f425fd0fe1b04f77fb8b552c6
|
|
| BLAKE2b-256 |
50b7fc2d3b89c001ac26a2831dc76823d1d5417e1fd4c552de8c22fab974779e
|
Provenance
The following attestation bundles were made for flywizz-0.1.0.tar.gz:
Publisher:
release.yml on victorlane/flywizz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flywizz-0.1.0.tar.gz -
Subject digest:
5d11f903f62486a37dbf1966d21b238a2c29e404f0538bdcaec5fbecff809bad - Sigstore transparency entry: 2680171887
- Sigstore integration time:
-
Permalink:
victorlane/flywizz@31544c1dac06d1357754a78b058c092211c1fe67 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/victorlane
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31544c1dac06d1357754a78b058c092211c1fe67 -
Trigger Event:
push
-
Statement type:
File details
Details for the file flywizz-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flywizz-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f84d7e9ef8f73e7e1bbc72dbd111e1c21e7389f51e3c40014d7dcdf215994b52
|
|
| MD5 |
429b46bbce5a95ec4f54e748aa58ca78
|
|
| BLAKE2b-256 |
a905be2b48e92bd0ef9b992df07eb0c839464098bda6895c9ec840fcd59ddb5b
|
Provenance
The following attestation bundles were made for flywizz-0.1.0-py3-none-any.whl:
Publisher:
release.yml on victorlane/flywizz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flywizz-0.1.0-py3-none-any.whl -
Subject digest:
f84d7e9ef8f73e7e1bbc72dbd111e1c21e7389f51e3c40014d7dcdf215994b52 - Sigstore transparency entry: 2680171910
- Sigstore integration time:
-
Permalink:
victorlane/flywizz@31544c1dac06d1357754a78b058c092211c1fe67 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/victorlane
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31544c1dac06d1357754a78b058c092211c1fe67 -
Trigger Event:
push
-
Statement type: