Skip to content

Quickstart

No install step, no account, no API key. uvx fetches and runs roost in one shot:

Terminal window
uvx roost search "kyoto" --check-in 2026-10-02 --nights 3 --json

That’s a real query: properties in Kyoto, checking in 2026-10-02, staying 3 nights, for the default 2 adults. roost reads this straight from Google Hotels — no login, no key.

Output shape (trimmed to one property):

{
"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": "...",
"name": "Sakura Cross Hotel Kyoto Kiyomizudera",
"type": "hotel",
"class": 3,
"rating": 4.4,
"reviews": 812,
"perNight": { "amount": 142.0, "currency": "USD" },
"total": { "amount": 426.0, "currency": "USD" },
"taxesIncluded": false,
"freeCancellation": true,
"amenities": ["Free Wi-Fi", "Air conditioning", "..."]
}
]
}

--json gives you machine output on stdout; without it, roost prints a plain-text table instead. Everything roost writes that isn’t the result — errors, warnings — goes to stderr, so stdout is always safe to pipe or parse.

If you don’t know exact dates yet — you want to know when a stay is cheapest — roost dates sweeps a window of check-in dates. It’s the one command that can issue more than one upstream request, so check the cost first with --dry-run, which fetches nothing:

Terminal window
roost dates "lisbon" --window 30 --nights 2 --dry-run
{
"schemaVersion": "1",
"query": {
"location": "lisbon",
"checkIn": "2026-08-21",
"checkOut": "2026-08-23",
"nights": 2,
"guests": { "adults": 2, "children": 0 },
"currency": "USD"
},
"scope": {
"backend": "google",
"brandsFilter": [],
"partial": false,
"note": "..."
},
"plan": {
"window": 30,
"step": 1,
"maxRequests": 8,
"requestsPlanned": 8,
"sampledDates": ["2026-08-21", "2026-08-22", "2026-08-23", "2026-08-24", "2026-08-25", "2026-08-26", "2026-08-27", "2026-08-28"]
}
}

A 30-day window at a 1-day step would be 30 requests, but --max-requests defaults to 8 and is a hard cap — the plan shows exactly which 8 dates get sampled and that the sweep is truncated. Drop --dry-run to actually run it:

Terminal window
roost dates "lisbon" --window 30 --nights 2 --max-requests 8 --json

That response replaces plan with dates (one entry per sampled date, each with available, perNight, total), plus cheapest (the lowest-priced date found), requestsIssued, and truncated.

Every read command — search, rates, dates — returns the same shape:

  • schemaVersion — currently "1". Pin your parsing to it; a bump means the shape changed.
  • query — the normalized stay you searched: dates, nights, guests, currency. Useful to confirm roost parsed your flags the way you meant.
  • scope — read this before trusting the result. It names the backend that answered, any brandsFilter you applied, whether the result is partial (narrowed — never the full corpus), and a note explaining why.
  • the collectionproperties for search, dates for dates, rates for rates.

Full field-by-field reference: Output schema.

Why the first search for a new city costs two requests

Section titled “Why the first search for a new city costs two requests”

roost search "kyoto" ... above cost two upstream requests, not one: first resolving “kyoto” to a Google place id, then fetching results for that place. That’s why the default burst allowance is 2 — one logical search legitimately needs both.

The place id is then cached for 30 days, so every repeat search for Kyoto after this one costs a single request. You’ll feel this the first time you search a new city and notice the politeness throttle account for it.