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

Scrape Google Hotels prices by check in and check out date: nightly price, total with taxes and fees, guest rating, review count, star rating, amenities and deal badges. Real date aware pricing, and you only pay for successful results.

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

## Pricing

from $2.00 / 1,000 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

## Google Hotels Scraper

Scrape **Google Hotels** prices for any city or area, **for the exact dates you care about**: nightly price, total price with taxes and fees, guest rating, review count, star rating, amenities, and Google's own deal badges. You only pay for hotels you actually receive.

### Why this one

- **Dates actually work.** This is the thing to check before you trust any Google Hotels scraper. Google accepts `checkin=` and `checkout=` in the URL and then **silently ignores them**, so a scraper built on those parameters returns the same prices for every date and still looks like it is working. This Actor writes your dates into the encoded parameter Google really reads. Verified: the same Glendale hotel returns **$207 for five nights in October and $152 over Christmas**, and a one night stay returns a different number again.
- **Total price, not just the teaser.** Where Google shows it, you get `totalPrice` including taxes and fees alongside `pricePerNight`, plus the number of nights, so the figures reconcile.
- **Built to survive Google's redesigns.** Most scrapers in this niche read CSS class names like `PVOOXe`, which Google rotates every few weeks. This one reads accessibility labels instead, because Google cannot rotate those without breaking screen readers.
- **It tells you when it breaks.** If Google changes the layout, the run **fails loudly and charges you nothing**, instead of returning an empty dataset that looks like a city with no hotels. Failed queries land in `FAILED_QUERIES` with the reason.
- **No charge for failures.** A query that fails after every retry is never written to the dataset, so it is never billed.

### What you can use it for

- **Rate shopping and competitor monitoring:** track what nearby hotels charge for the same dates, on a schedule.
- **Revenue management:** watch how prices for your own property move against the local market across a season.
- **Travel content and deal sites:** find real price differences between dates, cities, or lengths of stay.
- **Market research:** compare hotel pricing across neighbourhoods or entire cities.
- **AI agents:** one clean JSON object per hotel, with prices as numbers rather than strings.

### Input

| Field | What it does |
|---|---|
| Search queries | One or more, exactly as typed into Google Hotels. `hotels in Glendale CA`, `hotels near LAX`, `Paris hotels`. |
| Check in / check out date | `2026-11-30` format. Leave both empty for Google's default stay. |
| Nights | Used when only a check in date is given. |
| Check in days from now | For scheduled runs: `7` means a week from the day the run starts. |
| Adults / children | Room occupancy. |
| Currency | `USD`, `EUR`, `GBP`, and so on. |
| Language | Google interface language, for example `en`, `de`. |
| Google region | The region Google answers as. Defaults to `us`. Keep it matched to your proxy country, see below. |
| Maximum hotels per query | `0` for every hotel on the page, usually about 20. |
| Minimum guest rating | Skip anything rated below this out of 5. |
| Maximum price per night | Skip anything above this. |
| Proxy | Residential proxies are strongly recommended, pinned to a country. Google blocks datacenter addresses quickly. |

#### Example input

```json
{
  "searchQueries": ["hotels in Glendale CA", "hotels near LAX"],
  "checkInDate": "2026-11-30",
  "checkOutDate": "2026-12-03",
  "adults": 2,
  "currency": "USD",
  "minRating": 4,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}
```

### Output

One record per hotel.

```json
{
  "searchQuery": "hotels in Glendale CA",
  "rank": 3,
  "name": "Hilton Los Angeles North/Glendale & Executive Meeting Ctr",
  "entityId": "ChgI-M__6s-cz8mHARoLL2cvMXR4MTVtdHkQAQ",
  "url": "https://www.google.com/travel/hotels/entity/ChgI-M__6s-cz8mHARoLL2cvMXR4MTVtdHkQAQ",
  "price": 155,
  "pricePerNight": 155,
  "totalPrice": 173,
  "nights": 1,
  "priceIncludesTaxes": true,
  "priceText": "$155",
  "priceSymbol": "$",
  "currency": "USD",
  "deal": null,
  "rating": 4,
  "reviewCount": 2005,
  "starRating": 4,
  "amenities": ["Breakfast ($)", "Free Wi-Fi", "Parking ($)", "Outdoor pool", "Air conditioning", "Fitness center", "Bar", "Restaurant"],
  "reviewHighlights": ["Couples", "Nature", "Bar"],
  "vendor": null,
  "checkInDate": "2026-11-30",
  "checkOutDate": "2026-12-01",
  "adults": 2,
  "children": 0,
  "scrapedAt": "2026-09-21T06:54:00Z"
}
```

