# Google Hotels Scraper - Prices, Price Changes & Rate Monitor (`neverempty/google-hotels-scraper`) Actor

For travel agencies, revenue managers and price-tracking apps: hotel name, nightly and total price, taxes, rating, reviews, stars, amenities and booking sites from Google Hotels in 72 currencies. 10 of 10 prices matched the Google Hotels page. Monitoring returns only price changes and new hotels.

- **URL**: https://apify.com/neverempty/google-hotels-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Travel, Automation, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 hotel returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Google Hotels Scraper - Prices, Price Changes & Rate Monitor

For travel agencies, hotel revenue managers and price-tracking apps: every hotel Google Hotels lists for a city, district or landmark, with the nightly and total price, taxes and fees, currency, rating, review count, stars, amenities, coordinates and, optionally, every booking site and its price. In a check on 2026-09-21, 10 of 10 nightly prices matched the Google Hotels page opened in a browser for the same search. Set it on a schedule in monitoring mode and you get back only hotels whose price moved or that are new to your watch - no diffing on your side.

Unofficial. Public data only.

Export as JSON, CSV or Excel.

### What you get

- **One row per hotel** for each location: name, property type (hotel or vacation rental), stars, guest rating and review count, the nightly price and the stay total as Google shows them, the stay before taxes, the taxes and fees, and the stay with taxes.
- **Any of Google's 72 currencies** (`currency`), any stay up to 30 nights (`checkInDate`, `checkOutDate`) and 1-12 adults (`adults`). Every row repeats the dates and guests Google actually priced and says whether they match what you asked for (`stayAsRequested`), so a run never silently hands you a different stay.
- **Sorting and nightly price limits applied by Google itself** (`sortBy`, `minPrice`, `maxPrice`) - the same controls as the site's sort menu and price slider.
- **Booking sites** (`includeBookingSources`): Booking.com, Expedia, Agoda, the hotel's own site and every other site Google lists, each with its nightly and total price, cheapest first.
- **Monitoring mode** (`monitoringMode`): only hotels whose nightly price moved by at least `minPriceChangePercent` since the row last returned for them, or that are new to this watch, with the previous price in the same row.
- **No failed runs for "nothing found".** An empty search, a place Google reads differently, a refused request or a spending limit each come back as a free row that says why.

### Input

