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

Hotel price API for Google Hotels: nightly and total prices for your dates, ratings, reviews, stars, amenities, coordinates and deals for any city. Fast, cheap, no browser, built not to fail.

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

## Pricing

from $3.50 / 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

## Google Hotels Scraper & API - Hotel Prices, Ratings and Availability

**Google Hotels Scraper** is a fast hotel price scraper and unofficial Google Hotels API: enter a city or "hotels near ..." search plus your dates, and get every property's nightly price, total stay price with taxes, rating, review count, star class, amenities, coordinates and deal badges as clean JSON, CSV or Excel.

Use it to scrape Google Hotels for price monitoring, rate-parity checks, travel apps and hotel market research. It reads the data Google already embeds in the page over plain HTTP, with **no headless browser**, so runs finish in seconds, use very little compute, and don't time out on page rendering.

### How to use

1. Enter one or more searches in **Search queries** (e.g. `hotels in Paris`, `hotels near Times Square`) and, optionally, check-in / check-out dates, guests and currency.
2. Click **Start**. A 50-hotel query usually finishes in well under a minute.
3. Download the results as JSON, CSV, Excel or HTML from the **Output** tab, or call the actor through the Apify API and pull the dataset from your own code.

### What you get

- **Prices**: nightly price, nightly price with taxes, total for the whole stay, taxes and fees, currency, and Google's deal badge (e.g. "DEAL 24% less than usual").
- **Quality**: Google rating (1-5), review count, a 1-to-5-star review breakdown, and hotel class (2-star, 3-star, ...).
- **Location**: latitude and longitude, nearby places (airport, transit, attractions, restaurants) with travel times, and optionally the full street address and phone number.
- **Property info**: amenities (Free Wi-Fi, Pool, Free breakfast, ...), a short description, check-in and check-out times, official website, a thumbnail plus up to 10 photos.
- **IDs and links**: Google Hotels URL, Google Maps URL, Google entity token and Maps feature ID. Use them to join the data with other datasets or track the same hotel over time.
- **Pagination**: the scraper pages through the results until it reaches your `maxResults`. Duplicates across pages are removed automatically.

### Use cases

- Price monitoring and rate-parity checks for a city or a list of competitor hotels
- Travel apps, comparison sites and trip planners
- Market research: supply, star mix and price bands by neighbourhood
- Lead lists of hotels, with website and phone (turn on `includeDetails`)

### Input example

| Field | Default | Description |
|---|---|---|
| `queries` | - | What you'd type into Google Hotels: `"hotels in Paris"`, `"hotels near Times Square"`, `"boutique hotels in Lisbon"` |
| `checkIn` / `checkOut` | Google's default | `YYYY-MM-DD`. If you leave `checkOut` empty, the stay is one night. Stays can be up to 30 nights. |
| `adults` | 2 | Guests (occupancy changes prices) |
| `currency` | USD | Any 3-letter ISO code: EUR, GBP, JPY, ... |
| `maxResults` | 50 | Unique properties per query (0 = as many as Google returns) |
| `includeVacationRentals` | false | Include the apartments and houses Google mixes into hotel results |
| `includeDetails` | false | Adds the street address and phone number. Costs one extra request per hotel. |
| `language` / `country` | en / us | Google `hl` / `gl` parameters |
| `proxyConfiguration` | Apify Proxy | Switch to RESIDENTIAL if you see blocks |

Example:

```json
{
    "queries": ["hotels in Charlotte", "hotels in Paris", "hotels in Tokyo"],
    "checkIn": "2026-10-15",
    "checkOut": "2026-10-18",
    "adults": 2,
    "currency": "USD",
    "maxResults": 100
}
```

### Output example

One dataset item per property. This one is from a real run: Paris, 3 nights, 15-18 Oct 2026 (arrays shortened).

