Searching availability
roost search is the core command: what’s available in a location, for a stay, at what rate.
roost search "kyoto" --check-in 2026-10-02 --nights 3 --json{ "schemaVersion": "1", "query": { "location": "kyoto", "checkIn": "2026-10-02", "checkOut": "2026-10-05", "nights": 3, "guests": { "adults": 2, "children": 0 }, "currency": "USD" }, "scope": { "backend": "google", "brandsFilter": [], "partial": false, "note": "lead-in rates from Google Hotels metasearch; not a booking guarantee — confirm on the property's own site" }, "properties": [ { "id": "CgoI3Ni2rIz7-6BSGgkyNXpycmR3NncQAQ", "name": "Sakura Cross Hotel", "type": "hotel", "class": null, "rating": 4.9, "reviews": 2500, "nights": 3, "perNight": { "amount": 92.0, "currency": "USD" }, "total": { "amount": 304.0, "currency": "USD" }, "taxesIncluded": true, "deal": { "percentLessThanUsual": 60, "label": "GREAT DEAL" }, "amenities": ["Sauna", "Free Wi-Fi", "Parking"], "url": "https://www.google.com/travel/hotels/entity/CgoI3Ni2rIz7-6BSGgkyNXpycmR3NncQAQ" } ]}Location strings
Section titled “Location strings”location is free text — a city, a neighborhood, whatever you’d type into Google Hotels
yourself: "kyoto", "chicago", "lisbon, portugal". roost resolves it to a Google place id
on the first request and caches that id for 30 days, so repeat searches for the same city cost
one upstream request instead of two. If the location can’t be resolved to a place, the command
fails with exit code 5 (not found).
Setting dates
Section titled “Setting dates”Two ways to say how long you’re staying, and one rule for when you give both:
# nights (default 2) — check-out is computedroost search "chicago" --check-in 2026-11-10 --nights 3 --json
# explicit check-out — wins over --nights when both are givenroost search "chicago" --check-in 2026-11-10 --check-out 2026-11-14 --json--check-in defaults to today if omitted. If you pass both --check-out and --nights,
--check-out wins — --nights is silently ignored, not an error. --check-out must be after
--check-in, or the command fails with exit code 2 (usage).
Guests and currency
Section titled “Guests and currency”roost search "lisbon" --adults 4 --children 2 --currency EUR --json--adults defaults to 2 and must be at least 1. --children defaults to 0. --currency is an
ISO code, default USD — it’s normalized to uppercase and drives the Money shape in every
result ({"amount": ..., "currency": ...}), never a bare number.
Filters
Section titled “Filters”Every filter below runs client-side on the google backend: roost fetches one page of
results from Google Hotels and narrows/sorts that page in-process. It does not search deeper
or issue another request per filter. Whenever a filter narrows the set, the envelope’s
scope.partial is set to true and scope.note explains why — so an agent never mistakes a
filtered page for the whole corpus.
--class — minimum hotel star class:
roost search "chicago" --class 4 --json--min-rating — minimum guest rating:
roost search "chicago" --min-rating 4.5 --json--max-price — maximum nightly rate, compared against perNight.amount:
roost search "chicago" --max-price 200 --json--free-cancellation — only properties where freeCancellation is true:
roost search "chicago" --backend serpapi --free-cancellation --jsonSorting
Section titled “Sorting”--sort orders the (already filtered) result set: relevance (default, Google’s own order),
price, rating, or reviews.
roost search "chicago" --class 4 --min-rating 4.5 --sort price --jsonBounding output
Section titled “Bounding output”Two global flags, useful once a query returns more rows than you want to read:
# cap the number of rowsroost search "chicago" --limit 5 --json
# project down to specific fields (dot-path, comma-separated)roost search "chicago" --select name,perNight.amount,rating --json--limit defaults to 50; when it truncates the list, a note goes to stderr (note: output truncated to N of M items (use --limit to change)) so nothing is dropped silently. --select
takes comma-separated
dot-paths and applies per-row — pass perNight.amount to reach inside the Money object rather
than getting the whole thing.
In plain or tsv format (the default is plain), search prints the properties rows
directly as a table, not the envelope — the scope.note still goes to stderr so you don’t lose
it, and --select’s dot-paths become the column headers:
roost search "chicago" --limit 3 --select name,perNight.amount,ratingscope: lead-in rates from Google Hotels metasearch; not a booking guarantee — confirm on the property's own sitename perNight.amount ratingThe Drake Hotel 189.0 4.6...Without --select, the table falls back to every field present across the rows, alphabetized —
readable, but wide. Money fields (perNight, total) that aren’t projected down to a
sub-path render as their raw JSON object in the cell.
Reading the property fields
Section titled “Reading the property fields”Each row in properties is one card from the Google Hotels results page:
| field | meaning |
|---|---|
id | Google entity token — pass this (or name) as the property argument to roost rates |
name | property name, sanitized (control characters stripped, whitespace collapsed, capped at 200 chars unless --no-wrap-untrusted) |
type | hotel or vacation_rental — the two are priced and cancelled very differently, so filter on this before comparing rates |
class | star class, 1-5, or null if Google didn’t state one |
rating / reviews | guest rating and review count, or null |
nights | the stay length Google’s own card text states |
perNight / total | Money objects — always {"amount": ..., "currency": ...}, never a bare number or formatted string, and never interchangeable |
taxesIncluded | whether the stated price already includes taxes and fees |
deal | {"percentLessThanUsual": N, "label": "GREAT DEAL" or "DEAL"}, or null |
amenities | up to 12 sanitized strings |
url | the property’s Google Hotels page |
nightsMismatch | only present, and true, when Google’s card states a different stay length than the one you asked for — see below |
nightsMismatch: true means don’t trust perNight/total on that row for your exact dates.
Google occasionally echoes a card for a different number of nights than you requested; roost
does not silently relabel it as your stay — it flags the row instead so an agent can discard
it or re-query. Absence of the field means no mismatch was detected, not that it was checked
and confirmed.
To go deeper on one property’s per-source rate breakdown, see comparing rates across backends. To sweep a date range for the cheapest check-in, see finding cheap dates.