| Field | What it does |
|---|---|
| `locations` | Places to search, one per entry: a city (`Paris`), a district (`Shibuya, Tokyo`), a landmark (`Times Square`) or a hotel name. Each row carries `resolvedLocation`, the place Google actually searched. |
| `checkInDate` | `YYYY-MM-DD`, or `+N` for N days from today in UTC. The form is prefilled with `+30`, so a saved task never falls into the past. Empty = 30 days from today. Dates before yesterday (UTC), and arrivals more than 330 days ahead (Google showed no prices that far out in a 2026-09-21 check), are refused before anything is requested. |
| `checkOutDate` | `YYYY-MM-DD` or `+N`. Prefilled with `+32`. Empty = 1 night after the check-in you gave (2 nights when check-in is empty too). At most 30 nights. |
| `adults` | Guests the price is for, 1-12. Empty = 2. |
| `currency` | One of the 72 currencies Google Hotels offers. Empty = USD. |
| `sortBy` | `lowest-price` (empty = this), `relevance` (Google's default order), `highest-rating` or `most-reviewed`. |
| `minPrice` / `maxPrice` | Nightly price limits in your currency, applied by Google. |
| `maxHotels` | Hotels to read per location, 1-1000. Empty = 50. Google lists about 18-20 per page. |
| `includeBookingSources` | Also read each hotel's price page and list every booking site. One extra page per hotel, so the run is slower. |
| `monitoringMode` | Return only hotels that are new to this watch or whose price moved. |
| `minPriceChangePercent` | Monitoring only. Smallest move that counts, in percent of the price last returned. Empty = 2. |
| `resetMonitoringState` | Forget what this search remembered; the next monitoring run returns every hotel as new. |

A search written as "hotels near X" or "hotels in X" sometimes gets a page where Google loads the list later in the browser. The Actor then searches the place alone ("X") once, and says so in the log and in `googleQuery`.

### Output

```json
{
  "status": "ok",
  "hotelId": "8082771210398315600",
  "hotelName": "St Christopher's Paris - Canal",
  "propertyType": "hotel",
  "propertyTypeCode": 1,
  "hotelClass": null,
  "stars": null,
  "rating": 3.9,
  "reviewCount": 3971,
  "pricePerNight": 41,
  "pricePerNightText": "$41",
  "totalPriceText": "$83",
  "pricesShownIncludeTaxes": false,
  "totalBeforeTaxes": 82.89,
  "taxesAndFees": 8.31,
  "totalWithTaxes": 91.2,
  "currency": "USD",
  "priceShown": true,
  "deal": "35% less than usual",
  "checkInDate": "2026-11-10",
  "checkOutDate": "2026-11-12",
  "nights": 2,
  "adults": 2,
  "stayAsRequested": true,
  "amenities": ["Breakfast ($)", "Free Wi-Fi", "Parking", "Bar", "Accessible", "Smoke-free property"],
  "description": "Waterfront hostel with free Wi-Fi & a common lounge, plus a lively bar/restaurant & a nightclub.",
  "latitude": 48.888504999999995,
  "longitude": 2.3790066,
  "checkInTime": "2:00 PM",
  "checkOutTime": "11:00 AM",
  "position": 2,
  "searchQuery": "Paris",
  "googleQuery": "Paris",
  "resolvedLocation": "Paris",
  "googleHotelUrl": "https://www.google.com/travel/hotels/entity/ChkIvOHd86zKwZeBARoML2cvMTF4ZHB2Njd2EAE?q=Paris&hl=en&gl=us&ts=CAESCgoCCAMKAggDEAEaHBIaEhQKBwjqDxALGAoSBwjqDxALGAwYAjICCAEqCQoHKAM6A1VTRA",
  "googleMapsUrl": "https://www.google.com/maps?cid=8082771210398315600",
  "entityToken": "ChkIvOHd86zKwZeBARoML2cvMTF4ZHB2Njd2EAE",
  "scrapedAt": "2026-09-21T09:36:26.976Z",
  "bookingSources": [
    { "source": "St Christopher's Paris - Canal", "isOfficialSite": true, "pricePerNight": 41, "pricePerNightText": "$41", "totalPrice": 82.89, "totalPriceText": "$83" },
    { "source": "Booking.com", "isOfficialSite": false, "pricePerNight": 44, "pricePerNightText": "$44", "totalPrice": 87.16, "totalPriceText": "$87" }
  ],
  "bookingSourcesStatus": "ok"
}
```

#### About the prices

- `pricePerNight` and `totalPriceText` are exactly what Google Hotels shows for the cheapest offer. This Actor reads Google as a visitor in the United States, where Google shows prices **before taxes and fees**; `pricesShownIncludeTaxes` says which way each row is.
- `totalBeforeTaxes`, `taxesAndFees` and `totalWithTaxes` come from Google's own price breakdown. They are filled only when that breakdown belongs to the same offer as the shown price, and are `null` otherwise (in the 2026-09-21 checks: 32 of 40 priced hotels in Paris, 14 of 27 in New York and Tokyo). A number from a different offer is never mixed in.
- Hotels for which Google shows no price for your stay are still returned, with the prices `null` and `priceShown: false`.
- `bookingSourcesStatus` is `ok`, `none-shown` (Google listed no booking site), `unreadable` or `not-requested`. A hotel whose booking sites could not be read because Google stopped answering is not returned at all (see `blocked` below). Booking-site prices are on the same basis as the hotel's shown price.
- `propertyTypeCode` is Google's raw code (1 = hotel, 2 = vacation rental).

#### Monitoring rows

In monitoring mode each returned row also has `change` and the price last returned for that hotel in this watch:

- `"new"` - first time this watch returns the hotel (the first run returns every hotel as new). It means new to your watch, not newly listed on Google.
- `"price-down"` / `"price-up"` - the nightly price moved by at least `minPriceChangePercent` from the price last returned. Google's shown price can flip by 1 between reads (151 / 152); comparing with the price last returned, not the last one seen, stops those flips from being reported again and again.
- `"price-shown"` - the hotel had no price when last returned and has one now.

`previousPricePerNight`, `previousTotalPriceText` and `previousReturnedAt` carry the earlier values. A hotel that shows no price in this run, or that is missing from this run, is not reported. What is remembered is per search (location, dates as you wrote them, adults, currency, sort and price limits); with `+N` dates the watched stay moves forward every day, so a change can come from the new dates. Use a sorted order for monitoring: with `relevance`, Google shows a different mix of hotels on each read. Do not put the same search in two overlapping schedules - the key-value store has no atomic update, so a lost record cannot be fully prevented, only made unlikely.

#### Free rows (not charged)

| `status` | When |
|---|---|
| `no-results` | Google lists no hotel for the search (for example a nightly price range nothing fits). |
| `location-resolved-elsewhere` | Google searched a place whose name shares no word with what you wrote (for example a misspelling read as "United States"); check that it is the place you meant. The hotels for that place are still returned. Not raised for abbreviations like NYC or names in non-Latin scripts. |
| `list-not-in-page` | For this wording Google loads the hotel list later in the browser; write the place alone. |
| `blocked` | Google showed a check page or refused the request. The Actor does not try to get around Google's checks, so it stops the run there instead of guessing. Hotels whose booking sites could not be read because of this are not returned and not charged; the row says how many, and which locations were not searched. |
| `unreadable` | Google's answer could not be read. Nothing is guessed and no hotel is reported as missing. |
| `bad-input` | The input could not be used (a past date, an unknown currency...). Nothing was requested. |
| `no-change` | Monitoring: nothing moved since the rows last returned. The check fee applies to the hotels compared. |
| `budget-reached` | The run hit its maximum total charge; says what was not returned and that it was not charged. |

### Pricing

- **Hotel row: $4.99 per 1,000** on the Free plan, lower on paid Apify plans (down to $2.99 per 1,000). Booking sites are included in the row price.
- **Monitoring check: $0.30 per 1,000** hotels compared in monitoring mode on the Free plan (lower on paid plans), whether or not the hotel changed. A hotel is compared only if the run's maximum total charge leaves room for its check and one hotel row.
- Free rows cost nothing.

### Limits

- Prices are what Google shows a visitor from the United States on the day of the run; hotel prices change often.
- Google lists about 18-20 hotels per page; pages can overlap and the Actor drops repeats (`hotelId`).
- Speed measured on 2026-09-21: about 5 seconds per page of hotels and about 3 seconds per hotel for booking sites. A very large run (for example 1,000 hotels with booking sites, or many locations of 1,000 hotels) can pass the default run timeout of one hour; raise the timeout for such runs.
- Rating, reviews, stars, amenities, times and descriptions are only as complete as Google's listing (in the 2026-09-21 Paris check of 40 hotels: no star class for 24, no check-in time for 13, no description for 6).

# Actor input Schema

## `locations` (type: `array`):

Where to look, written as you would type it into Google Hotels: a city (Paris), a district (Shibuya, Tokyo), a landmark (hotels near Times Square) or a hotel name. Each entry is one search. Every row says which place Google actually searched (resolvedLocation); if Google reads your text as a different place, a free row says so.

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

YYYY-MM-DD (for example 2026-12-03), or +N for N days from today in UTC (+30 = 30 days from today). Dates before yesterday (UTC) and arrivals more than 330 days ahead (Google shows no prices that far out) are refused before anything is requested. Empty = 30 days from today.

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

YYYY-MM-DD or +N days from today. Must be after check-in; at most 30 nights. Empty = 1 night after the check-in you gave (2 nights when check-in is empty too).

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

Guests the price is for. Empty = 2. Every row repeats the dates and guests Google priced (stayAsRequested says whether they match what you asked for).

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

Currency of every price, from the list Google Hotels offers. Empty = USD. Each row carries the currency Google actually used.

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

Order Google lists hotels in. Empty = lowest price. Relevance shows a different mix of hotels on each read, so use a sorted order for monitoring.

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

Keep hotels at or above this nightly price, in the chosen currency. Applied by Google, as the price slider on the site.

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

Keep hotels at or below this nightly price, in the chosen currency. Applied by Google, as the price slider on the site.

## `maxHotels` (type: `integer`):

How many hotels to read for each location. Google lists about 18-20 per page. Empty = 50. In monitoring mode this is how many hotels are watched.

## `includeBookingSources` (type: `boolean`):

Also open each hotel's price page and list every booking site Google shows (Booking.com, Expedia, the hotel's own site...) with its nightly and total price. One extra page per hotel, so runs are slower. Off = no extra pages.

