CLI utility for browsing Amadeus seat-map availability across a set of long-haul trips. It pulls live data from the Amadeus Self-Service APIs and caches each day's responses under data/<YYYYMMDD> so you can re-render the output without hitting the API again.
- Builds ASCII seat maps with wide-character awareness so layouts stay aligned even when using emoji markers.
- Highlights the lowest fare per route with a green border so deals stand out immediately.
- Caches availability, pricing, and seatmap responses by run date (
data/<YYYYMMDD>) for quick offline replays. - Prints a week-by-week grid plus per-day window-seat summaries with optional pricing pulled from flight offers.
- Generates calendar heatmaps (per route and round-trip) to make fare trends obvious when scanning many dates.
- Python 3.11+ (tested locally with 3.11)
pip install -r requirements.txt- Amadeus Self-Service API credentials (test and/or production)
- Pillow (for PNG export; included in
requirements.txtso GitHub Actions can commit PNGs)
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
cp .env.template .envFill in the .env file with the credentials from your Amadeus developer account:
TEST_AMADEUS_CLIENT_ID=...
TEST_AMADEUS_CLIENT_SECRET=...
AMADEUS_CLIENT_ID=...
AMADEUS_CLIENT_SECRET=...
The script reads these via python-dotenv, so the .env file only needs to exist in the repo root.
Most runtime knobs live in config.py, so you rarely need to edit flight_search.py directly. Notable settings:
ENVIRONMENTpicks the Amadeus host and must be eitherproductionortestfor the fetch scripts.TRAVEL_WINDOWSlists the routes/date ranges that should be fetched and rendered.FLIGHT_SEARCH_FILTERSstores the request arguments (travel_class,non_stop,included_airline_codes, etc.) passed to the Amadeusflight_offers_searchendpoint.- Visual toggles such as
SEATMAP_OUTPUT_STYLE,SHOW_SEATMAP_PRICE,HEATMAP_EMPHASIS_STYLES,STATUS_SYMBOLS, andWINDOW_AVAILABLE_SYMBOLcontrol how the ASCII/emoji output looks. - Currency/price decoration (
CURRENCY_SYMBOLS,BORDER_COLORS, etc.) reference the semantic tokens defined incolors.py; extendcolors.TOKEN_MAPif you need custom ANSI sequences.
- Seatmap pipeline – Manually runnable via GitHub Actions (
Run Seatmap Pipeline). It installs deps, runsrun_pipeline.py, and commits the refreshed PNGs indocs/back to the repo. - Static site deploy –
Deploy static content to Pagespublishes the contents ofdocs/to GitHub Pages at https://robertzaufall.github.io/amadeus-seatmap-bc/. It triggers onmasterwhen files indocs/**/*.pngordocs/**/*.htmlchange, and can also be dispatched manually. - Local runs still work the same way; the automation just keeps the hosted artifacts current.
Define the routes and date ranges you care about by editing the TRAVEL_WINDOWS list inside config.py. Each window is inclusive, so the scripts request seat maps for every day in the range. The first two windows drive the combined round-trip heatmaps (outbound vs. return).
Flight-offer search filters live in config.FLIGHT_SEARCH_FILTERS, so you can tweak cabin class, airline, or connection rules without touching the main script. Any key/value pairs in this dict get splatted into the SeatMaps.fetch call.
- Configure
config.py(setENVIRONMENTtotestorproduction, defineTRAVEL_WINDOWS, adjustFLIGHT_SEARCH_FILTERS). - Load environment variables from
.env(see above). - Fetch and cache data for today's run:
Each script reuses the existing files for today's date unless you flip
python get_availability.py # builds travel_dates.json + availability_responses.json python get_prices_oneway.py # per-leg pricing -> pricing_responses_*oneway*.json python get_prices_return.py # round-trip pricing -> pricing_responses*.json python get_seatmaps.py # seat-map payloads -> seatmap_responses.json
refresh_data = Truenear the top of the file. - Render the reports from the cached data:
python flight_search.py
Use flight_offer_seatmaps.py to fetch seatmaps for a single flight offer without running the full pipeline. It searches /v2/shopping/flight-offers, requests seatmaps for the result, and caches everything under data/flights/<ENV>-<FROM>-<TO>-<DATE>-<TIME>-<CLASS>-<AIRLINE>-<CURRENCY>/.
Example (queries business class; omit --class to fetch both cabins):
python flight_offer_seatmaps.py \
--date 2024-12-05 \
--time 22:25 \
--from BKK \
--to FRA \
--airline TG \
--class BUSINESS \
--currency EURNotes:
- Seatmaps print to the terminal and save as
seatmaps.pngplus JSON files (flight_offers.json,seatmaps.json,metadata.json) in the cache folder. - Seatmaps are fetched when the flight-offer search yields exactly one matching offer per requested cabin; tweak
--max-offersor specify--classif you get multiple hits. - Cached responses stay fresh for 4 hours by default (
--cache-ttl-hours 0or--force-refreshto bypass); diffs for seatmaps write toseatmaps.diffwhen content changes. - Use
--environment test|productionto select the Amadeus host; credentials are read from.envjust like the main pipeline.
All generated API responses are timestamped by run date: data/<YYYYMMDD>/. Common files include:
travel_dates.json: cleaned list of dates per travel window (from availability checks).availability_responses.json/unavailable_flights.json: raw availability responses and filtered-out flights.pricing_responses_oneway.jsonandpricing_responses_simple_oneway.json: leg-level pricing used for labels and price lookups.pricing_responses.jsonandpricing_responses_simple.json: round-trip pricing used to build seatmap requests.seatmap_responses.json: raw seatmap payloads consumed byflight_search.py.
If you want to re-render a prior run, copy its dated folder to today's date inside data/ so flight_search.py can find the JSON files.
- Weekly seat-map grid – One block per day, grouped by week. Missing data shows a
NO DATAplaceholder, and the absolute lowest fare per route gets a green border plus a rounded price label at the bottom of the block. - Route availability boxes – After the grid, every route receives a bordered box that lists the available window seats per date, prefixed with a relative fare symbol and paired with a mini calendar heatmap for additional visual context.
- Round-trip price heatmaps – If you provide at least two travel windows (outbound + inbound), the script prints two combined matrices: one based on window-seat fares only and another that considers any price returned by the offer search.
No special tooling is required beyond standard linting/formatting for Python scripts. Please keep fixtures anonymized and avoid committing sensitive traveler data.
This project is released under the MIT License. See LICENSE for the full text.