Fields that Google does not show for a given hotel come back as `null` or an empty list rather than being dropped, so every record has the same shape. Vacation rentals often have no rating, and `vendor` is only set when the price comes from a booking site rather than the hotel itself.

Two extra records are written to the key value store on every run:

- **`FAILED_QUERIES`** — queries that failed, with the reason. These were not charged.
- **`RUN_STATS`** — results, retries, blocked responses absorbed, how many hotels your own filters removed, the Google region used, and how many pages had to be retried for missing prices.

### If prices come back empty

Google **gates nightly rates by region**. Ask it from outside the region and it
returns the hotel list, the ratings, the stars and the amenities, but every
price is null. Verified 2026-09-21: one residential session with no country set
returned 5 hotels and 0 prices, and the same query pinned to a United States
exit returned 5 hotels and 5 prices.

So keep these two in agreement:

- **Proxy country** (Proxy section). Defaults to `US`.
- **Google region** (Output and filters). Defaults to `us`.

The Actor retries once with a fresh proxy session when a page parses but carries
no prices, and if prices are still missing it says so as an error in the log
rather than leaving you to notice nulls in the dataset. `RUN_STATS` reports
`googleRegion` and `pricelessPagesRetried`.

### Notes and limits

- Google returns roughly **20 hotels per query**. To cover a city more deeply, send several narrower queries, for example by neighbourhood.
- Prices are what Google shows for that stay at that moment, and they move. For a series, run on a schedule and keep the dataset.
- Very long stays and unusual occupancy combinations fall back to whatever Google decides to show.
- `price` is kept as an alias of `pricePerNight` so existing integrations keep working.

### Testing

```
python3 tests/test_hotels.py            # offline, against a saved page
LIVE=1 python3 tests/test_hotels.py     # also hits Google
```

# Actor input Schema

## `searchQueries` (type: `array`):

One search per line, exactly as you would type it into Google Hotels. For example "hotels in Glendale CA", "hotels near LAX", or "Paris hotels". Any number of queries.

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

Format 2026-11-30. Prices change a lot by date, so set this whenever you care about a specific stay. Leave both dates empty to get Google's own default stay.

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

Format 2026-12-02. If you leave this empty but set a check in date, the stay is the number of nights below.

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

Used only when a check out date is not given.

## `checkInDaysFromNow` (type: `integer`):

Handy for a schedule: 7 means a week from the day the run starts. Ignored when a check in date is set above.

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

Number of adults in the room.

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

Number of children in the room.

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

Three letter code, for example USD, EUR, GBP.

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

Two letter code used for Google's interface language, for example en, de, fr.

## `maxResultsPerQuery` (type: `integer`):

0 means every hotel on the results page, which is usually about 20. Lower this to control cost.

## `minRating` (type: `integer`):

Skip hotels rated below this, out of 5. Leave empty for no filter. Hotels with no rating at all are skipped when this is set.

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

Skip hotels above this nightly price, in the currency above. Leave empty for no filter.

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

Google blocks datacenter addresses quickly, so residential proxies are strongly recommended. Keep the country set to US: a proxy exit outside the United States returns the hotel list with no nightly prices at all, because Google gates partner pricing by region.

## `countryCode` (type: `string`):

The region Google answers as, two letter code. Leave as us unless you specifically want another country’s pricing view. Changing this away from the country of your proxy is the most common cause of missing prices.

## Actor input object example

```json
{
  "searchQueries": [
    "hotels in Glendale CA",
    "hotels near LAX"
  ],
  "nights": 1,
  "adults": 2,
  "children": 0,
  "currency": "USD",
  "language": "en",
  "maxResultsPerQuery": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "countryCode": "us"
}
```

# Actor output Schema

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

No description

## `failedQueries` (type: `string`):

No description

## `runStats` (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 = {
    "searchQueries": [
        "hotels in Glendale CA"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("eaglizer_it/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 = { "searchQueries": ["hotels in Glendale CA"] }

# Run the Actor and wait for it to finish
run = client.actor("eaglizer_it/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 '{
  "searchQueries": [
    "hotels in Glendale CA"
  ]
}' |
apify call eaglizer_it/google-hotels-scraper --silent --output-dataset

```

## MCP server setup

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