# Google Hotels Scraper (`sz022/google-hotels-scraper`) Actor

Scrape Google Hotels for any city or hotel: prices per night and total for your dates, guests and currency, deals, ratings, review topics, amenities, location scores and photos. Add every booking site's rate, typical price range, cheaper dates and highlights. Export to CSV, Excel or JSON.

- **URL**: https://apify.com/sz022/google-hotels-scraper.md
- **Developed by:** [CHEN-SHU-CHEN](https://apify.com/sz022) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 hotel results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What is Google Hotels Scraper?

Google Hotels shows prices from every booking site side by side, but only one city, one set of dates and one screen at a time. Comparing a few cities, tracking prices for a trip, or building a list of hotels with their ratings means copying numbers by hand.

Type a **city or a search** ("hotels in tokyo", "beach resorts in Bali") or paste a **Google Hotels URL**, pick your **dates, guests and currency**, and get **one row per hotel**: price per night and for the whole stay, with taxes split out, deal badges ("38% less than usual"), star class, Google rating and review count, the 1–5 star split, what reviewers talk about, location scores, check-in and check-out times, nearby sights and airports with travel times, amenities, website, coordinates and photos.

Turn on **hotel details** and each row also gets **every booking site's price** with its link, which one is the **official site**, member rates, **free-cancellation deadlines**, taxes, the **typical price range** and where today's price sits in it, **cheaper dates** nearby, Google's highlights with the **reviews behind them**, **Tripadvisor and other sites' ratings**, the full amenity list, address, phone and **similar hotels nearby**.

Because it runs on Apify, you can **schedule** a daily price check, call it through the **API**, connect it to Make, Zapier or Google Sheets, and export the table as **CSV, Excel or JSON**.

### How to scrape Google Hotels

1. Open the **Input** tab and type one search per line into **Locations, searches or Google Hotels URLs**: `Tokyo`, `hotels in Shinjuku`, a search URL copied from google.com/travel, or a hotel's own page (`https://www.google.com/travel/hotels/entity/...`).
2. Set **Check-in** and **Check-out**. Leave them empty for a two-night stay 30 days from today. For scheduled runs, relative dates such as `+30 days` keep working every day.
3. Set **Adults**, **Children's ages** and **Currency**.
4. Choose **Hotels per search** (20 is about one Google page).
5. Optional: turn on **Add hotel details**, set filters (guest rating, stars, price, sort).
6. Click **Start**. A page of 20 hotels takes about 3 seconds; details add about one second per hotel.

### What data does Google Hotels Scraper return?

| Field | Notes |
|---|---|
| `status`, `error`, `notes` | `ok`, `not_found`, `blocked`, `parse_error`, `invalid_input`, `not_processed`; `notes` says when a price is missing or Google answered in another currency |
| `query`, `input`, `inputIndex`, `position`, `page` | Which input the row answers, and its rank in Google's order |
| `name`, `entityToken`, `mapsCid`, `featureId`, `placeId` | Ids for joining with Google Maps data; `placeId` with details or for hotel URLs |
| `googleHotelsUrl`, `googleMapsUrl`, `website` | The Google Hotels link opens at your dates and currency |
| `latitude`, `longitude`, `address`, `phone`, `countryCode`, `neighborhood` | Address and phone come with details, or for hotel URLs |
| `hotelClass`, `hotelClassText`, `propertyType`, `propertyTypes` | Official stars; hotel, hostel, ryokan, vacation rental... |
| `rating`, `reviewCount`, `ratingDistribution` | Google rating, and the percent and count of each star |
| `reviewTopics` | What reviews mention: name, mentions, positive, negative, sentiment |
| `locationScores` | `overall`, `thingsToDo`, `restaurants`, `transit`, `airportAccess`, 0–5 |
| `checkInTime`, `checkOutTime`, `nearbyPlaces` | Nearby sights, transit, airports and restaurants with walking, taxi or transit times |
| `currency`, `pricePerNight`, `priceTotal`, `priceBeforeTaxesTotal`, `taxesTotal`, `otherFeesTotal` | Google's lowest price for your stay; the display strings too (`pricePerNightDisplay`) |
| `pricedCheckIn`, `pricedCheckOut`, `checkIn`, `checkOut`, `nights`, `adults`, `childrenAges`, `requestedCurrency` | What Google priced, next to what you asked for |
| `deal` | `{"text": "38% less than usual", "label": "GREAT DEAL", "percentBelowUsual": 38}` |
| `ecoCertified`, `sponsored`, `amenities`, `amenityCodes`, `essentialInfo` | `essentialInfo` is "Sleeps 4", "2 bedrooms" for vacation rentals |
| `description`, `photos`, `thumbnail` | Photo URLs with size, and the booking site's original where Google has it |

#### With hotel details (`includeDetails`)

| Field | Notes |
|---|---|
| `rates` | Every booking site: price per night and total, `isOfficialSite`, `memberRateBadges` ("Member rate, save 5%") and the price before it, `freeCancellation` and `freeCancellationUntil`, `taxesPerNight` where Google shows it, and the booking link |
| `featuredOffers` | Featured sites with their rooms: room name, bed and breakfast details, price |
| `typicalPrice` | `{"low": 178, "high": 312, "current": 234, "positionInRange": 0.45, "level": "typical"}` |
| `priceCalendar`, `cheaperDates` | The same stay length starting each day a week either side, and the up to five cheapest of those that beat your dates, with the saving |
| `thingsToKnow`, `reviewHighlights` | Google's highlights; each review highlight carries the reviews it is based on (author, rating, text, date) |
| `reviewSummary`, `reviewSnippets`, `otherSiteRatings` | Tripadvisor, Trip.com and others: rating, review count, link and latest review |
| `amenitiesDetailed` | Every amenity by group (Internet, Pools, Food & drink...), with free/extra charge, what the hotel lacks ("No pools"), and sustainability practices |
| `longDescription`, `placeId`, `address`, `phone` | |
| `similarHotels`, `popularHotelsNearby`, `vacationRentalsNearby`, `sponsoredSimilarHotels` | Name, id, rating, price and distance of each |
| `detailsStatus`, `detailsError` | `ok`, or why the details could not be read (that hotel's details are then not charged) |

**Unknown is `null`, never `0`.** A hotel with no price for your dates has `pricePerNight: null` and a note.

### How much does it cost to scrape Google Hotels?

You pay per hotel row, not per page or per minute.

| Event | When | Price | Per 1,000 hotels |
|---|---|---|---|
| `hotel-result` | Each hotel row with `status: ok` | $0.004 | $4 |
| `hotel-details` | On top, for each row whose details were read (`detailsStatus: ok`) | $0.003 | $3 |

So 1,000 hotels cost $4, or $7 with details. Rows that are `not_found`, `blocked`, `parse_error`, `invalid_input` or `not_processed` are free, and so are hotels skipped by the minimum price.

#### Is it free to try?

Apify's free plan includes $5 of usage a month: about 1,250 hotels, or 700 with details.

#### How do I keep a run from costing more than I expect?

Set a maximum charge for the run. Each hotel row is written and charged in one step, and only if the remaining budget covers it, so you are never charged for a row you did not get. A hotel's details are read only when the budget covers the hotel and its details together, and are charged once, after their row is in the dataset. When the budget runs out, the run stops and every input it did not reach gets a `not_processed` row. If charging fails, the run stops the same way rather than keep working unbilled.

#### What happens if Apify restarts a run?

Progress is saved before and after each row. After a restart, a row that was already written is neither written nor charged again, and a row that was not written yet is read again and charged once. Searches already finished are skipped. The run also stops about 90 seconds before its timeout, so the remaining inputs still get their `not_processed` rows.

### Input example

```json
{
  "queries": ["hotels in tokyo", "https://www.google.com/travel/hotels/entity/ChUIuILA_uqFna0LGgkvbS8wMzV3X3AQAQ"],
  "checkInDate": "2026-11-17",
  "checkOutDate": "2026-11-19",
  "adults": 2,
  "childrenAges": [6],
  "currency": "EUR",
  "maxHotelsPerQuery": 40,
  "minRating": "4.0",
  "includeDetails": true
}
```

### Output example

One row from a run on 2026-10-18, trimmed:

```json
{
  "status": "ok",
  "query": "hotels in paris",
  "position": 1,
  "name": "Hilton Garden Inn Paris La Villette",
  "hotelClass": 4,
  "rating": 4.4,
  "reviewCount": 519,
  "locationScores": {"overall": 3.3, "thingsToDo": 3.6, "restaurants": 3.2, "transit": 4.4, "airportAccess": 4.4},
  "currency": "GBP",
  "pricePerNight": 192.87,
  "priceTotal": 578.61,
  "priceBeforeTaxesTotal": 487.06,
  "taxesTotal": 91.55,
  "deal": {"text": "17% less than usual", "label": "DEAL", "percentBelowUsual": 17},
  "ecoCertified": true,
  "amenities": ["Paid breakfast", "Free Wi-Fi", "Air conditioning", "Pet-friendly", "Fitness center"],
  "typicalPrice": {"low": 184.0, "high": 279.0, "current": 193.0, "positionInRange": 0.256, "level": "typical"},
  "cheaperDates": [{"checkIn": "2026-12-11", "checkOut": "2026-12-14", "pricePerNight": 178.5, "savingsPerNight": 14.37}],
  "rates": [{"site": "Booking.com", "isOfficialSite": false, "pricePerNight": 192.87, "taxesPerNight": 30.52}]
}
```

### FAQ

#### Are the prices for my dates and currency?

Yes. Dates, guests and currency go into the request itself, the same way Google's own page sends them, and each row says which dates Google priced (`pricedCheckIn`, `pricedCheckOut`) and in which `currency`. If Google ever answers for other dates or in another currency, the row says so in `notes`.

#### Why are some taxes per booking site null?

Google shows a site's tax only for the featured offers at the top of the price list. The other sites' prices already include taxes, but Google does not split them. The stay's overall split is always in `priceBeforeTaxesTotal` and `taxesTotal`.

#### Why does a minimum price take longer?

Google's price filter only takes a maximum. A minimum is applied after reading, so cheaper hotels are read, skipped and not charged, and more pages may be needed to fill **Hotels per search**.

#### What can it not return?

- **Individual Google reviews.** The highlights carry the reviews Google picked to support them; a full review list is a different tool.
- **Prices hidden behind sign-in.** Member prices that only appear when logged in to a booking site are not visible to Google either.
- **Amenity labels in other languages.** Labels are English whatever `language` is set; hotel names, descriptions and reviews follow `language`.
- **Rating of a hotel looked up by URL in other languages.** For hotel URLs the overall rating is read from the page's English markup, so set `language` to `en` to get it.

#### Does it work from Europe?

Google shows a cookie notice to visitors from the EU. The Actor accepts it up front, and a request that still lands on the notice is retried on another IP. The default Apify datacenter proxy reads Google Hotels without it.

#### What happens when Google changes its pages?

Google Hotels sends its data as unlabelled arrays. The Actor finds each object by the type key Google gives it and checks every field's shape, so a changed layout gives `parse_error` rows instead of numbers in the wrong column. Amenity labels were mapped from codes on 2026-10-18; a code that is new since then is still in `amenityCodes`, with no label.

#### Something broke or a number looks wrong

Open an issue on the **Issues** tab with the input and the run link.

# Actor input Schema

## `queries` (type: `array`):

One per line. A place or a search as you would type it on Google Hotels (Tokyo, hotels in Shinjuku, beach resorts in Bali), a Google Hotels search URL, or a hotel's own Google Hotels URL (https://www.google.com/travel/hotels/entity/...) to look up that one hotel. Up to 500 lines per run.

## `checkInDate` (type: `string`):

YYYY-MM-DD, or a relative date such as +30 days for scheduled runs. Leave empty for 30 days from today.

## `checkOutDate` (type: `string`):

YYYY-MM-DD or relative (+32 days). Leave empty for two nights after check-in. Stays of up to 30 nights.

## `adults` (type: `integer`):

Guests aged 18 or over.

## `childrenAges` (type: `array`):

One age (0-17) per child, for example \[4, 9]. Leave empty for no children.

## `currency` (type: `string`):

Three-letter code: USD, EUR, GBP, JPY, AUD, CAD, INR and about 70 more that Google Hotels offers. Every price in the output is in this currency, and each row's currency field confirms it.

## `maxHotelsPerQuery` (type: `integer`):

How many hotels to return for each search, in Google's order. Google shows about 20 per page; each further page takes about 3 seconds. Up to 1,000.

## `includeDetails` (type: `boolean`):

Reads each hotel's own page as well: every booking site's price with its link, the official-site flag, member rates, free-cancellation deadlines, taxes, the typical price range and where today's price sits in it, cheaper dates nearby, Google's highlights with the reviews behind them, ratings from Tripadvisor and other sites, the full amenity list, address, phone and nearby similar hotels. Adds about one second and the hotel-details event per hotel.

## `includeSponsored` (type: `boolean`):

Also return the paid listings Google shows above the results (marked sponsored: true). They carry fewer fields, and are charged like any other hotel row. A hotel already in the regular results is not repeated.

## `minRating` (type: `string`):

Google's own guest-rating filter.

## `hotelClass` (type: `array`):

Only hotels with these official star ratings. Leave empty for all, including vacation rentals and unrated places.

## `maxPrice` (type: `integer`):

In the currency above. Google's own price filter.

## `minPrice` (type: `integer`):

In the currency above. Google ignores a minimum, so the Actor skips cheaper hotels after reading them; skipped hotels are not charged and do not count toward Hotels per search.

## `sortBy` (type: `string`):

Google's sort order for the results.

## `language` (type: `string`):

Language of hotel names, descriptions and reviews, as a Google language code (en, de, fr, es, ja, zh-TW...). Amenity labels are always English. The overall rating of a hotel looked up by URL is only read in English.

## `country` (type: `string`):

Two-letter country code Google treats you as searching from. It decides which booking sites are listed. Prices stay in the currency above.

## `proxyConfiguration` (type: `object`):

Apify's datacenter proxy, which read Google Hotels in every test on the platform. Residential and Google SERP groups are not used: a Google Hotels page is 2-3 MB, and the price per hotel is set for datacenter traffic, so a run that asks for them falls back to the datacenter proxy and says so in the log. Proxy URLs of your own are used as given.

## Actor input object example

```json
{
  "queries": [
    "hotels in tokyo"
  ],
  "adults": 2,
  "childrenAges": [],
  "currency": "USD",
  "maxHotelsPerQuery": 10,
  "includeDetails": false,
  "includeSponsored": false,
  "minRating": "any",
  "sortBy": "relevance",
  "language": "en",
  "country": "us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `hotels` (type: `string`):

Prices for the requested dates, guests and currency, ratings, amenities, location data and photos; with details on, every booking site's rate, price insights and highlights.

## `failedInputs` (type: `string`):

Searches and hotel URLs that were invalid, not found, blocked or not processed, with the reason for each. Also present in the dataset itself.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "hotels in tokyo"
    ],
    "maxHotelsPerQuery": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("sz022/google-hotels-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["hotels in tokyo"],
    "maxHotelsPerQuery": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("sz022/google-hotels-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "hotels in tokyo"
  ],
  "maxHotelsPerQuery": 10
}' |
apify call sz022/google-hotels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sz022/google-hotels-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Fo2laKcnzj8X1MQwU/builds/GrU4f5xv2CHww7gMI/openapi.json
