DEV Community

Tedj MEABIOU
Tedj MEABIOU

Posted on

Hotel price tracking with Google Hotels data: an API in 10 minutes (Python + n8n)

Google Hotels already compares every booking site for a hotel and a stay — Booking.com, Expedia, Agoda, Hotels.com and the hotel's own site. It has a "track prices" button too, but it emails you on its own terms, picks the sources, and keeps the history. If you want the numbers — for a trip, a rate parity check, or a price history chart — you need them as rows.

This is how to get Google Hotels prices for exact dates as JSON, without a Google API key (there is no public Google Hotels API for reading prices; the official Hotel APIs are feeds for hotels sending prices to Google), and how to turn that into daily hotel price tracking.

1. One request, every booking site's rate

The Google Hotels Prices Scraper on Apify takes a place search or a list of hotels, a stay, occupancy and currency, and returns three row types: hotel (lowest nightly rate + stay total), offer (each source's rate, free‑cancellation flag, deep link) and status. You pay per priced row; sold‑out hotels and empty searches are free.

curl -X POST "https://api.apify.com/v2/acts/kestrel~google-hotels-prices/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["hotels in Lisbon"], "checkIn": "2026-10-03", "checkOut": "2026-10-06", "adults": 2, "currency": "USD", "maxHotels": 20}'
Enter fullscreen mode Exit fullscreen mode

A hotel row looks like this:

{ "type": "hotel", "name": "The Central House Lisbon Baixa", "check_in": "2026-10-03", "check_out": "2026-10-06", "nights": 3,
  "nightly": 81.81, "nightly_display": "$82", "total": 245, "stars": 2, "rating": 4.3, "reviews": 727, "deal": "19% less than usual",
  "entity_id": "ChkIg-b2ismUj7M1Gg0vZy8xMWg3MThreGg1EAE", "google_url": "https://www.google.com/travel/hotels/entity/ChkI…" }
Enter fullscreen mode Exit fullscreen mode

and an offer row (with "includeOffers": true):

{ "type": "offer", "name": "Hyatt Regency Lisbon", "source": "Booking.com", "official": false, "nightly": 569.35, "total": 1708.05,
  "free_cancel": true, "free_cancel_until": "Oct 1", "partner_url": "https://www.booking.com/hotel/pt/hyatt-regency-lisbon.html?checkin=2026-10-03…" }
Enter fullscreen mode Exit fullscreen mode

2. Python: a daily price history in a CSV

import csv, os
from datetime import date, timedelta
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
check_in = date.today() + timedelta(days=30)                     # always price the stay 30 days out
run = client.actor("kestrel/google-hotels-prices").call(run_input={
    "queries": ["hotels in Lisbon"], "checkIn": check_in.isoformat(), "checkOut": (check_in + timedelta(days=2)).isoformat(),
    "adults": 2, "currency": "USD", "maxHotels": 40, "includeOffers": False})
rows = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items() if r["type"] == "hotel" and r["nightly"]]

with open("hotel_price_history.csv", "a", newline="") as f:
    w = csv.writer(f)
    for r in rows: w.writerow([date.today(), r["check_in"], r["name"], r["entity_id"], r["nightly"], r["total"]])
print(f"{len(rows)} hotels priced; cheapest: {min(rows, key=lambda r: r['nightly'])['name']}")
Enter fullscreen mode Exit fullscreen mode

Run it from cron, or skip cron entirely: schedule the actor in Apify with relative dates ("checkIn": "30 days", "checkOut": "32 days") and every run appends dated rows to the dataset — that dataset is the price history.

3. Price drop alerts without a database (n8n)

The n8n template Google Hotels price drop alerts is four nodes: a daily trigger, one HTTP request to the actor, a Code node that keeps yesterday's price per hotel in workflow static data and emits the drops, and Gmail. Set the threshold in the Code node (0.10 = 10% cheaper than yesterday). Swap Gmail for Slack or Google Sheets.

4. Rate parity check for hoteliers (JavaScript)

Revenue managers care about one comparison: is any OTA undercutting the official site? Give the actor your properties as Google Hotels URLs and read the offer rows:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('kestrel/google-hotels-prices').call({
  hotels: ['https://www.google.com/travel/hotels/entity/ChkIhLCQwvjO2IYWGg0vZy8xMXE0bTZieDkyEAE'],
  checkIn: '30 days', checkOut: '32 days', currency: 'EUR', offerLevel: 'sources' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const offers = items.filter(r => r.type === 'offer');
const official = Math.min(...offers.filter(o => o.official).map(o => o.nightly));
console.table(offers.filter(o => !o.official && o.nightly < official).map(o => ({ source: o.source, nightly: o.nightly, official })));
Enter fullscreen mode Exit fullscreen mode

Sources are keyed by source_id — a name like Holidu can appear twice for one property, once as the official listing and once as a marketplace rate.

5. Things to know

  • Dates must be in the future; the actor rejects past check‑ins instead of silently pricing tonight (Google's default).
  • Prices follow the market (country) and currency you choose; compare the stay total, not the headline nightly figure.
  • A place search pages Google's results 20 at a time up to 200 hotels; neighbourhood queries add coverage.
  • It's public pricing data, no personal data; check the terms that apply to your use.

That's the whole pipeline: one call for the prices, a schedule for the history, a Code node for the alert. The actor's page has the full input/output reference and a FAQ: apify.com/kestrel/google-hotels-prices.


All the code above, the n8n workflow, and the same examples for the rest of the suite (Airbnb, Agoda, Google Flights, Amazon) are in github.com/mtedj/kestrel-actors-examples.

Top comments (0)