```json
{
    "query": "hotels in Paris",
    "position": 1,
    "name": "Les Jardins de Saint-Cloud",
    "propertyType": "hotel",
    "category": "extended_stay_hotel",
    "pricePerNight": 116,
    "pricePerNightWithTaxes": 127,
    "priceText": "$116",
    "currency": "USD",
    "totalPrice": 382,
    "totalPriceBeforeTaxes": 347,
    "totalTaxesAndFees": 34.67,
    "totalPriceText": "$382",
    "deal": "DEAL 24% less than usual",
    "checkIn": "2026-10-15",
    "checkOut": "2026-10-18",
    "nights": 3,
    "rating": 4.4,
    "reviewCount": 338,
    "ratingBreakdown": { "5star": 238, "4star": 53, "3star": 15, "2star": 9, "1star": 23 },
    "hotelClass": 3,
    "hotelClassText": "3-star tourist hotel",
    "address": null,
    "phone": null,
    "latitude": 48.8439194,
    "longitude": 2.2217806,
    "checkInTime": "3:00 PM",
    "checkOutTime": "12:00 PM",
    "description": "Chic studios in a refined apartment hotel offering a breakfast buffet, plus an indoor pool & a gym.",
    "amenities": ["Breakfast ($)", "Free Wi-Fi", "Indoor pool", "Air conditioning", "Pet-friendly", "Fitness center", "Parking", "Bar"],
    "nearbyPlaces": [
        { "name": "Aéroport de Paris-Charles de Gaulle", "type": "airport", "transport": [{ "mode": "car", "duration": "40 min" }, { "mode": "transit", "duration": "1 hr 19 min" }] }
    ],
    "thumbnail": "https://lh3.googleusercontent.com/grass-cs/ACvplmPh...=s150-w92-h150-n-k-no",
    "images": ["https://lh3.googleusercontent.com/grass-cs/ACvplmPh...=w1024"],
    "website": "http://www.lesjardinsdesaintcloud.fr/",
    "entityToken": "ChkI5cSZt5rXifwfGg0vZy8xMWMycTMxeW5rEAE",
    "googleMapsFeatureId": "0x47e67b2498ee8a6b:0x1ff826b9a6e66265",
    "googleMapsUrl": "https://www.google.com/maps?cid=3339634915179124171",
    "googleHotelsUrl": "https://www.google.com/travel/hotels/entity/ChkI5cSZt5rXifwfGg0vZy8xMWMycTMxeW5rEAE?hl=en&gl=us&curr=USD&ts=...",
    "searchPage": 1,
    "scrapedAt": "2026-09-23T23:01:41.052Z"
}
```

With `includeDetails: true`, `address` and `phone` are filled in, for example `"8629 J M Keynes Dr, Charlotte, NC 28262"` and `"(704) 547-7444"`.

The dataset has two ready-made views: **Overview** (photo, name, stars, rating, price, deal, link) and **Prices** (the full price breakdown).

#### Field fill rates (real test run: 300 hotels across Charlotte, Paris and Tokyo)

| Field | Fill rate |
|---|---|
| name, coordinates, photos, Google Hotels URL, Maps URL, currency | 100% |
| rating, reviewCount | 100% |
| amenities | 96% |
| website | 96% |
| pricePerNight, totalPrice | 91% (the rest had no rooms available for the dates) |
| hotelClass | 86% (Google lists no class for some properties) |
| checkInTime | 83% |
| description | 73% |
| deal | 25% (only properties Google flags as a deal) |
| address, phone | 13% without `includeDetails`, 95-100% with it |

### Pricing

Pay per result: **$3.50 per 1,000 results** (one result = one hotel saved to the dataset).

- 1,000 hotels = $3.50
- 10,000 hotels = $35

You're never charged for failed requests or for duplicates. If you set a maximum cost per run, the scraper stops cleanly when it reaches it. Apify's free plan includes $5 of monthly platform credit, enough to try the scraper on real searches.

### Integrations

