diff --git a/.env.example b/.env.example index 1bbc564..dfc9aec 100644 --- a/.env.example +++ b/.env.example @@ -32,8 +32,12 @@ AWARDWALLET_USER_ID= # Free tier (100 searches/mo) at https://serpapi.com SERPAPI_API_KEY= -# RapidAPI - Booking.com hotel prices -# Free tier (100 req/month) at https://rapidapi.com +# RapidAPI - Google Flights fares + Booking.com hotel prices +# Free key at https://rapidapi.com/auth/sign-up +# You must ALSO subscribe to each API separately (free BASIC plan), or calls return 403: +# https://rapidapi.com/mtnrabi/api/google-flights-live-api +# https://rapidapi.com/mtnrabi/api/booking-live-api +# Quotas are per API, not per account. RAPIDAPI_KEY= # LiteAPI - Hotel search with live rates and booking (remote MCP server) diff --git a/CLAUDE.md b/CLAUDE.md index 633ec1f..010d641 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -171,7 +171,7 @@ If you change skills, CLAUDE.md, or MCP config, run `bash scripts/smoke-test.sh` - Seats.aero data is cached, not live. Check `ComputedLastSeen` for freshness. Stale data (24h+) means verify on the airline site before booking. - Always search for 2+ seats when booking for multiple people. Award availability for 1 seat doesn't guarantee 2. -- RapidAPI free tier is 100 requests/month. Use sparingly. Prefer SerpAPI. +- RapidAPI quotas are per API, not per account, and each API needs its own free subscription on top of the key. A key with no subscription returns 403, not 401. The flights free tier is 10 requests/month. Use sparingly. Prefer SerpAPI. - Atlas Obscura and Airbnb scrape websites. Be respectful with request volume. - Skiplagged, Kiwi.com, Trivago, and Ferryhopper need no setup. They just work. - Ferryhopper focuses on European/Mediterranean routes. Great for Greek islands, Croatia, Scandinavia. diff --git a/agents/travel-hacker.md b/agents/travel-hacker.md index 633ec1f..010d641 100644 --- a/agents/travel-hacker.md +++ b/agents/travel-hacker.md @@ -171,7 +171,7 @@ If you change skills, CLAUDE.md, or MCP config, run `bash scripts/smoke-test.sh` - Seats.aero data is cached, not live. Check `ComputedLastSeen` for freshness. Stale data (24h+) means verify on the airline site before booking. - Always search for 2+ seats when booking for multiple people. Award availability for 1 seat doesn't guarantee 2. -- RapidAPI free tier is 100 requests/month. Use sparingly. Prefer SerpAPI. +- RapidAPI quotas are per API, not per account, and each API needs its own free subscription on top of the key. A key with no subscription returns 403, not 401. The flights free tier is 10 requests/month. Use sparingly. Prefer SerpAPI. - Atlas Obscura and Airbnb scrape websites. Be respectful with request volume. - Skiplagged, Kiwi.com, Trivago, and Ferryhopper need no setup. They just work. - Ferryhopper focuses on European/Mediterranean routes. Great for Greek islands, Croatia, Scandinavia. diff --git a/plugins/travel-hacking-toolkit/skills/rapidapi/SKILL.md b/plugins/travel-hacking-toolkit/skills/rapidapi/SKILL.md index c54b8b6..d07a521 100644 --- a/plugins/travel-hacking-toolkit/skills/rapidapi/SKILL.md +++ b/plugins/travel-hacking-toolkit/skills/rapidapi/SKILL.md @@ -1,6 +1,6 @@ --- name: rapidapi -description: Google Flights Live and Booking.com Live search via RapidAPI. Secondary source for cash flight prices and hotel availability when SerpAPI needs a second opinion. +description: Live Google Flights fares and Booking.com hotel rates via RapidAPI. Needs a free RapidAPI account key plus a separate free subscription to each of the two APIs, otherwise every call returns 403. Secondary source for cash flight prices and hotel availability when SerpAPI needs a second opinion. Trigger phrases like RapidAPI flight search, Booking.com hotel prices, second price opinion, live Booking.com room rates. category: hotels summary: Booking.com hotel prices. api_key: RapidAPI @@ -9,51 +9,61 @@ license: MIT # RapidAPI Skill -Search Google Flights and Booking.com via RapidAPI scrapers. Secondary source for cash flight prices and hotel/vacation rental pricing. +Search Google Flights and Booking.com via RapidAPI. Secondary source for cash flight prices and hotel pricing. **Sources:** -- [Google Flights Live API on RapidAPI](https://rapidapi.com/apiheya/api/google-flights-live-api) -- [Booking.com Live API on RapidAPI](https://rapidapi.com/apiheya/api/booking-live-api) +- [Google Flights Live API on RapidAPI](https://rapidapi.com/mtnrabi/api/google-flights-live-api), host `google-flights-live-api.p.rapidapi.com` +- [Booking Live API on RapidAPI](https://rapidapi.com/mtnrabi/api/booking-live-api), host `booking-live-api.p.rapidapi.com` ## Authentication -`RAPIDAPI_KEY` is set in `.env`. All requests use `x-rapidapi-key` header. +Two separate things are required, and missing the second one is the usual reason this skill appears broken: + +1. **A RapidAPI key.** Sign up free at [rapidapi.com](https://rapidapi.com/auth/sign-up), then copy the key from any API's **Endpoints** tab (`X-RapidAPI-Key`). Put it in `.env` as `RAPIDAPI_KEY`. +2. **A subscription to each API, individually.** RapidAPI quotas are per API, not per account. Open each listing above, click **Subscribe to Test**, and pick the free **BASIC** plan. Subscribing to one does not subscribe you to the other. + +Every request sends both `x-rapidapi-key` and `x-rapidapi-host`. The host header must match the host in the URL. + +Check the key is loaded before calling: + +```bash +echo "${RAPIDAPI_KEY:+set}${RAPIDAPI_KEY:-unset}" +``` ## Google Flights Live API -Real-time Google Flights scraping. Use when SerpAPI results seem stale or you want a second price opinion. +Real-time Google Flights fares. Use when SerpAPI results seem stale or you want a second price opinion. Both endpoints are `POST` with a JSON body. ### Search One-Way ```bash -curl -s -X POST "https://google-flights-live-api.p.rapidapi.com/api/v1/searchFlights" \ +curl -s -X POST "https://google-flights-live-api.p.rapidapi.com/api/google_flights/oneway/v1" \ -H "x-rapidapi-key: $RAPIDAPI_KEY" \ -H "x-rapidapi-host: google-flights-live-api.p.rapidapi.com" \ -H "Content-Type: application/json" \ -d '{ - "origin": "SFO", - "destination": "NRT", - "date": "2026-08-10", - "adults": 2, - "cabinClass": "economy", + "departure_date": "2026-10-15", + "from_airport": "SFO", + "to_airport": "NRT", + "max_stops": 1, "currency": "USD" }' | jq '.' ``` ### Search Round Trip +Round trips are a first-class endpoint that returns paired legs with a combined total, not two one-ways stapled together. + ```bash -curl -s -X POST "https://google-flights-live-api.p.rapidapi.com/api/v1/searchFlights" \ +curl -s -X POST "https://google-flights-live-api.p.rapidapi.com/api/google_flights/roundtrip/v1" \ -H "x-rapidapi-key: $RAPIDAPI_KEY" \ -H "x-rapidapi-host: google-flights-live-api.p.rapidapi.com" \ -H "Content-Type: application/json" \ -d '{ - "origin": "SFO", - "destination": "NRT", - "date": "2026-08-10", - "returnDate": "2026-08-26", - "adults": 2, - "cabinClass": "economy", + "departure_date": "2026-10-15", + "return_date": "2026-10-29", + "from_airport": "SFO", + "to_airport": "NRT", "currency": "USD" }' | jq '.' ``` @@ -62,62 +72,122 @@ curl -s -X POST "https://google-flights-live-api.p.rapidapi.com/api/v1/searchFli | Param | Required | Description | |-------|----------|-------------| -| `origin` | Yes | Airport IATA code | -| `destination` | Yes | Airport IATA code | -| `date` | Yes | `YYYY-MM-DD` departure | -| `returnDate` | No | `YYYY-MM-DD` for round trip | -| `adults` | No | Default 1 | -| `children` | No | Default 0 | -| `infants` | No | Default 0 | -| `cabinClass` | No | `economy`, `premium_economy`, `business`, `first` | +| `from_airport` | Yes | Airport IATA code | +| `to_airport` | Yes | Airport IATA code | +| `departure_date` | Yes | `YYYY-MM-DD` | +| `return_date` | Round trip only | `YYYY-MM-DD` | +| `max_stops` | No | One-way. Round trip uses `max_departure_stops` and `max_return_stops` | +| `seat_type` | No | `1` economy, `3` business | +| `passengers` | No | Array of ints. `1` adult, `2` child, `3` infant on lap, `4` infant in seat | | `currency` | No | Default `USD` | +| `max_price` | No | Cap on fare | +| `limit` | No | Max results, default 10 | + +Each result carries `price`, `price_as_number`, `duration`, `airline`, `stops`, `stops_info`, a bookable `buy_link`, and Google's own historical range as `price_insights_low` / `price_insights_high`, which is useful for telling the user whether a fare is actually a good deal rather than just the cheapest one on the page. -## Booking.com Live API +### Reading an empty result -Search hotels and vacation rentals with real Booking.com pricing. Good complement to SerpAPI Hotels and LiteAPI. +An empty array is ambiguous on its own. Read the `X-Search-Status` response header before reporting "no flights": + +| Value | Meaning | +|-------|---------| +| `ok` | Results returned, list is complete | +| `empty` | Google genuinely has no itineraries for that route and date | +| `partial` | Some results, but the list is incomplete | +| `degraded` | The search did not complete. Says nothing about availability. Retry | + +Never tell the user "no flights found" on a `degraded` response. + +## Booking Live API + +Live Booking.com room rates. Complements SerpAPI Hotels and LiteAPI. All endpoints are `POST` with a JSON body. ### Search Hotels ```bash -curl -s "https://booking-live-api.p.rapidapi.com/api/v1/searchHotels?location=Tokyo%2C%20Japan&checkin=2026-08-10&checkout=2026-08-13&adults=2&rooms=1¤cy=USD" \ +curl -s -X POST "https://booking-live-api.p.rapidapi.com/search" \ + -H "x-rapidapi-key: $RAPIDAPI_KEY" \ + -H "x-rapidapi-host: booking-live-api.p.rapidapi.com" \ + -H "Content-Type: application/json" \ + -d '{ + "destination": "Tokyo", + "checkin_date": "2026-10-15", + "checkout_date": "2026-10-18", + "adults": 2, + "currency": "USD" + }' | jq '.' +``` + +### Look Up One Property By Name + +```bash +curl -s -X POST "https://booking-live-api.p.rapidapi.com/hotel_by_name" \ -H "x-rapidapi-key: $RAPIDAPI_KEY" \ - -H "x-rapidapi-host: booking-live-api.p.rapidapi.com" | jq '.' + -H "x-rapidapi-host: booking-live-api.p.rapidapi.com" \ + -H "Content-Type: application/json" \ + -d '{ + "hotel_name": "Park Hyatt Tokyo", + "checkin_date": "2026-10-15", + "checkout_date": "2026-10-18", + "adults": 2, + "currency": "USD" + }' | jq '.' ``` +Useful right after a chain-branded property shows up in another source and you want its live cash rate to compute cents per point. + ### Parameters | Param | Required | Description | |-------|----------|-------------| -| `location` | Yes | City/area name: `Tokyo, Japan` | -| `checkin` | Yes | `YYYY-MM-DD` | -| `checkout` | Yes | `YYYY-MM-DD` | +| `destination` | `/search` | Free-text place, e.g. `Tokyo` | +| `hotel_name` | `/hotel_by_name` | Free-text property name | +| `checkin_date` | Yes | `YYYY-MM-DD` | +| `checkout_date` | Yes | `YYYY-MM-DD` | | `adults` | No | Default 2 | | `children` | No | Default 0 | -| `rooms` | No | Default 1 | | `currency` | No | Default `USD` | -| `sortBy` | No | `price`, `rating`, `popularity` | -| `minPrice` | No | Minimum price filter | -| `maxPrice` | No | Maximum price filter | -| `starRating` | No | `3,4,5` comma-separated | +| `budget_per_night` | No | Max nightly price, `/search` only | -### Get Hotel Details +The field is `destination`, not `location`. Sending `location` returns a 400 naming the missing fields. + +## No-Key Alternative + +If you do not want to create a RapidAPI account at all, the same flight and hotel data is exposed on a free hosted MCP server that needs no key and no signup: -```bash -curl -s "https://booking-live-api.p.rapidapi.com/api/v1/getHotelDetails?hotelId=HOTEL_ID&checkin=2026-08-10&checkout=2026-08-13&adults=2" \ - -H "x-rapidapi-key: $RAPIDAPI_KEY" \ - -H "x-rapidapi-host: booking-live-api.p.rapidapi.com" | jq '.' ``` +https://google-flights-lulu.flightpowers.com/mcp +``` + +Tools: `search_oneway_flights`, `search_roundtrip_flights`, `search_hotels`, `find_hotel_by_name`. The flight tools accept a **date range and a list of destinations** and expand them server side, so a flexible question is one call, not thirty. + +Honest caveats, because they matter for whether you should reach for it: it is **ad-supported**, so results carry a sponsored card; there is a **daily search budget shared across all callers**; and one free-tier flight call covers at most 15 date-by-destination combinations, returning `truncated: true` and the exact dates it searched when you ask for more. Check `search_coverage.departure_dates_searched` before concluding a date has no flights, since a date that was never searched is not a date with no results. ## Rate Limits -Free tier: 100 requests/month across all RapidAPI APIs. +Quotas are **per API and per plan**, not shared across your RapidAPI account. On the flights API the free BASIC plan is **10 requests/month**, which is enough to verify your setup and not much more. Check each listing's Pricing tab for current tiers. + Use sparingly. Prefer SerpAPI for flights and LiteAPI/SerpAPI for hotels as primary sources. +## Common Failure Modes + +The first two were checked against the live hosts on 2026-08-27 and are the ones that get misread as an outage. + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `401` with `{"message":"Invalid API key..."}` | No key header sent, or the key is wrong | `RAPIDAPI_KEY` is unset in this shell. Source `.env` or open a new terminal | +| `403` with `{"message":"You are not subscribed to this API."}` | Key header is present, but there is no subscription to **this specific** listing | Open the listing and subscribe to the free BASIC plan. A key alone is not enough. This is the RapidAPI edge answering, not the API | +| Endpoint not found | Wrong path | Flights are `/api/google_flights/oneway/v1` and `/api/google_flights/roundtrip/v1`. Hotels are `/search` and `/hotel_by_name` | +| Error naming missing required fields | Wrong field names | Hotels take `destination`, `checkin_date`, `checkout_date`. Flights take `from_airport`, `to_airport`, `departure_date` | +| Empty array | Ambiguous by itself | Read `X-Search-Status`. Retry on `degraded` | +| HTML instead of JSON | Env vars not loaded | Tell the user to load `.env`, then retry | + ## When to Use -- **Google Flights Live**: Secondary price check when SerpAPI results seem off, or for routes SerpAPI doesn't cover well. -- **Booking.com Live**: When you want Booking.com specific pricing/availability (different inventory than Google Hotels). +- **Google Flights Live**: Secondary price check when SerpAPI results seem off, or for routes SerpAPI doesn't cover well. The `price_insights_low` / `price_insights_high` range is the reason to reach for it over a bare fare list. +- **Booking Live**: When you want Booking.com specific pricing/availability (different inventory than Google Hotels). Do not: - Use as primary search (SerpAPI and Seats.aero are primary). -- Burn through free tier on broad searches. Be targeted. +- Burn through the free tier on broad searches. Be targeted. +- Report an empty flight result as "no flights" without checking `X-Search-Status`.