How I Built a Budget Travel Research Pipeline with Open-Source Tools
How I built a flexible flight + accommodation search pipeline using fli and trvl — without API keys.
June 6, 2026

How I Built a Budget Travel Research Pipeline with Open-Source Tools

June 6, 2026 · 8 min read
open-sourceaimcptravelautomation
Download MDX

The problem

Google Flights has a flexible dates view, but it only lets you pick fixed duration categories — “weekend,” “one week,” “two weeks.” There’s no way to say minimum 5 days, maximum 7 and get all the cheapest roundtrip combinations across a date window, sorted by price. I wanted exactly that.

There’s a second awkwardness: cities with more than one airport. Milan has three (Bergamo BGY, Malpensa MXP, Linate LIN), and the cheapest gateway changes by route and by date. Checking each one by hand is tedious — a pipeline can search all of them and rank the results together.


Finding fli

fli is a Python library by Punit Arani that reverse-engineers Google Flights’ internal endpoints. Install it with pip install flights and you get a CLI (fli dates) and a built-in MCP server. fli dates returns the cheapest roundtrip combinations in a date window — but had no concept of minimum or maximum stay length.


Building the feature

I forked the repo to RobertoReale/fli and implemented --min-duration and --max-duration flags for fli dates, applying the same logic to the MCP search_dates tool so an AI agent can call it with duration constraints too.

The changes touched five files — fli/cli/commands/dates.py, fli/mcp/server.py, two test files, and the README — adding parameter validation and wiring the constraints to filter results outside the duration window. A review pass from a bot called Greptile flagged a missing lower-bound check and an uninitialized variable, which I fixed. The final version went to branch feature/window-duration and is open as PR #195 upstream.

A second contribution tackled a different limitation. fli’s existing --time / departure_window filter applied the same time-of-day window to both legs of a round trip — asking for a morning departure forced a morning return too. I added --return-time (-T) and the matching return_departure_window MCP parameter to decouple them, so you can request a morning outbound and an evening return independently. The core change lives in fli/core/builders.py, using a three-state sentinel — inherit the outbound window, drop the return filter entirely, or set an explicit one — wired through the flights and dates commands and the search_flights / search_dates / get_booking_options MCP tools. It’s open separately as PR #196. The pipeline below uses the duration work; the return-time filter is there when a specific itinerary needs it.


Using it: The AI agent

With fli’s MCP server registered in the AI agent configuration, I used the agent to directly call the search tool — not to write code, but to orchestrate tool calls. This is the prompt that kicked off the first search:

My pull request to this fork has added --min-duration and --max-duration support. Use the fli MCP server to find the cheapest roundtrip flights from Milan:

— Departure window: July 22 – August 1 (so a 5-night trip returns by August 6)
— Stay duration: 5 nights (--min-duration 5 --max-duration 5; set different values for a range)
— Departure airports: MXP (Malpensa), BGY (Bergamo), LIN (Linate) — search each separately
— Destinations: Naples, Barcelona, Corfu, Brindisi (Salento), Split
— Only include departures before 4 PM
— Sort by cheapest roundtrip price
— At the end, suggest other destinations to check in the future

The agent runs a fli dates command for every origin–destination pair, collects the JSON output, and returns a ranked table with prices, optimal dates, and direct Google Flights booking links. The window is for departures — with a 5-night stay, the latest July-22-to-August-1 departure returns by August 6. The cheapest results from an actual run of this search:

#RouteRoundtrip priceDepart → ReturnNights
1BGY → Barcelona€46Jul 25 → Jul 305
2BGY → Naples€47Jul 30 → Aug 45
3MXP → Naples€47Jul 29 → Aug 35
4MXP → Barcelona€49Aug 1 → Aug 65
5MXP → Corfu€52Jul 26 → Jul 315