- **Make, Zapier and n8n**: start runs and send new hotel data to your other apps.
- **Google Sheets**: export the dataset straight into a spreadsheet, or refresh it on a schedule.
- **Apify API**: run the actor and fetch results over REST, or with the official JavaScript and Python clients.
- **Webhooks**: get notified when a run finishes and process the data right away.
- **Schedules**: run the same searches daily to track Google Hotels prices over time.
- **MCP for AI agents**: through the Apify MCP server (https://mcp.apify.com), Claude, ChatGPT, Cursor and other agents can call this Google Hotels API directly and read the results.

### Limits (read before large runs)

- **About 150-250 unique hotels per query.** Google keeps serving pages after that, but deeper pages contain only vacation rentals or listings you've already seen. The scraper notices this and stops the query. To cover a big city in full, split it into areas: `"hotels in Shinjuku"`, `"hotels in Shibuya"`, ...
- **Search ranking is not stable.** Google reshuffles the order between pages, so the same hotel can come up twice. The scraper removes these duplicates, and that's why a page sometimes adds fewer than 20 new hotels.
- **Prices are Google's "from" prices** for the dates and occupancy you set: the lowest rate across booking partners, shown in the currency you chose. Properties with no availability for your dates come back with `pricePerNight: null`.
- **Street address and phone** need `includeDetails`. That opens one extra page per hotel, so the run is slower and uses more proxy traffic.
- **Amenity and deal labels** come in the language you set. They're most complete with `language: "en"`.
- **Per-partner price lists** (Booking.com vs Expedia vs the hotel's own site) and **review texts** are not included in this version.

### FAQ

**Is it legal to scrape Google Hotels?**
This actor collects only publicly available data about businesses (hotels), not personal data. You're responsible for using it in line with Google's terms of service and privacy laws such as GDPR. This is not legal advice: if you're unsure, check with a lawyer.

**How does it avoid blocks?**
It sends requests through Apify Proxy with realistic Chrome desktop headers and a session pool. When Google blocks a request (403/429, a captcha page, or a page with no result data), that session is retired and the request is retried on a fresh session with a new IP. Other errors are retried with exponential backoff, up to 6 retries per page by default. If the log shows repeated "Blocked by Google" retries, switch the proxy group to RESIDENTIAL.

**What are the limits?**
About 150-250 unique hotels per query (Google stops serving new hotels after that), stays of up to 30 nights, up to 12 adults, and prices are Google's lowest "from" rate for your dates. Address and phone need `includeDetails`. Per-partner price lists and review texts are not included. See **Limits** above for details.

**Does it need a browser or cookies?**
No. It makes plain HTTPS requests and reads the structured data Google embeds in the page. That's why it's fast and cheap to run.

**Why did a query return fewer results than `maxResults`?**
Google ran out of new hotels for that query (see Limits). The log and the `RUN_SUMMARY` record in the key-value store show the stop reason for each query.

**Can I search one specific hotel?**
Yes. Use its name as the query, e.g. `"Hilton Charlotte University Place"`. Set `maxResults: 1` to get only the top match.

**Can I get prices for different dates?**
Run the actor once per date range, or schedule it daily to track prices over time. `entityToken` stays the same across runs, so you can use it to join the results.

### How it works (for developers)

Google Hotels pages carry their result data in `AF_initDataCallback({key: 'ds:N', data: ...})` script blobs. The scraper parses every blob. It then walks the nested arrays and picks out anything shaped like a hotel record (a name, a `[lat, lng]` pair, and an entity token or Maps ID). It doesn't rely on fixed array indexes. Every field is read defensively: if one record is malformed, that record just has fewer fields, and the run doesn't crash. Pagination, dates, guests and currency use the same `ts`/`qs` URL parameters Google's own interface uses.

Run it locally:

```bash
npm install
npm test                                  # parser tests on saved fixtures
APIFY_LOCAL_STORAGE_DIR=./storage node src/main.js   # input in storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

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

What you would type into Google Hotels, one search per line. Each search returns up to "Max results per query" hotels. Examples: "hotels in Paris", "hotels near Times Square", "boutique hotels in Lisbon", or a hotel name like "Hilton Charlotte University Place".

## `checkIn` (type: `string`):

Arrival date in YYYY-MM-DD format, e.g. 2026-10-15. Must not be in the past. Leave empty to let Google pick its default dates (usually a one-night stay in the next few days).

## `checkOut` (type: `string`):

Departure date in YYYY-MM-DD format, e.g. 2026-10-18. Must be after check-in; maximum stay is 30 nights. Leave empty for a one-night stay after check-in.

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

Number of adult guests, 1-12. Prices change with occupancy, so set this to match the stay you want to price.

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

3-letter ISO currency code for all prices, e.g. USD, EUR, GBP, JPY.

## `maxResults` (type: `integer`):

Stop each search after this many unique hotels. Google usually has 150-250 unique hotels per search. 0 = as many as Google will page through.

## `includeVacationRentals` (type: `boolean`):

Google mixes apartments and houses from rental partners into hotel results. On = include them (propertyType "vacation\_rental"); off = hotels only.

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

Opens each hotel's Google Hotels page to add the full street address and phone number. Adds one extra request per hotel, so runs are slower and use more proxy traffic.

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

Google interface language (hl), e.g. en, fr, de. Amenity and deal labels come in this language; keep "en" for the most complete labels.

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

2-letter country code (gl) Google should search from, e.g. us, gb, fr. Affects which booking partners and prices are shown.

## `maxConcurrency` (type: `integer`):

How many pages to fetch in parallel, 1-20. Different searches run in parallel; pages within one search run in order.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed page, 0-20. Blocked requests are retried on a fresh proxy session (new IP).

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

Apify Proxy is recommended and on by default. If the log shows repeated "Blocked by Google" retries, switch to the RESIDENTIAL proxy group.

## Actor input object example

```json
{
  "queries": [
    "hotels in Charlotte",
    "hotels in Paris"
  ],
  "adults": 2,
  "currency": "USD",
  "maxResults": 10,
  "includeVacationRentals": false,
  "includeDetails": false,
  "language": "en",
  "country": "us",
  "maxConcurrency": 5,
  "maxRequestRetries": 6,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All hotels found, with prices, ratings, amenities and coordinates.

## `summary` (type: `string`):

Per-query counts and stop reasons.

# 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 Charlotte",
        "hotels in Paris"
    ],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rel8ble/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 Charlotte",
        "hotels in Paris",
    ],
    "maxResults": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("rel8ble/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 Charlotte",
    "hotels in Paris"
  ],
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call rel8ble/google-hotels-scraper --silent --output-dataset

```

## MCP server setup

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