## `monitoringMode` (type: `boolean`):

Return only hotels that are new to this watch or whose nightly price moved since the row last returned for them (price-up, price-down, price-shown). Unchanged hotels are not returned; each hotel compared is charged the small check fee. The first run returns every hotel as new.

## `minPriceChangePercent` (type: `number`):

Monitoring only. A price counts as changed when it moved at least this much from the price last returned. Empty = 2. Google's shown price can flip by 1 between reads (151 / 152); 0 reports every flip.

## `resetMonitoringState` (type: `boolean`):

Forget what this search remembered, so the next monitoring run returns every hotel as new.

## Actor input object example

```json
{
  "locations": [
    "Paris"
  ],
  "checkInDate": "+30",
  "checkOutDate": "+32"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per hotel found on Google Hotels for each location: name, property type, stars, rating and review count, nightly price as shown, total price, price before taxes and taxes and fees, currency, the dates and guests Google priced, amenities, description, coordinates, check-in and check-out times, Google Hotels and Google Maps links, and optionally every booking site with its price. In monitoring mode, only hotels new to the watch or whose price moved, with the price last returned. Searches with no hotels, places Google read differently, refused requests and runs that hit their maximum charge come back as free rows that say why.

# 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 = {
    "locations": [
        "Paris"
    ],
    "checkInDate": "+30",
    "checkOutDate": "+32"
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/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 = {
    "locations": ["Paris"],
    "checkInDate": "+30",
    "checkOutDate": "+32",
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/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 '{
  "locations": [
    "Paris"
  ],
  "checkInDate": "+30",
  "checkOutDate": "+32"
}' |
apify call neverempty/google-hotels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/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/GekMB5JETKAKxCtPE/builds/albJu91KDDlXv63rN/openapi.json
