Skip to content

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:

Terminal window
roost search "lisbon" --backend serpapi --json
ROOST_BACKEND=serpapi roost dates "lisbon" --window 14 --json
google (default)serpapi
Authnone — keylessAPI key required
Dated availabilityyesyes
Per-night + total priceyesyes
Per-source rate breakdownbest-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 narrowingapplied server-side
Search filters (--class, --min-rating, --max-price, --free-cancellation, --amenity, --property-type, --sort)applied client-side, over whatever page Google returnedsame filters, same client-side application
Rate limitsroost’s own politeness throttle (6s min interval, burst of 2, exponential backoff on a block)governed by your SerpApi plan
Costfreebilled 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.

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
  • --brand actually narrowing results server-side, instead of just being declared unfiltered in scope
  • a rate limit governed by your own plan rather than roost’s shared politeness throttle

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:

Terminal window
echo "$SERPAPI_KEY" | roost auth login --token-stdin

This 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:

Terminal window
ROOST_SERPAPI_KEY="$SERPAPI_KEY" roost search "lisbon" --backend serpapi --json

4. Verify. roost auth status reports whether a key is present, without ever printing it in full:

Terminal window
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:

Terminal window
roost auth logout --json

roost auth login and the resolver check three places, in this order:

  1. ROOST_SERPAPI_KEY environment variable
  2. OS keyring (service roost, username serpapi-key)
  3. a 0600 file 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.