Google Hotels Scraper - Hotel Prices Across Every Booking Site
Pricing
$0.99 / 1,000 hotels
Google Hotels Scraper - Hotel Prices Across Every Booking Site
Search Google Hotels by destination and dates: hotel listings plus the price every booking site quoted for the same stay.
Pricing
$0.99 / 1,000 hotels
Rating
0.0
(0)
Developer
Lantern Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Google Hotels Scraper
Give it a destination and your dates and get Google Hotels' public results back: the hotel, its class, rating, review count, coordinates, address, amenities, photos, the cheapest nightly price and stay total - and prices, the quote every booking site gave for that same stay, with Google's own outbound link for each. Your dates, guest count and currency are honoured, not approximated. No Google login, no cookies, no account of your own. $0.99 per 1,000 hotels, no start fee; every booking site's price is included. See Pricing.
Input:
{"queries": ["hotels in Paris", "hotels in Tokyo"],"checkIn": "2026-11-10","checkOut": "2026-11-12","adults": 2,"currency": "USD","maxItems": 40}
Output - one row per hotel (recordType: "hotel"); per-destination status rows and the run summary are free. Sample from a real run, photo and link URLs shortened:
{"recordType": "hotel","hotelId": "ChgI9LOche-i1LjzARoLL2cvMXRkM2t4YjMQAQ","featureId": "0x47e66d84b6c685f7:0xf3715116f0a719f4","name": "Hôtel Belgrand","url": "https://www.google.com/travel/hotels/entity/ChgI9LOche-i1LjzARoLL2cvMXRkM2t4YjMQAQ","starClass": 3,"starClassText": "3-star tourist hotel","rating": 3.4,"reviewCount": 440,"address": "35 Rue Belgrand, 75020 Paris, France","lat": 48.8644845,"lng": 2.4062453,"countryCode": "FR","lowestPrice": 86.2708,"lowestPriceText": "$86","totalPrice": 173,"totalPriceText": "$173","currency": "USD","prices": [{ "provider": "Booking.com", "price": 86.27, "priceText": "$86", "totalPrice": 173, "totalPriceText": "$173", "link": "https://www.google.com/travel/lodging/clk?pc=AA80Osw6…" },{ "provider": "Expedia.com", "price": 92.5, "priceText": "$93", "totalPrice": 185, "totalPriceText": "$185", "link": "https://www.google.com/travel/lodging/clk?pc=AA80OsyR…" }],"pricesStatus": "ok","amenities": ["Breakfast ($)", "Free Wi-Fi", "Parking", "Restaurant", "Accessible", "Kid-friendly", "Smoke-free property"],"imageUrls": ["https://lh3.googleusercontent.com/grass-cs/AABkmLcNHAM7…"],"description": "Unassuming rooms with flat-screens in a straightforward budget hotel offering complimentary Wi-Fi.","website": "https://hotel-belgrand-paris.example-hotel-site.fr/","checkIn": "2026-11-10","checkOut": "2026-11-12","nights": 2,"adults": 2,"pricedCheckIn": "2026-11-10","pricedCheckOut": "2026-11-12","pricedAdults": 2,"query": "hotels in Paris","destination": "Paris","destinationId": "0x47e66e1f06e2b70f:0x40b82c3688c9460","hl": "en","gl": "us","rank": 1,"partial": false,"partialReason": null,"publishedAt": null,"collectedAt": "2026-10-24T10:41:22.318Z"}
Limits:
- About 20 hotels per destination. That is one Google Hotels results page, and Google loads the rest only through an in-page call that needs a signed browser session. Use several narrower destinations ("hotels in Montmartre, Paris", "hotels near Gare du Nord") to cover a big city. Every destination row says how many Google listed (
hotelsAvailable) and whether its page was full (pageLimitReached). - Prices move, and
lowestPriceis not the cheapest row inprices.lowestPrice/totalPriceare what Google printed on the results page - its featured rate for that hotel.prices[]comes from the hotel's own page a moment later and regularly contains a cheaper quote, which is the point of fetching it. Both are kept as read; neither is recomputed from the other. A row is a quote atcollectedAtfor the stay incheckIn/checkOut, not a reservation - re-run for current prices, and expect different prices from a differentgl. - How many booking sites appear is Google's choice, not a completeness guarantee. Measured 2026-10-24: 13-31 offers per hotel for Paris, Tokyo, Bangkok, Dubai and Singapore, exactly 1 (the hotel's own site) for Moscow hotels, and occasionally a short list where a repeat of the same request returned the full one.
prices[]is what the hotel page carried when it was read;pricesStatustells you whether it could be read at all. - Public data only. The public Google Hotels result page and hotel page; no login, no reviews text, no room-level inventory, no guest names or contact details.
- Every destination gets a status row saying what happened. A destination Google answered with nothing is an empty result; one cut short by a limit, refusal or error is
partialwith the reason - the two are never confused. - Your limits stop collection and appear in the row:
maxItems, themaxUpstreamRequestsrequest ceiling and the maximum charge per run (see Pricing).
What it does
- Searches each destination separately and labels every row with the destination that produced it, so one run can cover many cities or districts.
- Sends your stay exactly as Google's own date picker does, so the prices are for your dates, your guest count and your currency - not for Google's defaults. Google ignores the
checkin=/checkout=/curr=parameters people usually try; this Actor does not use them. - Checks Google's answer against what was asked. Every row carries
pricedCheckIn/pricedCheckOut/pricedAdults- the stay Google says it priced. If it ever differs from yours, the rows are markedpartialwithpartialReason: "stay_not_applied"and not charged, instead of being handed to you as prices for the wrong dates. - Fetches each hotel's own page for the street address and the full
priceslist - one row per booking site, as Google ordered them, each with its nightly price, its stay total and Google's outbound link. - Deduplicates by
hotelIdacross the whole run, so a hotel that matches two of your destinations is written and charged once. - Says which place Google decided your search term meant, in
destination. Google answers a term it cannot place with hotels somewhere else entirely rather than with nothing, and this is the field that shows it. - Writes
RUN_SUMMARYas the last dataset row and as a key-value store record: totals, empty destinations, incomplete destinations by reason, provider offers read, requests sent and why collection stopped.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
queries | string[] | required | One destination per line: a city, a district, a region, or a hotel name. |
checkIn | string | tomorrow | Check-in day, YYYY-MM-DD. Must be today or later, at most 330 days ahead. |
checkOut | string | day after check-in | Check-out day, YYYY-MM-DD. After check-in, at most 30 nights later. |
adults | integer | 2 | Guests the rooms are priced for, 1-6. |
currency | string | USD | Three-letter code the prices come back in, e.g. EUR, GBP, RUB, JPY. |
gl | string | us | Two-letter country code of the Google edition; changes which booking sites are listed. |
hl | string | en | Interface language; the language of amenity names, the class sentence and the description. |
maxItems | integer | 100 | Upper bound on hotel rows for the whole run. |
includeProviderPrices | boolean | true | Off: no prices list, no address, one request per destination instead of one per hotel. |
maxUpstreamRequests | integer | derived | Hard ceiling on requests to Google. Left empty it is worked out from your input, so a run is never unbounded. |
proxyConfiguration | object | Apify Proxy | Where the requests come from. The default datacenter proxy is what this Actor is measured on. |
The 330-day and 30-night bounds are Google's own, taken from its date picker; an input outside them fails before any request is sent, so it costs nothing.
Output
Three record types in one dataset, told apart by recordType:
hotel - one per hotel, the only charged row. prices[] holds { provider, price, priceText, totalPrice, totalPriceText, link } per booking site. A field Google did not provide is null, never guessed: starClass is absent for apartments and some vacation rentals, rating and reviewCount for places with no reviews yet, address on runs with includeProviderPrices off. price/totalPrice are numbers; priceText/totalPriceText are what Google displayed, kept verbatim.
pricesStatus says why prices is empty:
pricesStatus | Meaning |
|---|---|
ok | Offers were read. |
no_offers | Google showed no bookable offer for this hotel and stay. A real answer. |
not_requested | includeProviderPrices was off. |
upstream_request_cap | The run's request ceiling was reached before this hotel's page. |
blocked | Google refused the hotel page. |
request_failed / unreadable | The hotel page failed, or carried no readable payload. |
query_status - one per destination, never charged. hotelsReturned, hotelsCharged, duplicatesSkipped, hotelsAvailable, pageLimitReached, partial, partialReason, statusText, and destination (the place Google resolved the term to).
partialReason | Meaning |
|---|---|
null | Complete. hotelsReturned: 0 with partial: false means Google really listed no hotel. |
page_limit_reached | You had room for more rows and Google's one page ran out first. |
max_items_reached | maxItems was reached here. |
upstream_request_cap | The request ceiling was reached here. |
charge_limit_reached | The run's maximum charge was reached here. |
blocked | Google refused our requests for this destination. Not an empty result. |
upstream_error | A request failed in a way a retry inside the run would not fix. |
unreadable_page | Google answered with a page holding no readable hotel list. |
stay_not_applied | Google priced a stay other than yours. The rows are kept, marked, and not charged. |
not_collected | The run stopped earlier and never reached this destination. |
RUN_SUMMARY - the last row, and the RUN_SUMMARY key-value store record.
Pricing
$0.99 per 1,000 hotel rows. No start fee, no per-page fee, no charge for the booking-site prices.
- Only
hotelrows are charged. Destination status rows andRUN_SUMMARYare free, so a destination that returned nothing, was blocked or was cut short costs nothing. - Rows marked
partialare not charged. In practice that means a stay Google did not apply: you can see what came back without paying for prices that are not for your dates. - A hotel matching several of your destinations is charged once.
- Set Maximum cost per run in the run options to cap a run. Collection stops at the cap and the destination it stopped on is marked
charge_limit_reached- it is never reported as empty.
Notes
includeProviderPricesis the one setting that changes the cost of a run for us as well as the time: it is one extra request and roughly 25 times more traffic per hotel. The price above covers it either way; turning it off just makes runs faster.- Measured on Apify Proxy datacenter IPs. Google Hotels serves these public pages over them without a bot check, so no residential proxy is needed; you can still point
proxyConfigurationat one if you want a specific country's exit. hotelIdis Google Travel's own identifier and is stable across runs;featureIdis the Google Maps feature id for the same place, for joining to other Google data.