# Google Hotels Scraper & API: Price Tracker & Comparison (`garje/google-hotels-scraper`) Actor

Scrape Google Hotels prices for any city search across a range of check-in dates, through the Apify API or as CSV, in your currency. Hotel rate shopping, rate parity and hotel rate monitoring with booking-site offers. Works on Google Travel hotels; schedule it as a daily hotel price tracker.

- **URL**: https://apify.com/garje/google-hotels-scraper.md
- **Developed by:** [Aniruddha Garje](https://apify.com/garje) (community)
- **Categories:** Travel, E-commerce
- **Features:** Limited permissions
- **Stats:** 5 total users, 3 monthly users, 92.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 1,000 hotel prices

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

## Google Hotels Scraper: Prices, OTA Rates and Rate Parity

A Google Hotels scraper and API for hotel prices by date. Scrape Google Hotels (Google Travel) prices for any city search across a calendar of check-in dates, in the currency you choose. Every row carries the stay length and occupancy. Turn on offers to add the price each booking site shows (Booking.com, Expedia, Agoda, the hotel's own site and more) for rate parity checks. I built it to read the data Google embeds in its own pages with plain HTTP requests, with no browser.

### At a glance

| | |
|---|---|
| Use it when | You need Google Hotels nightly prices for a city over one or many check-in dates, optionally with each booking site's price: as a hotel price tracker, for hotel rate shopping and rate monitoring, or for hotel price comparison across booking sites. |
| Input you need | A search in `queries`, for example "hotels in Paris" (required); dates as `checkInFrom`/`checkInTo` or `checkInFromDaysAhead`/`checkInToDaysAhead`; `nights`, `adults`, `currency`, `country`. |
| What you get | One row per hotel and check-in date with nightly price, stay and occupancy echoed, and the place Google matched; with `includeOffers`, one row per booking site. |
| Cost | $1.99 per 1,000 hotel prices; $1.00 per 1,000 booking-site offers. |
| Limits | The result list can differ from what a browser shows. Named-hotel tracking by link is coming soon. |

I built this Actor and test it against what the live site shows. The check runs again every week.

### What you get

One row per hotel and check-in date (`type: "hotel"`):

| Field | Example |
|---|---|
| `name`, `hotelId`, `url` | `Oden Hôtel Paris Ivry`, Google Hotels link |
| `checkInDate`, `checkOutDate`, `nights` | `2026-11-12`, `2026-11-14`, `2` (as Google reported them) |
| `adults`, `children`, `childAges` | `2`, `0`, `[]` |
| `currency`, `pricePerNight`, `pricePerNightText`, `totalPriceText` | `USD`, `97.95`, `$98`, `$196` |
| `hotelClass`, `rating`, `reviewCount` | `4`, `4.7`, `158` |
| `latitude`, `longitude`, `countryCode`, `website`, `description` | |
| `dealText` | `16% less than usual` |
| `query`, `resolvedPlace`, `country` | The search, the place Google read it as (for example `Paris`), and the searcher country |

With **Include per-site offers** on, one extra row per booking site (`type: "offer"`) with `provider`, `pricePerNight` and `totalPrice` for that hotel and date.

### Sample output

One real row from a run on 11 Oct 2026 (selected fields):

```json
{
  "name": "Court Square Hotel Long Island City New York, Belvilla District 6 Formerly Sonder",
  "hotelClass": 4,
  "rating": 4,
  "reviewCount": 429,
  "checkInDate": "2026-11-10",
  "checkOutDate": "2026-11-12",
  "nights": 2,
  "adults": 2,
  "currency": "USD",
  "pricePerNight": 140.27,
  "totalPriceText": "$281",
  "country": "US",
  "url": "https://www.google.com/travel/hotels/entity/ChoIn8Lf9cvOi9DxARoNL2cvMTFycThtZ3g0bRAB"
}
```

### What this Actor handles for you

1. **Blocked pages are retried, never charged.** Google sometimes answers with a rate-limit page. This Actor detects it, switches to a fresh IP and retries, and it never saves or charges a blocked page. Each run writes `RUN_STATS` with pages fetched, blocked responses and failed requests, so you can see the success rate yourself.
2. **The stay length you asked for.** `nights` is sent to Google the same way Google's own date picker does, and the check-in date, check-out date and nights Google actually priced are read back from Google's data and echoed on every row.
3. **Filters.** Hotel class (for example 4 stars) and amenities (for example free Wi-Fi) are applied by Google itself, the same way its own filter panel applies them. Minimum rating and price range are applied to the results. There is no free-cancellation filter.
4. **A full date calendar, checked up front.** One search runs for every check-in date between `checkInFrom` and `checkInTo`, so the calendar has no gaps. Past dates are rejected before the run starts, with a clear message, so they cost nothing. For scheduled runs, set `checkInFromDaysAhead` and `checkInToDaysAhead` (days after the run date) instead of fixed dates, so the calendar moves forward with every run and never expires.
5. **Your currency.** Set a three-letter ISO 4217 currency code; measured with USD, INR, IDR and AED.

### Input

| Field | Default | What it does |
|---|---|---|
| `queries` | | City searches as typed into Google, for example `hotels in Paris` |
| `hotelUrls` | `[]` | Coming soon: named-hotel tracking. A run with hotel links stops at once and costs nothing |
| `checkInFrom`, `checkInTo` | tomorrow | Price calendar range as fixed dates |
| `checkInFromDaysAhead`, `checkInToDaysAhead` | | The same range as days after the run date, for scheduled runs |
| `nights` | 1 | Stay length |
| `adults`, `children`, `childrenAges` | 2, 0 | Occupancy |
| `currency` | `USD` | Three-letter ISO 4217 code, for example USD |
| `country` | `US` | Searcher country; also pins the proxy country, because Google's prices follow the requester's country |
| `filters` | `{}` | `hotelClass`, `amenities`, `minRating`, `priceMin`, `priceMax` |
| `includeOffers` | false | Per-site offers as a separate paid event |
| `includeUnavailable` | false | Also return hotels without a price for that date (`pricePerNight` null, `available` false). These rows are not charged |
| `maxHotelsPerQuery` | 50 | Hard cost cap per search and date |

### Pricing

Pay per result: **$1.99 per 1,000 hotel prices** and **$1.00 per 1,000 booking-site offers**. You pay only for rows saved to your dataset, never for errors, blocked pages or duplicates. If you set a maximum cost per run, the Actor stops cleanly when it is reached.

### Reliability

Each search is first matched to a place by Google, and every row carries that place as `resolvedPlace`. If Google does not recognise a search, it is skipped and not charged, and the run's status message lists it. Add a region to choose between places with the same name, for example "hotels in Springfield, Massachusetts".

The result list can differ from what a browser shows. Google changes its hotel list between visits: in our test on 8 October 2026, two browser visits 12 minutes apart had 87% of their hotels in common. Prices for the hotels returned matched the browser to within 10% in 99% of cases in the same test.

A restarted or migrated run resumes where it stopped and does not deliver or charge the same row twice.

### Named-hotel tracking: coming soon

Tracking specific hotels by their Google Hotels link (`hotelUrls`) is coming soon. Until then, run a city search with **Include per-site offers** on and `checkInFromDaysAhead` and `checkInToDaysAhead` set, schedule it daily, and compare `pricePerNight` per `provider` for the hotels you follow.

### Use it as an API

Every run can be started and read through the Apify API. One request runs the Actor with your input as the JSON body and returns the hotel prices as JSON, or as CSV or Excel with `format=csv` or `format=xlsx`:

`POST https://api.apify.com/v2/acts/garje~google-hotels-scraper/run-sync-get-dataset-items`

Send your Apify API token in the `Authorization` header. A worked example in Python: [Google Hotels API: hotel prices by date as JSON](https://dev.to/garje/google-hotels-api-hotel-prices-by-date-as-json-123).

### Use it in n8n, Make or Zapier

Apify has integrations for n8n, Make and Zapier. In each, add a step that runs an Actor, choose `garje/google-hotels-scraper`, paste your input as JSON, and wait for the run to finish; the next step reads the dataset items. A schedule step in front of it turns this into a daily or weekly feed into Google Sheets, Slack or email.

### Use it from an AI agent (MCP)

Add `https://mcp.apify.com?tools=garje/google-hotels-scraper` as an MCP server in Claude, Cursor, VS Code or any other MCP client. The agent then has this Actor as one tool and runs it with your Apify account, for example: "What does a 2-night stay for 2 adults in Lisbon cost for each check-in date next month?"

### Use cases

Each one is a ready task you can open, run and copy:

- [Hotel price calendar for one city](https://apify.com/garje/google-hotels-scraper/examples/hotel-price-calendar-one-city): one price per check-in date from 7 to 31 days ahead. The dates move with every run.
- [Hotel prices with booking-site offers](https://apify.com/garje/google-hotels-scraper/examples/hotel-prices-booking-sites): hotel rate shopping, each booking site's price for the same hotel and night, for rate parity checks.
- [Scheduled hotel price check, 14 days ahead](https://apify.com/garje/google-hotels-scraper/examples/scheduled-hotel-price-check): a daily hotel price tracker for the same stay, with no dates to update.
- [Google Hotels prices by date for 10 hotels](https://apify.com/garje/google-hotels-scraper/examples/google-hotels-api-prices-by-date): call it from your own code on a schedule and read the prices as JSON or CSV.
- [Daily hotel rate monitoring, 30 days out](https://apify.com/garje/google-hotels-scraper/examples/hotel-rate-monitoring-30-days-out): watch how a market's rates move day by day for stays a month out.
- [Google Travel hotel prices in Dubai, in AED](https://apify.com/garje/google-hotels-scraper/examples/google-travel-hotels-dubai-aed): hotel prices in the currency your market uses.
- [Hotel prices in two cities on the same night](https://apify.com/garje/google-hotels-scraper/examples/hotel-price-scraper-two-cities): hotel price comparison between two cities, in one table with the search on every row.

You can also take weekly price snapshots across several cities in one run.

### FAQ

**Why is the hotel list different from what my browser shows?**
Google changes which hotels it lists between searches, so the list can differ from a browser. Compare the same `hotelId` from run to run.

**Why do prices change with the country?**
Google shows each country its own price for the same hotel. For the same hotels and night, country JP showed a median 10% more than country US, and country AE 19% more. Set `country` to where your customers book.

**Can I see each booking site's price?**
Yes. Turn on `includeOffers` for one row per booking site, at $1.00 per 1,000 offers.

**Does it work on Apify's free plan?**
Yes. On a free account, a run asked for 40 Tokyo hotels returned all 40.

### Read more

I write up what the data shows, with the run ID behind every number:

- [Google Hotels API: hotel prices by date as JSON](https://dev.to/garje/google-hotels-api-hotel-prices-by-date-as-json-123)

- [Copenhagen hotel prices across 30 check-in dates](https://garje-data-notes.laude--pify.workers.dev/articles/copenhagen-hotel-prices-30-check-in-dates.html)

- [Booking-site prices for 290 Berlin hotels: how far apart they are](https://garje-data-notes.laude--pify.workers.dev/articles/berlin-hotel-booking-site-price-spread.html)

- [The cheapest night of the week in five cities](https://garje-data-notes.laude--pify.workers.dev/articles/cheapest-night-of-the-week-in-five-cities.html)

- [Facts and limits of this Actor](https://garje-data-notes.laude--pify.workers.dev/facts/google-hotels-scraper.html) and its [changelog](https://garje-data-notes.laude--pify.workers.dev/changelog/google-hotels-scraper.html)

# Actor input Schema

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

City or hotel searches as you would type them into Google, for example "hotels in Paris". Each row carries resolvedPlace, the place Google read the search as. A search Google does not recognise is skipped and not charged.

## `hotelUrls` (type: `array`):

Coming soon: named-hotel tracking. Leave this empty for now. A run with hotel links stops at once and costs nothing; use Searches instead, for example "hotels in Paris".

## `checkInFrom` (type: `string`):

First check-in date of the price calendar (YYYY-MM-DD), for example 2026-11-12. Defaults to tomorrow. Past dates are rejected before the run starts, so they never cost anything. For scheduled runs use First check-in, days ahead instead, so the date moves forward with every run.

## `checkInTo` (type: `string`):

Last check-in date of the calendar. One search runs per check-in date in the range. Defaults to the first check-in date.

## `checkInFromDaysAhead` (type: `integer`):

First check-in date as days after the run date: 0 is today, 7 is a week ahead. Use it instead of First check-in date for scheduled runs, so they never ask for a past date. <a href="https://garje-data-notes.laude--pify.workers.dev/articles/copenhagen-hotel-prices-30-check-in-dates.html" target="_blank">Read an example</a>.

## `checkInToDaysAhead` (type: `integer`):

Last check-in date as days after the run date, for example 30. Defaults to the first check-in date. <a href="https://garje-data-notes.laude--pify.workers.dev/articles/copenhagen-hotel-prices-30-check-in-dates.html" target="_blank">Read an example</a>.

## `nights` (type: `integer`):

Stay length applied to every check-in date and echoed on every row, as Google reported it.

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

Adults per room. Prices change with occupancy.

## `children` (type: `integer`):

Number of children. Ages default to 8 unless set below.

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

Optional ages (0 to 17), one per child.

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

Three-letter ISO 4217 code, for example USD, EUR, GBP, INR or IDR.

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

Two-letter country of the searcher. It pins the proxy country, because Google's prices follow the requester's country.

## `filters` (type: `object`):

Optional: hotelClass (array of 2 to 5), amenities (for example \["freeWifi", "pool"]), minRating (for example 4.0), priceMin and priceMax (per night, in your currency).

## `includeOffers` (type: `boolean`):

Adds one row per booking site (Booking.com, Expedia, the hotel's own site and others) for each hotel and date. Each offer is a separate paid event. <a href="https://garje-data-notes.laude--pify.workers.dev/articles/berlin-hotel-booking-site-price-spread.html" target="_blank">Read an example</a>.

## `includeUnavailable` (type: `boolean`):

Also return hotels Google lists without a price on that date (sold out or not bookable), with pricePerNight null and available false. These rows are not charged. Off by default.

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

Hard cost cap per search and check-in date.

## `canary` (type: `object`):

For scheduled monitoring runs. Example: {"baselineStore": "canary-baseline", "thresholdPoints": 5}. The first run saves the null rate of key fields (and the mix of a status field) to that named key-value store; later runs compare and fail with an alert if any value moves by more than thresholdPoints percentage points. Leave empty for normal runs.

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

Residential proxy pinned to the country above is recommended.

## Actor input object example

```json
{
  "queries": [
    "hotels in Paris"
  ],
  "hotelUrls": [],
  "checkInFrom": "2026-11-12",
  "checkInTo": "2026-11-18",
  "checkInFromDaysAhead": 7,
  "checkInToDaysAhead": 30,
  "nights": 2,
  "adults": 2,
  "children": 1,
  "childrenAges": [
    8
  ],
  "currency": "USD",
  "country": "US",
  "filters": {
    "hotelClass": [
      4,
      5
    ],
    "amenities": [
      "freeWifi"
    ]
  },
  "includeOffers": false,
  "includeUnavailable": false,
  "maxHotelsPerQuery": 50,
  "canary": {
    "baselineStore": "canary-baseline",
    "thresholdPoints": 5
  },
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

## `stats` (type: `string`):

No description

# 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 Paris"
    ],
    "filters": {},
    "maxHotelsPerQuery": 5,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("garje/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 Paris"],
    "filters": {},
    "maxHotelsPerQuery": 5,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("garje/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 Paris"
  ],
  "filters": {},
  "maxHotelsPerQuery": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call garje/google-hotels-scraper --silent --output-dataset

```

## MCP server setup

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