Google Hotels Prices Scraper — Provider Prices & Price Alerts
Pricing
Pay per event
Google Hotels Prices Scraper — Provider Prices & Price Alerts
Scrape Google Hotels prices for any city or hotel: lowest price, every booking site's rate (Booking.com, Expedia, Agoda, official site…), free cancellation, rating, stars and amenities in USD. Built-in price-change alerts.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Samuel IZABAYO
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Share
Google Hotels Scraper – Prices, Ratings & OTA Rates
Scrape Google Hotels for any city or hotel and get, per hotel: the lowest nightly price, every OTA's rate (Booking.com, Expedia, Hotels.com, Agoda, Trip.com, Priceline, the hotel's official site and more), free-cancellation flags, direct booking links, rating, review count, star class, amenities, address and coordinates. Prices come back in USD by default, or in one of 11 other currencies (EUR, GBP, JPY, …).
It also has a built-in price alert mode: schedule it, and each run outputs only the hotels whose lowest price changed since the last run, ready for Slack, email, Google Sheets, Zapier, Make or a webhook.
Pay only for results: $1.00 per 1,000 hotels, plus $2.00 per 1,000 for the full OTA price list. Failed or empty hotels are never charged.
Why this Google Hotels scraper?
- Built to keep working. It reads the structured data that Google Travel's own frontend uses instead of fragile HTML class names, so cosmetic redesigns don't break it.
- Automatic retries and proxy fallback. Blocked or empty responses are retried on a fresh IP, then through US residential proxies if needed. You don't configure anything.
- You never pay for failures. A hotel is charged only when it comes back with a price; the OTA price list is charged only when it was fetched and isn't empty.
- Fast and light. No headless browser. Pagination and OTA price lists use small requests, which keeps runs quick and cheap.
- Prices in your currency, wherever the proxy is. The currency is part of the request, so you never get a random local currency.
- Accurate stays. Exact check-in/check-out dates and adult count are sent to Google, and the OTA booking links carry the same dates and guests.
- Price alerts built in. No database or diffing script needed.
What data do you get?
| Field | Example |
|---|---|
hotel | Ritz Paris |
entityId | ChUImcGKtLfWgL9OGgkvbS8wNTZuODEQAQ (stable Google hotel ID) |
location | Your input query, e.g. Paris |
checkIn / checkOut / adults | 2026-12-01 / 2026-12-04 / 2 |
lowestPrice | 2806.1 (per night) |
totalPrice | 8418 (whole stay) |
currency | USD |
rating / reviews | 4.6 / 4643 |
stars | 5 |
amenities | ["Free Wi-Fi", "Indoor pool", "Spa", "Fitness center", …] |
address, latitude, longitude | 15 Pl. Vendôme, 75001 Paris, France |
providerPrices | OTA rates, see below |
providerCount | 12 |
googleUrl | Link to the hotel on Google Hotels |
scrapedAt | ISO timestamp |
previousLowestPrice, priceChange, changeType | Alert mode only |
history | Price history: 30/90-day min/median/max, percentile, lowest seen, sparkline (only with Add price history, see below) |
Each entry in providerPrices looks like this:
{"provider": "Booking.com","price": 129,"totalPrice": 258,"freeCancellation": true,"freeCancellationUntil": "Nov 9","official": false,"url": "https://www.booking.com/searchresults.en.html?...checkin=2026-11-10&checkout=2026-11-12&group_adults=2..."}
OTA offers are sorted from cheapest to most expensive, one entry per booking site. official: true marks the hotel's own website.
How to use it
- Enter one or more Locations (e.g.
New York,Shibuya, Tokyo,beach hotels in Cancun) and/or Hotel names (e.g.Bellagio Las Vegas). - Pick check-in and check-out dates and the number of adults. If you leave the dates empty, the stay starts 14 days from today and lasts 2 nights.
- Set Max hotels per location. Google returns about 20 hotels per page, and the scraper paginates automatically.
- Keep Include provider prices on for the full OTA price comparison. Turn it off for a cheaper hotel list with the lowest price only.
- Click Start, then download the results as JSON, CSV, Excel or HTML, or use the API.
Examples
Hotel list with prices and ratings (cheapest)
{"locations": ["Barcelona", "Lisbon"],"checkIn": "2026-12-01","checkOut": "2026-12-04","maxHotelsPerLocation": 100,"includeProviderPrices": false}
200 hotels with lowest price, rating, stars and amenities: at most $0.20.
Full OTA price comparison
{"locations": ["New York", "Paris", "Tokyo"],"checkIn": "2026-12-01","checkOut": "2026-12-04","adults": 2,"maxHotelsPerLocation": 50,"includeProviderPrices": true,"currency": "EUR"}
150 hotels, each with every booking site's rate: at most $0.45.
Specific hotels
{"hotelNames": ["Ritz Paris", "Bellagio Las Vegas", "Marina Bay Sands Singapore"],"checkIn": "2026-12-20","checkOut": "2026-12-22"}
One row per hotel, matched to Google's top result. Add the city to the name for best matching.
Daily price-drop alerts
{"locations": ["Manhattan, New York"],"checkIn": "2026-12-28","checkOut": "2027-01-02","maxHotelsPerLocation": 60,"onlyChangedSinceLastRun": true,"stateKey": "nyc-new-year","minPriceChangePercent": 5}
Run it on a daily Schedule. After the first (baseline) run, only hotels whose price moved by 5 % or more are output — and charged.
From the API
curl -X POST "https://api.apify.com/v2/acts/niyomurinzi.elis~google-hotels-prices/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \-H "Content-Type: application/json" \-d '{"locations": ["London"], "maxHotelsPerLocation": 20}'
Migrating from other Google Hotels scrapers
Switching is meant to take minutes, not a rewrite. Common field names from other Google Hotels scrapers are accepted as aliases:
| Your existing field | Used as |
|---|---|
checkInDate | checkIn |
checkOutDate | checkOut |
maxResults | maxHotelsPerLocation |
languageCode, countryCode, requestTimeoutSecs | Accepted and ignored (results are in English; timeouts and retries are automatic) |
If both an alias and the native field are set, the native field wins. Other fields need to be mapped by hand — usually just your search terms into locations (cities or free-text queries) or hotelNames (specific hotels). Output fields may also be named differently; see What data do you get? and use entityId as the stable hotel key.
Example of an input written for another scraper, adapted:
{"locations": ["Rome"],"checkInDate": "2026-12-01","checkOutDate": "2026-12-03","maxResults": 40,"languageCode": "en"}
Hotel price alerts (monitoring mode)
Turn on Price alerts: only output changed prices and give your watch-list a State key, e.g. nyc-december.
- First run. Everything is output as a baseline, and the prices are saved.
- Every later run. Only hotels whose lowest price changed are output, with
previousLowestPrice,priceChangeandchangeType. - Tuning.
- Use Minimum price change (%) to ignore small moves.
- Turn on Also alert on newly listed hotels if you also want hotels that show up for the first time.
- Scope of the saved prices. They are stored per hotel, stay dates, adults and currency, in a named key-value store (
google-hotels-prices-state), so they persist between runs.
To automate it, create a Schedule, for example daily at 08:00, and add an integration such as Slack, email, Google Sheets or a webhook that fires when the run succeeds. Since alert-mode runs only output changes, every row is a real price movement.
Price history and lowest-price tracking
Turn on Add price history to see whether today's price is actually low. Each hotel gets a history object built from prices collected every day for the tracked hotels (the top hotels of 20 popular cities, 2-night stays 14, 30 and 60 days ahead, 2 adults, USD):
| Field | Meaning |
|---|---|
currentPrice | Today's price compared against the history (live price when your stay is 2 nights / 2 adults, else the latest collected price) |
min30d / median30d / max30d | Lowest, median and highest daily price over the last 30 days |
min90d / median90d / max90d | Same over the last 90 days |
percentile | Share of past observations cheaper than the current price: 0 = cheapest ever seen, 100 = most expensive |
lowestSeenPrice / lowestSeenDate | The lowest price on record and when it was seen |
sparkline | 30 daily lowest prices, oldest first (null for days without data), ready for a mini chart |
observations / historyDays | How much history the numbers are based on |
status / charged / message | ok (charged) or tracking-started / not-tracked / unavailable (not charged) |
{"locations": ["Paris"],"checkIn": "2026-12-01","checkOut": "2026-12-03","adults": 2,"history": true}
Already have hotel IDs? Put them in Hotel entity IDs (entityIds) together with history: true to get the history without a live price scrape.
- New hotels and cities. A hotel or city that isn't tracked yet is added to the tracked set. Its row says
tracking-startedwith 0 days of history, and daily prices are collected from the next day. - Basis. History is for 2-night stays, 2 adults, in USD. For other stays, the statistics still describe the hotel's price level, and the
messagesays so. - Retention. Up to 120 days of history are kept.
See samples/history_sample.json for an example output (synthetic prices).
Pricing
This Actor uses pay-per-event pricing, with no start fee and no monthly rental:
| Event | Price | When it's charged |
|---|---|---|
hotel | $1.00 per 1,000 | Once per hotel row that has a price |
hotel-prices | $2.00 per 1,000 | Once per hotel row with a successfully fetched, non-empty OTA price list |
hotel-history | $0.003 per hotel | Only with Add price history: once per hotel whose history has at least one observation |
- 1,000 hotels with full OTA prices: $3.00. Without OTA prices: $1.00.
- Not charged: history rows without data yet (
tracking-started,not-tracked,unavailable), hotels that fail to load, hotels without any rates for your dates (output withlowestPrice: null), failed or empty OTA price lists, and hotels skipped as unchanged in alert mode. - The Actor respects your maximum cost per run and stops cleanly when that limit is reached.
- Platform usage (compute and proxies, including residential fallback) is included in the event price.
Reliability and proxies
- Default proxy. Apify datacenter proxy (US).
- Retries. Any request that is blocked (captcha or HTTP 429), errors, or returns no data is retried on a new IP. If it still fails, it is retried through US residential proxy.
- Persistent blocking. If datacenter IPs are blocked consistently, the run switches to residential and re-checks the datacenter path from time to time.
- Monitored daily. A small multi-city test run checks the scraper every day so that changes on Google's side are caught early.
- Run statistics. Each run writes a
RUN_STATSrecord: hotels found, saved and not charged, OTA price-list success rate, requests, blocks, residential fallbacks and bandwidth. - Your own proxy. You can choose a different proxy in the input, for example your own residential group.
FAQ
Is scraping Google Hotels legal? This Actor collects publicly available price information, without logging in. You are responsible for using the data in line with applicable laws and Google's terms. Don't use it to collect personal data.
Why do some hotels have no price?
Some listings, often vacation rentals or small guesthouses, have no bookable rates for your dates. Google shows them without prices, and so does this scraper (lowestPrice: null, providerPrices: []). These rows are free.
Is the price per night or for the whole stay?
lowestPrice and providerPrices[].price are per night. totalPrice is for the whole stay, as shown by Google, before any taxes that Google doesn't display.
Can I get prices in EUR or GBP? Yes. Set Currency. USD is the default.
Why is the hotel order different between runs?
Google personalizes and shuffles rankings slightly. Use entityId as the stable hotel key. Alert mode already does this.
Can I scrape many dates for one hotel? Yes. Run the Actor once per date range, for example with a schedule or through the API, using Hotel names.
Support
Found a problem or need a field that's missing? Open an issue on the Actor's Issues tab. Issues are usually answered within a day.