# Google Hotels Scraper — Hotel Prices, Ratings, Amenities (`architekture/google-hotels-scraper`) Actor

Google Hotels (google.com/travel/hotels) scraper: nightly prices, stay totals, currency, guest ratings, review counts, star class, amenities, property type and stay dates, by destination or hotel name with optional check-in/check-out dates. 20 free previews. Pay per result. No login.

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

## Pricing

$4.00 / 1,000 hotel properties

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 — Hotel Prices, Ratings, Amenities

**Recorded run: 4 properties from 2 destination searches in 48 seconds** (2026-10-22, build 0.1.9, run `1WGNCnruKEKRLxHEa`). A bounded canary sample, not a full-catalogue benchmark.

architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.

Google Hotels nightly prices, stay totals, guest ratings, review counts, star class and amenities from google.com/travel/hotels, by destination or hotel name, for the stay dates you choose.

| Source | Market | From | Standout fields |
|---|---|---|---|
| Google Hotels (google.com/travel/hotels) | Any destination Google Hotels covers | $4.00 / 1,000 properties | `nightlyPrice` / `totalPrice`, `rating` / `reviewCount`, `hotelClass`, `amenities` |

### ✅ Why choose this scraper

#### Google Hotels prices for exact stay dates

Set `checkIn` and `checkOut` and every row returns `nightlyPrice`, `totalPrice`, `currency` and the stay dates decoded from the result. A page whose rows show other dates than the ones requested is retried and then failed, never delivered as if it matched.

#### Hotel ratings, review counts and star class

`rating` (out of 5), `reviewCount`, `hotelClass` (the stated star rating) and the `amenities` list come back on the same row as the price.

#### Hotels and holiday rentals told apart

`propertyType` is `hotel` or `vacation_rental`, so a city search can be split into the two without reading names.

#### Several destinations in one run

Up to 20 `queries` per run, up to 25 result pages and 500 properties each. Properties are de-duplicated by `id` within each query.

#### Measured on a schedule

Two canary tasks run fixed searches (London; Paris and Tokyo) daily on the live site and record rows, inputs and seconds; the hero line above is the latest green run.

### ⚠️ Worth knowing up front

- Prices are the "Prices starting from" offer and the stay total that Google shows on each search card. They are not booking quotes. Per-provider (OTA) rates, room types, cancellation terms and review text are not collected.
- Searches use Google's default occupancy. `adults` and `children` are rejected as input, and currency cannot be chosen: `currency` is what Google showed the run (USD for London, Paris and Tokyo in the 2026-10-22 runs), with `currencyEvidence` saying where it was read.
- Without `checkIn` and `checkOut`, Google picks the stay: on 2026-10-22 it chose a 2026-10-25 night for London and a 2026-11-01 night for Paris and Tokyo. Supply both dates (1 to 30 nights) for comparable runs.
- Missing values are `null`, not zero. A property shown without a price has `nightlyPrice: null` and is still a delivered, charged row. `totalIncludesTaxesAndFees` is `true` only when the card says "with taxes + fees", otherwise `null`. `hotelClass` is `null` when no star rating is stated.
- The run stops cleanly 90 seconds before its timeout. With the default 180-second timeout that leaves about 90 seconds of collection: 18 properties on 2026-10-22, about 4.5 seconds per property. Raise the run timeout for larger runs: 60 properties from 3 destinations took 274 seconds with a 600-second timeout.
- De-duplication is per query: a property found by two searches is two rows and two charges.
- `COMPLETE` means no further results page was shown, not that every hotel in a destination was found. `LIMITED` means the run stopped at `maxItemsPerQuery`, `maxPagesPerQuery` or your spending limit. A query whose page shows no recognisable hotel cards after 3 attempts is reported as failed (`PARTIAL` or `BLOCKED`), never as an empty success.
- Invalid input, a checkpoint from another account, a run that already carries charges at start (`RECONCILIATION_REQUIRED`), exhausted preview attempts (`PREVIEW_LIMIT`), a source failure after retries (`PARTIAL` or `BLOCKED`) and the run deadline (`TIME_LIMIT`) end with a free status row (`"recordType": "status"`) that says what happened. `COMPLETE` and `LIMITED` runs, including a spending-limit stop, end without one; check `RUN_SUMMARY`. An uncertain dataset write or charge acknowledgement stops the run as failed so it can be reconciled.

### ⚡ Try the example

Each Apify account gets 20 lifetime free preview attempts on this actor (`"mode": "preview"`): one query, one page, up to 10 rows, no property charges. Failed or uncertain attempts count. After the twentieth, preview runs stop with a `PREVIEW_LIMIT` status row; they never switch to paid collection.

Open the actor's input page, switch to JSON, paste this and start the run:

```json
{
  "mode": "preview",
  "queries": ["London"],
  "maxItemsPerQuery": 10,
  "maxPagesPerQuery": 1
}
```

For paid collection set `"mode": "paid"`; add `"checkIn"` and `"checkOut"` (for example `"2026-11-10"` and `"2026-11-11"`) to price a fixed stay.

