Google Hotels Price Tracker: Calendar, Deals & Typical Range
Pricing
from $0.80 / 1,000 priced dates
Google Hotels Price Tracker: Calendar, Deals & Typical Range
Track a hotel's price on every booking site for each check-in date from Google Hotels: the lowest price, whether Google calls it low, typical or high for the dates, sold-out dates, price changes and alerts. Not affiliated with Google.
Pricing
from $0.80 / 1,000 priced dates
Rating
0.0
(0)
Developer
Aleksandr Kobtsev
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Track what every booking site charges for a hotel on each check-in date, as Google Hotels lists it, and what Google itself says about that price: low, typical or high for that hotel and those dates. One dataset row per hotel and date, with the cheapest site, the hotel's own price, the typical range, the change since the last run and an alert when the price is low, falls, reaches your target or sells out. Put it on a schedule and read ALERTS.
There is no login, no browser and no partner API. Prices come from Google Hotels pages that anyone can open, read as plain HTML. The Actor requests only the /travel/hotels/ pages Google's robots.txt leaves open, and says in every record what it could and could not read.
Who is this hotel price tracker for?
| You are | What you run | What you get |
|---|---|---|
| A hotel owner or revenue manager | your hotel and its competitors, every day | your price against the set date by date (rank, rate index), the dates Google calls low or high for each hotel, who undercuts your own site |
| A travel agent, a deal site or a travel blogger | the hotels you write about, the next 60 days | the cheapest dates, the dates Google calls low, the price drops since yesterday |
| A traveller planning a trip | one hotel, a season | what it costs on each date and which dates are cheaper than usual for that hotel |
| An analyst | the hotels of a city, weekly | the market date by date: cheapest, median and dearest price, the share of hotels sold out |
What makes this different from other Google Hotels price scrapers?
- It says whether the price is low for that hotel. Google prints a typical range for each hotel and stay and calls the price low, typical or high against it. The row has the range and the call, and the dates Google calls low raise an alert. A price calendar alone does not tell a cheap date from an ordinary one.
- It knows which dates are sold out. A date on which no booking site has a price is delivered as sold out and costs nothing. A date that had a price in the previous run and has none now raises
sold_outonce. - It compares you with the competitive set. Name your own hotel and the COMPSET record gives, for every date, your price, the competitors' lowest, median and highest, your rank and your rate index.
- It remembers. Each run says what moved since the last:
previousPrice,priceChange,priceChangePercenton every row and a PRICE_CHANGES record with the biggest drops first. - It shows every booking site. The row keeps the offer of each site Google lists (price, total, free-cancellation deadline, link), the hotel's own price and which sites undercut it.
- It is priced by the date that has a price. A priced date is one event; a sold-out date is free.
What data does it return?
One row per hotel and check-in date, in calendar order, hotel by hotel.
| Field | What it is |
|---|---|
| name, hotelClass, rating, reviewsCount | The hotel, its stars (null where Google does not class it), Google's guest rating of 5 and the number of reviews. |
| address, city, countryCode, latitude, longitude, website | Where it is and its own website as Google lists it. |
| googleUrl, googleMid, entityToken | The hotel's Google Hotels page for this stay, and Google's own identifiers: the map feature id (null for vacation rentals) and the token in the page address. Stable, so they are the keys to join runs on. |
| checkIn, checkOut, nights, dayOfWeek, adults, currency, country | The stay: check-in and check-out dates (YYYY-MM-DD), the length, the weekday of check-in (Mon, Tue ...), the guests, the currency of the prices and the country of sale they are for. |
| available | false when no booking site has a price for the hotel for this stay: the date is sold out or not bookable. Such a row is delivered and not charged. |
| price, priceBeforeTaxes, total | The lowest price per night among the booking sites, as Google shows it (taxes and fees included where Google splits them out), the same before taxes and fees (null where the site gives one figure only) and the price for the whole stay. |
| cheapestSite, sitesCount | The booking site with the lowest price and how many sites have a price. |
| officialPrice, parityStatus, officialUndercutBy | Rate parity: the price of the hotel's own site, undercut, parity or no_official_offer, and the sites that charge at least one percent less than the hotel's own site (provider, price, percentBelow, biggest gap first). |
| offers | Every booking site's offer, cheapest first: provider, providerId, official, price, priceBeforeTaxes, total, totalBeforeTaxes, currency, originalPrice, promotion, freeCancellation, freeCancellationUntil, freeCancellationUntilText and bookingUrl. Empty when the run was set to leave offers out. |
| googlePrice, typicalLow, typicalHigh | The price Google sets against the typical range, and the low and high ends of that range for this hotel and these dates. All three are before taxes and fees, as Google prints them: compare googlePrice with the range, not price. null when the page has no range. |
| priceLevel | low under the range, typical inside it, high above it, as Google judges; null when the page has none. |
| pricePosition | Google's own number from 0 (cheap for the hotel) to 1 (dear for the hotel). Not a share of the range. |
| usualDeltaPercent, dealLabel | The label Google prints under a price that is well below the usual one, as text (19% less than usual) and as a number (-19). Google prints it from 15 percent below. |
| previousPrice, priceChange, priceChangePercent, previousScrapedAt | With Remember prices between runs: the price this hotel and check-in date had in the previous run of the same kind of stay, the difference (negative is cheaper), the difference in percent and when that run was. null on a first run, and where either price is missing. |
| alerts | Why the row is on the ALERTS list: low_for_the_dates, below_target, price_drop, sold_out (below). |
| query, source, scrapedAt | The hotel name or search text the row came from, whether it was a search, a name or a link, and when it was read (ISO-8601, UTC). |
| customData | Your own JSON from the input, copied onto every row. Present only when you give it. |
An example row, as a run returned it on 11 October 2026: a hotel in Batumi, sold in the United States, for one night. The list of offers and the addresses in it are shortened.
{"name": "Courtyard by Marriott Batumi","hotelClass": 5,"rating": 4.3,"reviewsCount": 591,"address": "5 Sherif Khimshiashvili St, Batumi 6000, Georgia","city": "Batumi","countryCode": "GE","latitude": 41.6417259,"longitude": 41.6157544,"website": "https://www.marriott.com/en-us/hotels/buscy-courtyard-batumi/overview/?scid=f2ae0541-1279-4f24-b197-a979c79310b0","googleUrl": "https://www.google.com/travel/hotels/entity/ChkIkcLNqfSn-JFgGg0vZy8xMWcy...","googleMid": "0x4067876ab1deee13:0x6023e13f45336111","entityToken": "ChkIkcLNqfSn-JFgGg0vZy8xMWcyeGtzczRsEAE","checkIn": "2026-10-27","checkOut": "2026-10-28","nights": 1,"dayOfWeek": "Tue","adults": 2,"currency": "USD","country": "us","available": true,"price": 148.45,"priceBeforeTaxes": null,"total": 148.45,"cheapestSite": "Qantas Hotels","officialPrice": 148.68,"parityStatus": "parity","officialUndercutBy": [],"sitesCount": 22,"offers": [{ "provider": "Qantas Hotels", "providerId": 660369587, "official": false, "price": 148.45, "priceBeforeTaxes": null, "total": 148.45, "totalBeforeTaxes": null, "currency": "USD", "originalPrice": null, "promotion": null, "freeCancellation": false, "freeCancellationUntil": null, "freeCancellationUntilText": null, "bookingUrl": "https://www.qantas.com/hotels/properties/894340?adults=2&children=0&infa..." },{ "provider": "Courtyard by Marriott Batumi", "providerId": 100231600, "official": true, "price": 148.68, "priceBeforeTaxes": 126, "total": 148.68, "totalBeforeTaxes": 126, "currency": "USD", "originalPrice": 148.68, "promotion": "Get the best prices plus free Wi-Fi with free membership enrollment", "freeCancellation": false, "freeCancellationUntil": null, "freeCancellationUntilText": null, "bookingUrl": null },{ "provider": "Expedia.com", "providerId": 89, "official": false, "price": 148.68, "priceBeforeTaxes": 126, "total": 148.68, "totalBeforeTaxes": 126, "currency": "USD", "originalPrice": null, "promotion": null, "freeCancellation": false, "freeCancellationUntil": null, "freeCancellationUntilText": null, "bookingUrl": null }],"googlePrice": 126,"priceLevel": "low","typicalLow": 133,"typicalHigh": 177,"pricePosition": 0.106,"usualDeltaPercent": -19,"dealLabel": "19% less than usual","previousPrice": 148.45,"priceChange": 0,"priceChangePercent": 0,"previousScrapedAt": "2026-10-11T21:44:16.443Z","alerts": ["low_for_the_dates"],"query": "hotels in Batumi","source": "search","scrapedAt": "2026-10-11T21:46:35.891Z"}
The records of the run
Beside the dataset, the run writes these records to its key-value store.
DATE_SUMMARY: the market the run saw, one line per check-in date.
| Field | What it is |
|---|---|
| checkIn, dayOfWeek | The date and its weekday. |
| hotels, priced, soldOut, soldOutShare | Hotels read for the date, how many have a price, how many do not, and the share without a price (0 to 1). |
| minPrice, medianPrice, maxPrice, cheapestHotel | The spread of the lowest prices per night and the hotel that has the lowest. |
| low, typical, high | How many hotels Google calls low, typical and high for the date. |
COMPSET: with Your hotel, one line per date: your hotel against the other hotels of the run.
| Field | What it is |
|---|---|
| checkIn, dayOfWeek | The date and its weekday. |
| myPrice, myLevel, mySoldOut | Your lowest price per night, Google's call on it, and whether your hotel has no price for the date. |
| competitors, competitorsSoldOut | Competitors with a price and without one. |
| compMin, compMedian, compMax | The competitors' lowest, median and highest price per night. |
| rank, cheaperCompetitors | Your place among yourself and the competitors with a price (1 is the cheapest), and how many competitors are cheaper. null and 0 when you have no price. |
| rateIndex | Your price over the competitors' median, times 100: 100 is level with the median, 120 is a fifth dearer. |
HOTELS: one line per hotel over all its dates: name, entityToken, googleMid, googleUrl, isMine (it is your hotel), query, dates, priced, soldOut, minPrice, medianPrice, maxPrice, cheapestDates (the three cheapest: checkIn, price, priceLevel), lowDates (dates Google calls low) and withTypicalRange (priced dates for which the page gave a range).
ALERTS: the rows that raised an alert, the earliest date first: name, entityToken, checkIn, dayOfWeek, alerts, price, currency, cheapestSite, priceLevel, usualDeltaPercent, previousPrice, priceChangePercent and url (the hotel's Google page for the stay).
PRICE_CHANGES: the prices that moved since the previous run, the biggest drops first: name, entityToken, checkIn, dayOfWeek, previousPrice, price, priceChange, priceChangePercent, currency, cheapestSite and url.
SUMMARY: hotels, dates, rowsRead, rowsDelivered, priced, soldOut, withTypicalRange, low, alerts, yourHotel, charged, notCharged, googleRequests, googleFailedAttempts and failed.
FAILED_HOTELS: the searches, names and dates that could not be read, each with what, stage and reason; absent when there were none.
What are the alerts?
An alert is a fixed rule over a row. A row can raise several; the ALERTS record lists them with the reason.
| Alert | The rule |
|---|---|
low_for_the_dates | Google calls the price low for the hotel and the dates, or the label under it says at least Alert when below the usual price by percent below usual (15 by default). |
below_target | The price is at or under Target price per night. |
price_drop | The price fell by at least Alert on a drop of at least percent (10 by default) since the previous run. Needs Remember prices between runs. |
sold_out | The hotel had a price for the date in the previous run and has none now. Raised once: the next run does not repeat it for the same date. |
How does the typical range work?
On a hotel's own page Google prints a typical range for the stay and says whether the price is low, typical or high for it ("Prices are lower than usual for these dates"). The Actor reads that from the data of the page. Three things to know before relying on it:
- It is not on every page. Of 269 priced hotel dates measured on 12 October 2026 (ten hotels of Batumi, thirty dates starting two days ahead), 158 carried a range, 59 percent. The share was 63 percent for dates up to nine days ahead, 67 percent for ten to nineteen, 48 percent for twenty to twenty-nine and, in the few dates beyond, three of eight. Two of the ten hotels never carried one. A row without a range has
priceLevelnull; that says Google gave none, not that the price is typical. - It is before taxes and fees.
googlePriceand the range are the figures Google prints, which leave out taxes and fees where Google can split them out.priceis what the booking site shows. In most of the rows of Batumi measured,googlePricewaspricedivided by 1.18, the 18 percent tax of Georgia. SetgooglePriceagainsttypicalLowandtypicalHigh. - It depends on the market. Google makes its call for the country of sale of the run, so the same hotel and dates can carry the label in one market and not in another. Measured on 12 October 2026: Courtyard by Marriott Batumi carried the label on fifteen of thirty dates sold in the United States and on none of twelve dates sold in Germany. Run the markets you sell to separately.
- The call is Google's. The Actor reads it and does not recompute it. On the 26 rows of one run that carried a range,
priceLevelagreed withgooglePriceagainst the range on all but one, whose price stood on the low end of the range.
The label (dealLabel, usualDeltaPercent) is a separate figure: Google prints it only from 15 percent below the usual price, and it was on 67 of the 269 rows, including all 32 that Google called low.
How do I use it?
- Under Hotels put hotel names with the city (
Hilton Batumi) or paste a Google Hotels link of a hotel. Or under Searches type what you would type into Google Hotels (hotels in Batumi) and set Hotels per search. To compare your hotel with its competitors, put it under Your hotel. - Set Check-in dates to track and Days between check-ins: 30 dates one day apart is the next month, 13 dates a week apart is three months, one weekday every week.
- Press Start, or put the run on a schedule (daily is the usual choice) and read ALERTS after each run. Turn on an integration (Slack, e-mail, a webhook) on the run's success to get the ALERTS record where you work.
From the API:
{"myHotel": "Hilton Batumi","hotels": ["Radisson Blu Batumi", "Sheraton Batumi Hotel", "Le Meridien Batumi"],"daysAhead": 1,"dates": 60,"nights": 1,"currency": "USD","targetPrice": 120,"alertDropPercent": 10}
What can I set?
| Setting | What it does |
|---|---|
| Hotels | Hotels by name with the city, or by Google Hotels link. |
| Your hotel | Your hotel, tracked with the others; adds the COMPSET record. |
| Searches, Hotels per search | Texts to search on Google Hotels, and how many of the hotels each lists to track, 1 to 200. |
| First check-in date, Days ahead | The first check-in: a date, or this many days from today (1 by default). A scheduled run with Days ahead always prices the same distance ahead. |
| Check-in dates to track, Days between check-ins | How many dates (1 to 120, 30 by default) and the gap between them (1 to 30 days). Every hotel is read once for every date. |
| Nights | The length of each stay, 1 to 30 (1 by default). |
| Adults, Children (ages) | The party the prices are for. |
| Currency, Country of sale, Language | The currency of the prices, the country Google sells the stay in (it decides which booking sites it lists) and the language of Google's texts. |
| Remember prices between runs | On by default: the run remembers each hotel and date in a store of your own account (google-hotels-tracker-memory) and says what changed on the next run of the same kind of stay (same nights, guests, currency and country). |
| Target price per night | A price at or under it raises below_target. |
| Alert on a drop of at least (%), Alert when below the usual price by (%) | The percents for price_drop and for the label side of low_for_the_dates. 0 switches them off. |
| Every booking site's price in the rows | On by default. Off: rows keep the lowest price, the cheapest site and the other fields, without the offers, which makes them much smaller. |
| Custom data | JSON copied onto every row. |
| Requests in parallel | 1 to 10, five by default. |
| Proxy | Apify datacenter proxies by default; your own proxies if you choose Custom proxies. |
What does it cost?
Pay per event. A date event for every hotel and check-in date delivered with a price: $0.0012 on the free plan, $0.00105 Bronze, $0.0009 Silver, $0.0008 Gold and above. A date with no price (sold out) is delivered and costs nothing. The platform's own events are the start of the run ($0.00005) and the dataset item ($0.00001) for every row stored.
Measured on the platform on 12 October 2026: ten hotels of Batumi for thirty dates, 300 rows, took 4 minutes 40 seconds and used $0.0155 of platform usage, with 368 requests to Google and no failed attempt. 269 of the rows had a price and 31 were sold out, so the run costs a buyer on the free plan 269 x $0.0012 + 300 x $0.00001 + $0.00005, about $0.33. One hotel for fourteen dates took 16 seconds. A run of 1,000 rows (25 hotels of Lisbon for 40 dates) took 26 minutes 28 seconds and used $0.0765 of platform usage, with 1,634 requests to Google, one failed attempt and a peak of 177 MB of memory.
Limits
- A run prices one stay length and one party. Different lengths or parties are separate runs, and each kind of stay has its own memory.
- Google prices stays up to about eleven months ahead, and gives a typical range for far fewer of them (above). A calendar of 120 dates for 100 hotels is 12,000 pages; one run delivers at most 20,000 rows.
- A hotel's page does not list the same booking sites in every answer: Google leaves some out, the more often the smaller the hotel. A hotel with few offers is read again, up to three times, and the offers are merged. How steady the figure is, measured on the same reading code: six hotels for ten dates were read three times on 11 October 2026 (165 pairs of reads of the same hotel and date). The cheapest price differed by more than half a percent in 4 pairs (2%), because a booking site listed in one answer was missing from the other; the Booking.com price and the hotel's own price did not differ in any pair. The default drop alert of 10 percent is above that noise; a lower one, or the
offersentry of the site you care about, needs more care. - "Sold out" means that no booking site Google lists has a price for the hotel on the date. A hotel can have a room it sells on its own site only, or be closed for the date for another reason.
- The memory is a store of your account. A date that has passed is forgotten; the first run of a kind of stay has no changes to report.
- Google's pages change from time to time. The Actor finds hotels and offers by their shape and checks that every page answers for the stay it asked, so a change shows as a clear error rather than as wrong rows; if it ever does, open an issue with the run link.
Is it legal to scrape Google Hotels prices?
This is an unofficial tool, not affiliated with, endorsed by or connected to Google LLC or any hotel. It reads publicly visible pages and collects no personal data. Google's terms restrict automated access, and you are responsible for making sure your use complies with those terms and with the law in your jurisdiction. See Apify's stance on web scraping legality.
What the Actor does about robots.txt: it reads Google's robots.txt at the start of every run and requests only addresses it leaves open, which are the /travel/hotels/ pages. It refuses any address the file closes.
Found a problem?
Open an issue on the Issues tab with the run link and the hotel. Breakages in Google's pages are the expected failure mode of any scraper, and this one is built to say so rather than to deliver wrong data. Reports get read.