Choosing a backend
roost talks to two backends. google is the default — no account, no key, nothing to set
up. serpapi is opt-in and needs an API key, but sees more of what Google Hotels actually
knows. Both speak the same command grammar and emit the same envelope shape; only scope
and how much of it is filled in changes.
Pick one with --backend or the ROOST_BACKEND environment variable:
roost search "lisbon" --backend serpapi --jsonROOST_BACKEND=serpapi roost dates "lisbon" --window 14 --jsonComparison
Section titled “Comparison”google (default) | serpapi | |
|---|---|---|
| Auth | none — keyless | API key required |
| Dated availability | yes | yes |
| Per-night + total price | yes | yes |
| Per-source rate breakdown | best-effort from the search surface only (scope.partial: true) | full per-source list |
Server-side brand filtering (--brand) | not applied — Google’s brand filter ids aren’t exposed on this surface; results still span all chains, scope declares the narrowing | applied server-side |
Search filters (--class, --min-rating, --max-price, --free-cancellation, --amenity, --property-type, --sort) | applied client-side, over whatever page Google returned | same filters, same client-side application |
| Rate limits | roost’s own politeness throttle (6s min interval, burst of 2, exponential backoff on a block) | governed by your SerpApi plan |
| Cost | free | billed per SerpApi search |
Why rates is partial on google: the property page that carries the full rate matrix is
client-rendered, and the data comes over an RPC roost declines to call. The google backend
answers from the search surface instead and is upfront about it — every response sets
scope.partial: true with a note pointing at --backend serpapi. roost brands on google
returns a curated chain list with id: null for the same reason: Google’s brand filter ids
are only exposed through SerpApi.
When to switch
Section titled “When to switch”Stay on google for exploratory search, price-checking, and date sweeps — it is the
keyless default and the headline feature. Reach for --backend serpapi when you need:
- the full per-source rate breakdown on
rates(brand-direct vs. every OTA), not the best-effort subset --brandactually narrowing results server-side, instead of just being declared unfiltered inscope- a rate limit governed by your own plan rather than roost’s shared politeness throttle
Setting up serpapi
Section titled “Setting up serpapi”1. Get a key. Sign up at SerpApi and copy your API key from their dashboard.
2. Store it. Secrets go through stdin or the environment — never argv, where they’d
leak into ps, /proc, shell history, and an agent’s own command log:
echo "$SERPAPI_KEY" | roost auth login --token-stdinThis stores the key in your OS keyring. If no keyring backend is available, it falls back
to a 0600 file instead — roost auth login warns you if the file’s permissions can’t be
locked down to 0600.
3. Or use an env var, e.g. for CI. ROOST_SERPAPI_KEY is checked before the keyring or
the file, so setting it is enough on its own — no auth login step needed:
ROOST_SERPAPI_KEY="$SERPAPI_KEY" roost search "lisbon" --backend serpapi --json4. Verify. roost auth status reports whether a key is present, without ever printing
it in full:
roost auth status --json{ "defaultBackend": { "name": "google", "authenticated": true, "method": "none", "note": "the google backend is keyless — no account, no API key" }, "serpapi": { "authenticated": true, "method": "api_key", "key": "…a1b2", "note": null }, "keyringAvailable": true}5. Logout when you’re done. roost auth logout is idempotent and only removes the
local copy — it does not revoke the key upstream:
roost auth logout --jsonWhere the key lives
Section titled “Where the key lives”roost auth login and the resolver check three places, in this order:
ROOST_SERPAPI_KEYenvironment variable- OS keyring (service
roost, usernameserpapi-key) - a
0600file at$XDG_CONFIG_HOME/roost/credentials(falls back to~/.config/roost/credentials)
The env var wins if set, which is why it’s the right choice for CI: no keyring to
provision, no file to manage, just a secret in the environment the way every other CI tool
expects. Without a key, any serpapi-backed command fails fast with exit code 4
(AUTH_REQUIRED) rather than falling back to google silently.
For the full envelope shape both backends emit, see the
output schema reference. For exit codes like AUTH_REQUIRED
and RATE_LIMITED, see exit codes.