### ⚙️ Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `preview` (prefill: `paid`) | `preview` uses one of 20 lifetime free attempts (one query, one page, at most 10 rows). `paid` charges per delivered property. API callers who omit it get `preview`. |
| `queries` | array | `["London"]` | 1 to 20 destination or hotel-name searches, each up to 150 characters. Preview allows one. |
| `checkIn` | string | — | Check-in date, `YYYY-MM-DD`. Supply together with `checkOut`. |
| `checkOut` | string | — | Check-out date, `YYYY-MM-DD`, 1 to 30 nights after `checkIn`. |
| `maxItemsPerQuery` | integer | `20` | 1 to 500 unique properties per query, counting IDs already in a supplied `checkpoint`. Preview caps it at 10. |
| `maxPagesPerQuery` | integer | `1` | 1 to 25 result pages per query. Preview uses one. A resumed run revisits earlier pages. |
| `checkpoint` | object | — | The `CHECKPOINT` record from an earlier paid run by the same account, with the same queries and dates. Delivered IDs are skipped. |
| `feedback` | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |

Any other input field is rejected with an `INVALID_INPUT` status row rather than ignored.

### 🧱 Stable fields

These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.

| Field | Type | Meaning |
|---|---|---|
| `id` | string | Google Hotels entity ID; the join key across runs. |
| `name` | string | Property name as displayed. |
| `url` | string | Google Hotels entity page for the property. |
| `query` | string | The search that produced the row. |
| `nightlyPrice` / `currency` / `priceText` | number / string / string, nullable | Displayed "from" price per night, its ISO currency and the original price text. |
| `totalPrice` / `totalCurrency` | number / string, nullable | Displayed total for the stay and its currency. |
| `totalIncludesTaxesAndFees` | boolean, nullable | `true` when the card states taxes and fees are included; otherwise `null`. |
| `rating` / `reviewCount` | number, nullable | Guest rating out of 5 and the number of reviews behind it. |
| `hotelClass` | number, nullable | Stated star class, for example `4` for a 4-star hotel. |
| `amenities` | array | Amenity strings shown on the card; empty when none are shown. |
| `propertyType` | string | `hotel` or `vacation_rental`. |
| `checkIn` / `checkOut` | string, nullable | Stay dates (`YYYY-MM-DD`) decoded from the result. |
| `checkInText` / `checkOutText` | string, nullable | Stay dates as Google displayed them, for example `Sun, Nov 1`. |
| `currencyEvidence` | string, nullable | `price_label` or `result_link`: where `currency` was read. |
| `source` / `scrapedAt` | string | `google_hotels` and the UTC capture time. |
| `recordType` | string | `"status"` on free status rows; absent on property rows. |

Other fields are extras, not part of the contract. To have one pinned, say so in `feedback`.

### 📦 Output example

Abridged real row from run `1WGNCnruKEKRLxHEa` on 2026-10-22; values are a historical snapshot.

```json
{
  "id": "ChgIxfW9593Qkbu3ARoLL2cvMXRnczA3ZHQQAQ",
  "name": "Pullman Paris Tour Eiffel",
  "url": "https://www.google.com/travel/hotels/entity/ChgIxfW9593Qkbu3ARoLL2cvMXRnczA3ZHQQAQ",
  "query": "Paris",
  "nightlyPrice": 382,
  "currency": "USD",
  "priceText": "$382",
  "totalPrice": 437,
  "totalCurrency": "USD",
  "totalIncludesTaxesAndFees": true,
  "rating": 4.6,
  "reviewCount": 17441,
  "hotelClass": null,
  "amenities": ["Breakfast ($)", "Free Wi-Fi", "Parking ($)", "Air conditioning", "Pet-friendly", "Fitness center", "Bar", "Restaurant"],
  "propertyType": "hotel",
  "checkIn": "2026-11-01",
  "checkOut": "2026-11-02",
  "checkInText": "Sun, Nov 1",
  "currencyEvidence": "result_link"
}
```

A free status row looks like this:

```json
{
  "recordType": "status",
  "status": "INVALID_INPUT",
  "message": "Previews allow one query and no continuation"
}
```

### 🧠 Advanced usage

#### Track Google Hotels prices for the same dates every day

Schedule a paid run with fixed `checkIn` and `checkOut` and diff `nightlyPrice` and `totalPrice` by `id` between datasets. Compare rows only when `currency` matches.

#### Collect more hotels in one city across several runs

Raise `maxPagesPerQuery` and `maxItemsPerQuery`. When a run stops on time or budget, copy its `CHECKPOINT` record from the key-value store into `checkpoint`, keep the same queries and dates, and run again: delivered IDs are skipped and not charged again, and earlier pages are revisited without charge. A preview checkpoint cannot be resumed in paid mode.

#### Check a run finished before importing

Read `RUN_SUMMARY` from the run's key-value store. `status` is `COMPLETE`, `LIMITED`, `PARTIAL`, `BLOCKED` or `TIME_LIMIT`, and `queries` lists each search with its own status, `reason` (for example `item_limit` or `no_next_page`) and `newRows`.