Two things to read correctly. The price is the full roundtrip fare, not one leg — €46 covers BGY→Barcelona and Barcelona→BGY, and the booking link opens that roundtrip. And it’s the fare for a single adult: fli doesn’t take a passenger count, so for two travellers you double it. Searching all three Milan airports is what surfaces the cheap gateways — here Bergamo and Malpensa take every top slot, while Linate (the legacy city airport, no low-cost carriers) came back two to three times more expensive on the same routes and never made the cut. Note also that every option departs from and returns to the same airport: fli models a roundtrip as one airport pair, so it can’t build an open-jaw (out of Bergamo, back into Malpensa) — searching each airport just tells you which single gateway is cheapest.


Expanding and exporting

I then asked the agent to broaden the search and export everything:

Run the same search for all the future destinations you suggested. Then export everything to my desktop as a complete structured file — full ranking with dates, prices, and booking links.

The agent widens the destination set, re-runs the per-route searches, and exports two files: a complete planning document (every route with per-route date breakdowns, prices, and booking links) and — at my explicit request — a technical reference guide summarizing the tool’s command syntax and flags. The reference file gives the agent a ready cheat-sheet, so future searches don’t need to re-derive how the tool works. Both are just the structured output of the same two-step loop: ask fli for ranked dates, have the agent collect and format them.


Extending to accommodation: trvl

Flights are half the equation. I went looking for an accommodation equivalent and chose MikkoParkkola/trvl: a single Go binary that searches six accommodation sources at once — Google Hotels, Trivago, Airbnb, Booking.com, Hostelworld, HomeToGo — with no API keys. Same philosophy as fli, structured platform data callable by an agent, but it competes across the whole market instead of a single platform. (Airbnb-only MCP servers exist; for budget search, pricing one platform leaves too much on the table — a Booking.com hotel or a Hostelworld room often undercuts the cheapest Airbnb.)

Configuration adds it alongside fli:

{
  "mcpServers": {
    "fli": { "command": "fli-mcp" },
    "trvl": { "command": "trvl", "args": ["mcp"] }
  }
}

With both fli and trvl configured as MCP servers, the agent can call them together without any custom glue code. The prompt:

Using both fli and trvl: for each of the top flight options, search accommodation for 2 people, maximum €150 per night. Give me a final ranking by total vacation cost — flight plus accommodation — with booking links for both.

The agent handles the rest: calling both servers, combining the outputs, returning a report ranked by total trip cost.

One caveat that turns out to matter a lot: the accommodation prices trvl returns for free are Google Hotels teaser rates, not bookable quotes — and they can be wildly optimistic (in testing, a hotel shown at €46/night was actually €269 at checkout). Part 2 covers how to get verified, bookable prices instead. Treat the numbers from this raw version as a first pass, not a booking decision.

Step 1 — fli MCP
  search_dates(origin=BGY, dest=NAP, from=Jul22, to=Aug1, min=5, max=5)
  → ranked date combinations with roundtrip flight prices

Step 2 — trvl MCP
  search across 6 sources (location=Ischia, checkin=Jul30, checkout=Aug4, guests=2)
  → listings with nightly prices and total stay cost

Step 3 — Agent
  → report ranked by total vacation cost (flight + accommodation)

Caveats

All of these tools reverse-engineer internal endpoints that can change without notice. Using them sits in a legal grey area — most platforms prohibit automated access in their ToS. Use them for personal research. On Windows, fli requires PYTHONIOENCODING=utf-8 and --format json to avoid encoding errors.

One more thing worth noting: LLMs are non-deterministic. If you run the same agent prompt, the model may structure its tool calls differently, query a different subset of destinations, or occasionally skip a route. The prompts here worked well in testing, but expect to iterate — small adjustments in wording can meaningfully change how the model orchestrates the search.

Sources

  1. RobertoReale/fli — fork with --min-duration / --max-duration
  2. PR #195 on punitarani/fli — duration-window pull request
  3. PR #196 on punitarani/fli — separate return-leg time filter
  4. MikkoParkkola/trvl — Go binary, flights + 6 hotel sources
  5. pyairbnb on PyPI — Python library for Airbnb search
  6. heskarioth/py-booking.com — Python scraper for Booking.com