tripscout
Find the cheapest complete trip — not the cheapest flight.
A cheap flight is not a cheap trip. You find a €40 fare to Vienna, then discover the
hotel costs €70 more on those exact nights than the week before, so you look for other
dates, and the flight goes up. You switch city and the loop starts again. tripscout
prices flights and hotels together on the same dates, adds what it costs to reach
each departure airport in the first place, and ranks whole trips by the total.
Then it remembers. Every price it sees goes into a local SQLite cache, so a repeat
search costs almost nothing and you can ask whether a price is high or low for those
dates.
What it does that a flight search does not
| | |
|---|---|
| Joint search | The hotel price for a date window is fetched before the flight for that window. Google Flights shows you fares; it does not know your hotel bill. |
| Ground access per airport | Each departure airport carries an access cost (train, bus, parking, fuel, return). That is what makes Bologna, Florence and Pisa actually comparable instead of nominally comparable. |
| Budget-driven pruning | If the cheapest suitable hotel plus the cheapest possible ground access already breaks your budget, the flight requests for that combination are never made. Exploration is spent where it can still win. |
| Total-cost ranking | The answer is one number per option — flight × travellers + stay + access + airport transfer — with the breakdown, so you can see whether a city is expensive because of the flight or the bed. |
| Lodging types | Restrict to hostel, hotel or apartment. The category comes from Google's own entity type (gcid:), not from the property's name, so a hostel branded "hotel" is still a hostel. |
| City, not airport | Say home: "Prato" and it is resolved to every airport serving it — Florence, Pisa, Bologna — each with the estimated ground cost of getting there and back. 33 departure cities and 49 destinations ship with the plugin. |
| A Desktop pane | The same engine behind a real UI in Hermes Desktop: pick the city you leave from, tap destinations, filter by kind of bed, see each trip's total and breakdown. |
| Daily cost estimate | Optionally add approximate food and attraction money for the days you are there, as a second total that never contaminates the bookable one. |
| Local price history | Every observation is recorded. "Is 144 € good for a Thursday-to-Saturday in Budapest in November?" becomes a question with an answer. |
Install
hermes plugins install <owner>/tripscout
hermes plugins enable tripscout
Python dependencies are declared in plugin.yaml (fast-flights, primp,
typing_extensions) and are prepared by Hermes at install time with your consent.
If they are missing, the plugin still loads and the tools tell you what to install.
No API keys. No account. No browser.
Tools
tripscout_search
The main one. Give it the airports you can reach, the cities you would consider, and a
date window; it prices the whole grid and returns a ranking.
{
"origins": [
{"city": "Bologna", "airports": ["BLQ"], "access_cost": 18},
{"city": "Florence", "airports": ["FLR"], "access_cost": 12},
{"city": "Pisa", "airports": ["PSA"], "access_cost": 20}
],
"destinations": [
{"city": "Vienna", "airports": ["VIE"], "country": "AT", "transfer_cost": 4.5},
{"city": "Budapest", "airports": ["BUD"], "country": "HU", "transfer_cost": 3.5}
],
"window_start": "2026-11-19",
"window_end": "2026-11-22",
"nights_min": 2, "nights_max": 3,
"weekdays": ["giovedì", "venerdì"],
"adults": 1, "budget_total": 400, "max_requests": 60
}
Returns the best option, a ranked list with the cost breakdown, and the best option
per destination. A real run of exactly the request above:
Budapest (HU) 2026-11-19 → 2026-11-21 (2 notti) from Pisa PSA 144 EUR
volo 77 + hotel 40 (Flow Spaces) + adduzione 20 + transfer 7
Vienna (AT) 2026-11-20 → 2026-11-22 (2 notti) from Bologna BLQ 255 EUR
volo 156 + hotel 72 (Suburb Hostel) + adduzione 18 + transfer 9
max_requests is a real budget: the sweep stops when it runs out and says the answer
is partial rather than pretending it finished.
Lodging: hotels, hostels, apartments
lodging restricts what counts as a bed. ["hostel"] is the cheap-weekend answer:
{"lodging": ["hostel"], "estimates": true}
Budapest (HU) 2026-11-19 → 2026-11-21 (2 notti) from Pisa PSA 192 EUR [hostel]
Budapest Party Hostel at U… prenotabile 192 + cibo 54 + attrazioni 21 = 267 EUR
Vienna (AT) 2026-11-19 → 2026-11-21 (2 notti) from Bologna BLQ 247 EUR [hostel]
Suburb Hostel prenotabile 247 + cibo 78 + attrazioni 30 = 355 EUR
The category is read from Google's entity type on each result (gcid:hostel,
gcid:capsule_hotel, gcid:motel, …), not guessed from the name — which matters,
because chains like a&o and MEININGER call themselves hotels while being hostels.
Apartments come from a second query: Google exposes them under "vacation rentals", so
asking for apartment costs one extra request per city and date. Hotels and hostels
arrive together from a single query.
Leaving from a city, not an airport
Nobody thinks in IATA codes. Pass the town and the plugin works out the rest:
{"home": "Prato", "destinations": ["Budapest", "Praga", "Vienna"]}
Prato resolved to FLR ~9 PSA ~16 BLQ ~24 EUR (ground, return)
Thirty-three departure cities ship with it (Prato, Firenze, Pisa, Bologna, Milano, Roma,
Napoli, Palermo, Torino, Venezia, Bari, Cagliari, Zurigo, Monaco…), each with the
airports that serve it and a return-trip ground cost per person: the train, the bus, the
shuttle. That number is what makes three airports comparable at all — the €16 to Pisa is
part of the trip, not a footnote. Every cost is an estimate for cheap travel and can be
overridden by passing origins yourself. tripscout_origins lists them, or resolves one.
Destinations work the same way: ["Vienna"] is enough, and 49 European cities are
already mapped to their airports, country and airport-to-town transfer cost. Anything
outside the table still works — pass {city, airports, country, transfer_cost}.
The Hermes Desktop pane
The plugin also ships a UI half (desktop/plugin.js + dashboard/plugin_api.py), so the
same engine is usable without typing a query: a pane where you pick the city you leave
from, tap destinations by region, choose any / hostel / hotel / apartment, set nights
and a budget ceiling, and press search. Results arrive as a ranked list; click one for
the line-by-line breakdown and the all-in estimate with food and tickets.
It is the same code path as the tools — the pane calls the engine, not a copy of it — so
what the UI shows and what the agent answers cannot drift apart. The pane is opt-in: flip
it on in Settings → Plugins after installing (hermes plugins enable tripscout for
the backend half).
Food and attractions (optional, approximate)
estimates: true adds, per option, what eating and getting into places roughly costs:
| | |
|---|---|
| food | per person per day, from a per-country table (Poland 16, Hungary 18, Czechia 20, Austria 26, Norway 44 …) with a few dearer cities overridden |
| attractions | per person per day, per country (default 9) |
| days | nights + 1 — you eat lunch on the day you land and on the day you leave |
Both can be overridden with food_per_day and activities_per_day. The estimate is
deliberately a cheap-travel budget: breakfast and lunch at a bar or supermarket, one
simple dinner, one paid attraction a day. It never enters the ranking or the
budget_total check — total stays the bookable cost and total_with_estimates is the
honest all-in figure next to it. Nothing here is a quote; the tool says so in its notes.
tripscout_price_trip
One exact trip, end to end: cheapest flight for those dates, cheapest hotel for the
same nights, every line of the total, plus the next five hotels as alternatives.
tripscout_history
Reads the local cache only — no network. Price observations for a route or a stay,
the lowest ever seen, and how much is cached.
Schedules: turn it into a standing search
Point a cronjob at it and let the price history do the work:
Every day at 08:00, run tripscout_search for my usual airports and cities two weekends
out, and message me only if some option's total is below 200 EUR or is lower than any
total recorded so far for that combination.
The cache makes the daily version cheap, and tripscout_history is what makes "lower
than anything seen so far" a real comparison instead of a guess.
How the data is fetched
Both sources are Google Travel's public web pages, read over plain HTTPS with a
browser TLS fingerprint (primp, Chrome impersonation). No API key is involved, which
is the point: there is no free, open flight-and-hotel API left — Amadeus shut its
self-service tier in July 2026 — and the ones that exist are per-route, per-date, and
priced for businesses.
- Flights — Google Flights.
fast-flightsbuilds thetfsquery protobuf and
parses the results page; the transport is this plugin's own, because it must carry
the consent cookie below. - Hotels — Google Hotels'
batchexecuteRPC (AtySUc), implemented here: dates,
occupancy and currency are encoded in the request, and the response gives the
stay total for the nights asked for, which is the quantity that actually adds to
the flight. - Consent — in the EU Google answers with the "Before you continue" interstitial
instead of results. The plugin sends theSOCScookie declaring essential cookies
only, which is what a rejection of non-essential cookies looks like. It never sends
a consent value that accepts tracking.
Disclosure, for the catalog and for you. Searches send the cities, airports, dates,
traveller count and currency to Google; nothing else about you is included, and no
credential or personal data is ever read, stored or transmitted. Prices, hotel names and
cache keys are stored locally in SQLite under Hermes' own plugin data directory
(plugin-data/tripscout/). Nothing is booked and no purchase is ever initiated —
tripscout prices and returns links-free numbers, it does not reserve.
Known limitations
- The flight price floor is Google's default ranking, not its "cheapest" list. The
page contains cheaper fares than the parser extracts on some routes, so a route served
by low-cost carriers can look more expensive than it is. On many routes the numbers
are right (the run above prices Pisa→Budapest at 77 € round trip); on some they are
too high. Treat the flight component as an upper bound and the ranking, which
compares like for like across all options, as the reliable part. Parsing the cheapest
tab properly is the next piece of work. - One request per route and date pair. Google has no bulk endpoint for this, so a
wide grid costs a request per cell and is rate-limited by design. The cache and the
pruning exist precisely because of this; raisemin_intervalif you see blocks. - Hotels are the cheapest suitable stay for the dates, which is the right default
for "spend little" but is not a hotel you would necessarily want. - Prices are snapshots, not quotes. Nothing here is bookable state.
Development
python tests/test_offline.py # 10 tests, no network
hermes plugins validate . --install-deps
hermes plugins doctor .
The tests cover the date grid, the weekday parsing, the hotel request encoding, the
price extraction, the cache and history, and — the one that matters most — that a
combination whose hotel alone breaks the budget never spends a flight request.
Licence
MIT.
Prior art: viajante (MIT) demonstrated
that Google Flights and Google Hotels can be read without keys from the user's own
machine. The consent-cookie approach and the position of the hotel price in the
AtySUc response were confirmed against its published source; the code here is an
independent implementation of the same public protocol.
README from the reviewed commit 3a0e1e1; it updates when the author re-pins.

marcofailli