### 🔌 Integrations

Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. Daily runs with fixed stay dates track how the price for the same nights moves. Copy-paste code in Python, Node, curl and n8n: [architek-earth/examples/google-hotels-scraper](https://github.com/architek-earth/examples/tree/main/google-hotels-scraper).

### 💸 Pricing

Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. Property IDs already in a supplied `checkpoint` are skipped and not charged again; a run that already carries charges when it starts stops with `RECONCILIATION_REQUIRED` and charges nothing more.

| Event | Price | What it covers |
|---|---|---|
| Free preview | $0 | 20 lifetime attempts per account; one query, one page, up to 10 rows each. |
| `property` | $4.00 / 1,000 properties | One unique property delivered for a query in paid mode, with or without a displayed price. |

1,000 properties cost $4.00; the 3-property example task costs $0.012.

### ❓ FAQ

**Is there a Google Hotels API for hotel prices?** This actor is one: it returns Google Hotels search results as JSON through the Apify API, callable from any language or from n8n, Make and Zapier.

**Do I need a Google account or login?** No. No Google account, cookies or API key; the actor opens the public Google Hotels search pages.

**Can I get hotel prices for specific check-in and check-out dates?** Yes. Set `checkIn` and `checkOut` (1 to 30 nights); each row carries the stay dates it was priced for.

**Can I compare OTA rates from Booking.com, Expedia or Agoda?** No. The actor returns the lowest displayed price and the stay total per property, not the per-provider rate list.

**Can I set the number of guests or the currency?** No. Searches use Google's default occupancy, and `currency` is read from the result rather than chosen.

**Can I get Google Hotels reviews?** Only the `rating` and `reviewCount`. Review text is not collected.

### 🤖 For AI agents & LLM apps

Paste this into your AI assistant:

`architekture/google-hotels-scraper` on Apify. Python: `from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/google-hotels-scraper").call(run_input={"mode": "paid", "queries": ["London"], "maxItemsPerQuery": 10}); items = client.dataset(run["defaultDatasetId"]).list_items().items` (page with `offset`/`limit` for large exports). Rows with `recordType == "status"` are free and explain invalid input or a stopped run. Full input schema and README: `GET https://api.apify.com/v2/acts/architekture~google-hotels-scraper/builds/default`. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (`mcp.apify.com`). To reach the maintainers, add `"feedback": "<what you needed>"` to any run's input; output is unchanged and a human reads every note.

### 🧰 More from architek.earth

**Search, travel and trends**

- [Google Trends Scraper](https://apify.com/architekture/google-trends-scraper) — Trending Now by country, traffic, related queries
- Google Hotels Scraper — *you are here*

Full catalogue: [github.com/architek-earth](https://github.com/architek-earth).

architek.earth · code like concrete

### 🙋 Support & legal

Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.

This actor collects only publicly available data from google.com, the same pages any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by Google. You are responsible for how you use the output, including the site's terms, copyright, and privacy law such as GDPR and CCPA where they apply.

### 📝 Changelog

- **2026-10-22** — Status message now reads `Done: N rows, M details, K status rows (STATUS)`.
- **2026-10-22** — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
- **2026-10-22** — Invalid input, source walls and the run deadline now stop cleanly with a free status row instead of a failed run.
- **2026-10-20** — Published with three example tasks; one `property` event at $4.00 / 1,000 properties, previews free.
- **2026-10-20** — Private hosted validation across four destinations, dated-price extraction, saved-checkpoint continuation, preview quota implementation and explicit per-property billing safeguards. Private customer validation passed: 10 preview rows with zero events, 3 paid rows with 3 events at a $0.012 cap, and 2 new continuation rows with 2 events and no prior-ID overlap. Publication authorized after release verification; schedules remain off.

# Actor input Schema

## `mode` (type: `string`):

Preview: 20 lifetime attempts, one page and up to 10 rows. Paid: $4 per 1,000 delivered properties. No automatic paid fallback.

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

Hotel names or destinations to search.

## `maxItemsPerQuery` (type: `integer`):

Maximum unique properties per query, including previously delivered checkpoint rows.

## `maxPagesPerQuery` (type: `integer`):

Maximum result pages visited per query. Resuming revisits earlier pages.

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

Optional ISO stay date; supply together with checkOut.

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

Optional ISO departure date, after checkIn.

## `checkpoint` (type: `object`):

CHECKPOINT record from a previous compatible run.

## `feedback` (type: `string`):

Feedback logged with this run; does not change output.

## Actor input object example

```json
{
  "mode": "paid",
  "queries": [
    "London"
  ],
  "maxItemsPerQuery": 10,
  "maxPagesPerQuery": 1,
  "feedback": ""
}
```

# Actor output Schema

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

No description

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

No description

## `checkpoint` (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 = {
    "mode": "paid",
    "queries": [
        "London"
    ],
    "maxItemsPerQuery": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("architekture/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 = {
    "mode": "paid",
    "queries": ["London"],
    "maxItemsPerQuery": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("architekture/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 '{
  "mode": "paid",
  "queries": [
    "London"
  ],
  "maxItemsPerQuery": 10
}' |
apify call architekture/google-hotels-scraper --silent --output-dataset

```

## MCP server setup

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