# Google Hotels Price Scraper – Vendor Rates, Price History (`yappman/google-hotels-prices`) Actor

Hotel prices from Google Hotels: per-vendor rates (OTA price ladder), rate comparison and price history for tracked stays. API and MCP-ready. Data-quality figures: data.yappman.com/data-quality

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

## Pricing

from $1.40 / 1,000 hotels

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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, Vendor Rates

Get Google Hotels prices for a destination (a city, area or landmark) and dates as structured data: one row per hotel with the nightly price, stay total, rating, star class (hotels only) and coordinates, where Google shows them.

A real row from a Lisbon search (the image URL is left out):

```json
{
  "hotelId": "ChkI7drbt9vwh-JTGg0vZy8xMWI2cGMwcjE4EAE",
  "name": "InterContinental Lisbon",
  "propertyType": "hotel",
  "hotelClass": 5,
  "rating": 4.6,
  "reviewCount": 2998,
  "address": null,
  "latitude": 38.726817,
  "longitude": -9.1552236,
  "checkIn": "2026-10-23",
  "checkOut": "2026-10-25",
  "nights": 2,
  "adults": 2,
  "rooms": 1,
  "leadPrice": {"perNight": 204.06, "total": 408, "currency": "USD", "vendor": null, "isSponsored": null},
  "vendorRates": null,
  "amenities": [],
  "amenityCodes": [165, 29, 15, 2, 18, 8, 4, 23, 24, 1, 31, 27, 7, 11, 25],
  "url": "https://www.google.com/travel/hotels/entity/ChkI7drbt9vwh-JTGg0vZy8xMWI2cGMwcjE4EAE",
  "filtersApplied": {"google": [], "actor": []},
  "history": [],
  "scrapedAt": "2026-10-09T06:30:32.350585+00:00"
}
```

A default run (the prefilled input: Lisbon, 2 adults, a two-night stay, 5 hotels) costs up to about $0.015 on the free tier: 5 hotel rows at $0.002 each, plus the $0.005 Actor start fee. It costs less if fewer hotels come back, and a blocked run costs only the start fee. Hotel rows cost less on higher Apify plans; the start fee is flat.

**Run it with the default input.**

[![Lisbon hotels demo: running the Actor with the default input](https://data.yappman.com/assets/lisbon-hotels-demo.gif)](https://youtu.be/sURf-8czh8g)

16-second demo on YouTube: https://youtu.be/sURf-8czh8g

### What it does

Get hotel prices from Google Hotels as structured data. Search a destination and a pair of dates and get one row per hotel: the lead nightly price Google shows, the total for the stay, star class, guest rating, review count, amenities and coordinates. In search mode you can add the per-vendor rate ladder (the rate each booking vendor and OTA shows, such as Booking.com, Expedia and Hotels.com) to each row, for rate comparison and price comparison; hotel mode returns no vendor rates in v0.1. You can also look up specific hotels by Google hotel id or Google Travel URL for one pair of dates, and read weekly lead-price history for the hotels in the cities the Actor tracks, for price tracking over time. It needs no browser and returns no personal data, and AI agents can call it through Apify's MCP server.

It is for travel-analytics and rate-intelligence teams, metasearch builders, researchers pricing destinations, and anyone who needs Google's hotel prices as structured data on a stable schema.

Every price belongs to the searched dates, and every row carries those dates.

### Modes

| Mode | Status | What it does |
| --- | --- | --- |
| `search` | Available | Hotels for each destination and dates in `searches`. |
| `hotel` | Available | The hotels in `hotels`, for the one pair of dates in `dates`. Returns no vendor rates in v0.1. |
| `history` | Available | Weekly lead-price snapshots for hotels the Actor tracks, for the dates they are tracked on. See History mode. |
| `tracked` | Available, free | Lists every tracked hotel id with its dates. See Tracked mode. |

### Input

The default input (one search for Lisbon, two adults, a two-night stay starting 14 days after the run, 5 hotels in the prefilled input and 20 if `maxItems` is left out, no vendor rates) runs without changes.

| Field | Type | Default | Limits |
| --- | --- | --- | --- |
| `mode` | `search`, `hotel`, `history`, `tracked` | `search` | One mode per run. `tracked` takes no other input and is free. |
| `searches` | array of objects | one item: Lisbon, 2 adults, 1 room | `search` mode. Up to 20 items; at least 1 in `search` mode. |
| `searches[].destination` | string | required | 1 to 200 characters; a city, area or landmark. No control characters. |
| `searches[].checkIn`, `searches[].checkOut` | `YYYY-MM-DD` | see below | `checkOut` must be after `checkIn`. If left out, the stay is two nights starting 14 days after the run. |
| `searches[].adults` | integer | `2` | 1 to 12. |
| `searches[].rooms` | integer | `1` | Must be `1`. Google ignores the rooms field, so other values are rejected rather than charged for a setting that changes nothing. |
| `hotels` | array of strings | `[]` | `hotel` and `history` modes: Google hotel ids or the hotel's Google Travel page URL. Up to 1,000 entries in `hotel` mode (the `maxItems` cap) and 100 in `history` mode, each up to 500 characters. Required in both modes. A `history` run stops after 20 untracked hotels in a row; run `tracked` first to see what's covered. |
| `dates` | object `{ "checkIn", "checkOut" }` | none | `hotel` and `history` modes: required, `YYYY-MM-DD`, `checkOut` after `checkIn`. Both modes use 2 adults and 1 room; there is no adults input for them. |
| `includeVendorRates` | boolean | `false` | Adds the per-vendor rate ladder to each row. `search` mode only for now: in `hotel`, `history` and `tracked` modes it is rejected with an input error, and `hotel` mode returns no vendor rates in v0.1. One extra request per hotel. Charged as its own event (see Pricing). |
| `filters` | object | `{}` | See Filters. |
| `sortBy` | `relevance`, `price`, `rating`, `reviews` | `relevance` | Order of results as Google sorts them. |
| `currency` | string | `USD` | ISO 4217 code, three capital letters. |
| `language` | string | `en` | Google's `hl`, e.g. `en` or `en-GB`. |
| `country` | string | `us` | Two lowercase letters; Google's `gl`. |
| `maxItems` | integer | `20` | 1 to 1,000. Hotels returned per run, across all searches. |

One search returns at most about 540 hotels (30 pages), whatever `maxItems` is.

There is no proxy input. The Actor manages its own network egress.

#### Filters

`minPrice`, `maxPrice`, `minRating` and `hotelClass` are applied by the Actor after the results are fetched, and each row's `filtersApplied.actor` lists the ones applied.

| Filter | Meaning | Notes |
| --- | --- | --- |
| `minPrice` | Minimum lead price per night, 0 or more | Hotels with no lead price are excluded when a price filter is set. |
| `maxPrice` | Maximum lead price per night, 0 or more | As above. |
| `minRating` | Minimum guest rating, 0 to 5 | Hotels with no rating are excluded. |
| `hotelClass` | Array of star classes, each 1 to 5 | Hotels with no star class are excluded. |
| `freeCancellation` | Rejected for now | Google's results carry no cancellation terms the Actor can check. |
| `amenities` | Rejected for now | Most hotels carry Google's amenity codes without labels (see `amenityCodes`), so there is nothing reliable to match against. |

Because filters are applied after fetch, a run with a narrow filter can fetch many hotels to return few. If every hotel is filtered out, the run ends as blocked with the reason "no hotel matched the filters" and charges no `hotel`/`vendor-rates`/`history` event (only the platform's own start fee applies).

Very narrow filters can stop a search early (after 6 pages with no match, or once far more hotels were checked than returned); sorting by price or rating pages toward matches faster. The run summary's `stoppedEarly` says when this happened.

### Output

One row per hotel per search (`search`), per looked-up hotel (`hotel`), or per tracked hotel (`history`).

Prices (`leadPrice` and `vendorRates`) are Google's pre-tax figures, nightly and for the stay, rounded half-up to the currency's minor unit (2 decimals for USD, 0 for JPY).

| Field | Type | Notes |
| --- | --- | --- |
| `hotelId` | string | Google's property id, stable across searches. |
| `name` | string | |
| `propertyType` | string or null | `hotel` when Google shows a star class; the vacation-rental label Google shows when present; otherwise null. |
| `hotelClass` | integer or null | Star class, 1 to 5. |
| `rating` | number or null | Google's aggregate guest rating, rounded to one decimal (half-up). |
| `reviewCount` | integer or null | Number of reviews behind the rating. |
| `address` | string or null | Filled in `hotel` mode. Null in `search` mode (a search result does not carry it), and normally null in `history` mode (stored rows come from search results). |
| `latitude`, `longitude` | number, each nullable | |
| `checkIn`, `checkOut` | date | The searched itinerary, on every row. |
| `nights`, `adults`, `rooms` | integer | The searched itinerary. |
| `leadPrice` | object or null | The price Google shows first: `perNight`, `total`, `currency`, `vendor`, `isSponsored`. Null when Google shows no price. Search results do not attribute the lead price to a vendor, so `vendor` and `isSponsored` are filled only when `includeVendorRates` is on and a vendor's offer matches the lead price; otherwise null. |
| `vendorRates` | array or null | Null unless `includeVendorRates` is on. One entry per vendor, that vendor's cheapest total across Google's room groups: `vendor`, `perNight`, `total`, `currency`, `guests`, `isSponsored`, `bookingUrl`, `bookingUrlType`, `vendorDomain`, `offerCount`. |
| `amenities` | array of strings | Google's own labels, only where Google serves them (vacation rentals carry inline labels). Empty otherwise. |
| `amenityCodes` | array of integers | Google's numeric amenity codes as served, for hotel-type records whose labels are not in the response. Empty otherwise. No labels are guessed. |
| `imageUrl` | string or null | One thumbnail. |
| `url` | string | The hotel's Google Travel page. |
| `filtersApplied` | object | `google` and `actor`: which filters each side applied. Today every filter is applied by the Actor. |
| `history` | array | Empty in `search` and `hotel` modes. In `history` mode: `{ readAt, leadPrice, deltaPerNight }` per weekly snapshot, oldest first. `deltaPerNight` is that snapshot's `leadPrice.perNight` minus the previous one's, null for the first snapshot or when either price is missing. |
| `scrapedAt` | ISO 8601 date-time | |

Within `vendorRates`:

- `offerCount` is how many of Google's room-group offers were collapsed into that vendor's cheapest entry.
- `bookingUrl` and `bookingUrlType` are always null. Google's click links carry session identifiers, which the Actor never passes on.
- `vendorDomain` is the vendor site's registrable domain only (for example `booking.com`, not `www.booking.com` or `secure.booking.com`), or null when Google's link does not carry one. To book, use `vendorDomain` together with the row's `url` (Google's hotel page).

Example `search` row without vendor rates (values illustrative):

```json
{
  "hotelId": "ChIJ…",
  "name": "Hotel Example Lisboa",
  "propertyType": "hotel",
  "hotelClass": 4,
  "rating": 4.6,
  "reviewCount": 2310,
  "address": "Rua Exemplo 1, Lisbon",
  "latitude": 38.71,
  "longitude": -9.14,
  "checkIn": "2026-11-06",
  "checkOut": "2026-11-08",
  "nights": 2,
  "adults": 2,
  "rooms": 1,
  "leadPrice": { "perNight": 142, "total": 284, "currency": "EUR", "vendor": null, "isSponsored": null },
  "vendorRates": null,
  "amenities": [],
  "amenityCodes": [6, 29, 2, 4, 23, 1, 11],
  "imageUrl": "https://…",
  "url": "https://www.google.com/travel/hotels/entity/ChIJ…",
  "filtersApplied": { "google": [], "actor": ["minRating"] },
  "history": [],
  "scrapedAt": "2026-09-28T10:00:00Z"
}
```

With `includeVendorRates` on, `leadPrice.vendor` and `leadPrice.isSponsored` are filled when a vendor's offer matches the lead price, and `vendorRates` holds entries like this one (illustrative):

```json
{
  "vendor": "Booking.com",
  "perNight": 142,
  "total": 284,
  "currency": "EUR",
  "guests": 2,
  "isSponsored": false,
  "bookingUrl": null,
  "bookingUrlType": null,
  "vendorDomain": "booking.com",
  "offerCount": 3
}
```

### History mode

`history` mode returns weekly lead-price snapshots for hotels the Actor tracks, for the dates they are tracked on. It makes no Google request: it reads stored snapshots.

- Input: `mode: "history"`, `hotels` (ids or Google Travel URLs) and `dates`. Adults are fixed at 2.
- Tracked hotels currently come from a seed set of ten cities: New York, Los Angeles, Chicago, Miami, Las Vegas, London, Edinburgh, Lisbon, Paris and Barcelona. Stays are two nights, 2 adults, starting 2 and 6 weeks out, and are re-read weekly.
- Rows are returned as tracked: one fixed series (USD, US locale). The `currency`, `language` and `country` inputs do not change them.
- `includeVendorRates` is rejected in this mode, and history rows carry no vendor rates.
- One row per tracked hotel, with `history` listing its snapshots oldest first.
- A hotel and dates that are not tracked return no row and are not charged. If none of the requested hotels is tracked, the run fails with "none of the requested hotels are tracked for these dates".
- A row with two or more snapshots is charged the `history` event; a row with one snapshot is free.

No coverage beyond the seed set is promised. Run `tracked` first to see which hotels and dates `history` covers.

### Tracked mode

`tracked` mode is free and makes no Google request. It lists every hotel currently tracked, and it writes the list to the run's key-value store record `TRACKED_HOTELS`, not to the dataset.

- Input: `mode: "tracked"`. Nothing else is needed; `includeVendorRates` is rejected.
- The record holds `generatedAt`, `count` and `items`. Each item has `hotelId`, `checkIn`, `checkOut`, `adults` and `destination` (the seed city), sorted by destination, then check-in, then hotel id.
- Use an item's `hotelId` as `hotels` and its `checkIn` / `checkOut` as `dates` in a `history` run. `history` uses 2 adults.
- This code charges no `hotel`/`vendor-rates`/`history` event for a `tracked` run - only the platform's own `apify-actor-start` launch fee applies, the same as any run.

### Standby

On Apify Standby this Actor also answers two HTTP endpoints directly, instead of running as a single batch job:

- `GET /search?destination=&checkIn=&checkOut=&max=` - the same validation as `search` mode's input (`destination` up to 200 characters, `rooms` must be `1` if given at all). Optional: `adults`, `sortBy`, `currency`, `language`, `country`, `includeVendorRates`, and the filters as query parameters (`minPrice`, `maxPrice`, `minRating`, `hotelClass`, repeated or comma-separated). `max` is 1 to 100 (narrower than batch's `maxItems`, since one call answers in a single response) and defaults to 20; capped at 20 when `includeVendorRates` is on (each vendor-rates lookup takes a few seconds and all of them must finish inside the request timeout).
- `GET /hotel?id=&checkIn=&checkOut=` - the same validation as `hotel` mode's input. Optional: `currency`, `language`, `country`. `includeVendorRates` is rejected here, the same as in batch `hotel` mode.

Both use the same validation, normaliser, dedupe and filters as a batch run of this Actor, and return `{ "items": [...], "count", "blocked": null | "<reason>", "vendorRatesCharged": n, "chargeLimitReached": true | false }` - `items` is exactly the Output row schema above; `vendorRatesCharged` is how many of the returned rows actually got the extra `vendor-rates` charge (normally equal to how many carry `vendorRates`, but can be lower if that one extra charge failed after the `hotel` charge already succeeded - the rows are still returned either way); `chargeLimitReached` is `true` when the run's own charge limit either trimmed `max` before fetching anything or was actually hit while charging, `false` otherwise. Nothing is pushed to this Actor's dataset from Standby; the response is the only place the rows go.

Only the parameters listed above are accepted per endpoint: an unknown query parameter, a single-value parameter repeated, or a value that does not parse in its documented format (a plain base-10 integer, a plain decimal, or `true`/`false`/`1`/`0` for a boolean) is always rejected with `400`, naming the parameter - never silently dropped or defaulted.

Each row returned is charged the `hotel` event (and `vendor-rates` too, for a row that ends up carrying vendor rates when `includeVendorRates` is on) *before* it is returned, so you are only ever billed for rows you actually got back. A request is charged before its response is written, so a dropped connection after that point is still charged. The platform's own `apify-actor-start` fee applies once per Standby run (the long-lived server process), not per request. **In Standby mode you also pay the Apify platform usage of the run that serves your request - its compute units and any residential proxy traffic it uses - on top of these per-row events** (Apify support, relayed 9 Oct 2026); a batch run instead absorbs that platform usage into the row price, as the Pricing section below describes. The platform sets each Standby run's own charge limit, and it can vary by caller - you cannot set or raise it yourself. This Actor reads the limit's remaining headroom from the run itself before fetching anything and never assumes a figure: a request can be capped below `max`, or refused outright, by that headroom, and `chargeLimitReached` says when it was. A request that found candidate rows but whose charge covered none of them gets `402` ("the run's charge limit allows no further rows"); a request that found nothing at all (or was blocked) gets `200` with `items: []` and a `blocked` reason, and charges no `hotel`/`vendor-rates` event either way.

`history` and `tracked` mode are not available on Standby.

This server handles one request at a time: a second request made while one is already in progress gets `503` immediately (not queued), so you should not send concurrent requests to your own running instance. That run (including its platform usage above) stops after 60 idle seconds, so an idle endpoint costs you little. The first response from a run must arrive within 5 minutes including cold start (an Apify platform limit); this Actor's own request timeout shrinks accordingly for a run's very first request only, so that limit is always met.

See `.actor/web_server_schema.json` for the full OpenAPI description, including every parameter and error code.

### Pricing

Pay per event. Prices below are the free-tier prices; paid Apify tiers are discounted: Bronze, Silver and Gold at 10%, 20% and 30% off the free-tier price; Platinum and Diamond at the Gold price.

**Standby pays platform usage too.** In Standby mode (see above) you also pay the Apify platform usage of the run that serves your request - its compute units and any residential proxy traffic it uses - on top of the events below (Apify support, relayed 9 Oct 2026). A batch run instead absorbs that platform usage into the row price; there is nothing extra to pay beyond the table below for a batch run.

| Event | Charged when | Price |
| --- | --- | --- |
| Actor start (`apify-actor-start`) | Charged by Apify itself, at launch - not by this Actor's own code. | $0.005 per GB of memory (1,024 MB default = one event) |
| `hotel` | Each hotel row pushed to the dataset. | $0.002 ($2.00 per 1,000) |
| `vendor-rates` | Each hotel row that carries vendor rates, and only when `includeVendorRates` is on. Never charged otherwise. | $0.0025 ($2.50 per 1,000) |
| `history` | Each history row with two or more snapshots. A one-snapshot row is free. | $0.01 |

`tracked` mode charges no event of this Actor's own. `history` mode does not charge `hotel`. Blocked or empty runs charge no `hotel`/`vendor-rates`/`history` event: no row stored means no row charge - only the platform's own start fee applies, the same as any run. Filtered-out hotels are not pushed and not charged.

### What to expect

- **Blocked runs fail visibly.** If Google refuses the requests or the responses no longer match the expected layout, the run ends with an error status ("Blocked: <reason>") and a `RUN_SUMMARY` record in the run's default key-value store explaining why. The Actor never writes placeholder or sentinel rows.
- **Currency is as requested.** In `search` and `hotel` modes prices are in the currency you set in `currency`, as Google returns them. The Actor never converts.
- **No booking links.** `bookingUrl` is always null because Google's click links carry session identifiers, which the Actor never passes on and never follows. Use `vendorDomain` and the row's `url`.
- **Network egress is managed by the Actor.** There is no proxy setting to configure.

### FAQ

**How do I get prices in another currency, language or country?**
Set `currency` (e.g. `GBP`), `language` (e.g. `en-GB`) and `country` (`uk` for the United Kingdom: it returned a normal Google results page when tested; `gb` has not been tested). They map to Google's `curr`, `hl` and `gl`, sent unchanged. Currency is as Google returns it; nothing is converted.

**Why does a price differ from what I see on Google?**
Every price is specific to the itinerary searched (dates, adults) and to the request as an anonymous visitor. Prices you see in your own browser can differ if you are logged in, have a personalised profile or different location, or searched a different number of guests. Google also changes prices frequently, and `rooms` is not honoured by Google, so it does not affect results.

**What are the limits?**
Up to 20 searches per run, up to 1,000 hotels per run (`maxItems`), and about 540 hotels per search. Requests are paced at one per two seconds, so large runs take time. As a guide, a 20-hotel search takes about 10 seconds, and 5 hotels with vendor rates take 20–30 seconds.

**Can I filter by free cancellation or amenities?**
Not yet; see Filters.

**Why did my search return fewer rows than `maxItems`?**
Very narrow filters can stop a search early (after 6 pages with no match, or once far more hotels were checked than returned); sorting by price or rating pages toward matches faster. The run summary's `stoppedEarly` says when this happened.

**Can I read rate history?**
Yes, for tracked hotels only; see History mode.

**Are prices before or after tax?**
Pre-tax, as Google's nightly figure is.

### Read more

- [How to get Google Hotels prices with an API in 2026](https://dev.to/yappman/how-to-get-google-hotels-prices-with-an-api-in-2026-4120): a worked example with input and output, pricing, and limits.

### Legal

This Actor is not affiliated with, endorsed by or sponsored by Google. Google and Google Hotels are trademarks of Google LLC and are named here only to say where the data comes from. Google's terms restrict automated access to its services; you are responsible for how you use the data and for complying with applicable law and terms. This is not legal advice.

The Actor collects no personal data: no reviewer names, no review text, no user data. It returns what Google shows any visitor: hotel details, prices and vendor names. For history mode it keeps a list of tracked hotel ids and stay dates, and weekly price snapshots for them; it records nothing about who runs the Actor or what they search for.

# Actor input Schema

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

What to run. search: find hotels for a destination. hotel: look up hotels by id or Google Travel URL for one pair of dates. history: stored lead-price snapshots for tracked hotels. tracked: list the tracked hotels (free).

## `searches` (type: `array`):

search mode only. Up to 20 searches, each with destination (a city, area or landmark, 1-200 characters), optional checkIn and checkOut (YYYY-MM-DD, checkOut after checkIn), adults (1-12, default 2) and rooms (must be 1, Google ignores it). Dates should be in the future. Give both dates, or neither: with neither, the stay is 2 nights starting 14 days after the run starts; checkIn alone gives a 2-night stay; checkOut without checkIn is rejected. Each search reads at most 30 result pages (about 540 hotels); the run stops at maxItems.

## `hotels` (type: `array`):

hotel and history modes only. Google hotel ids (they start with Ch) or Google Travel hotel page URLs containing /travel/hotels/entity/<id>. Up to 1,000 in hotel mode and 100 in history mode, each up to 500 characters. Entries that are neither are skipped.

## `dates` (type: `object`):

hotel and history modes only. { "checkIn": "YYYY-MM-DD", "checkOut": "YYYY-MM-DD" }, checkOut after checkIn, both in the future. Both modes use 2 adults and 1 room; history returns only stays stored for exactly these dates.

## `includeVendorRates` (type: `boolean`):

search mode only (rejected in other modes). Adds each hotel's per-vendor rates, with the vendor's registrable domain as vendorDomain. Off by default. It makes one extra request per hotel and is charged as its own event for each hotel that comes back with rates.

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

search and hotel modes. Optional keys: minPrice and maxPrice (per night, in the chosen currency), minRating (0-5; compares against the rating rounded to one decimal), hotelClass (a list of 1-5). The Actor applies them after fetching; hotels that fail a filter, or lack the data it needs, are dropped and do not count toward maxItems. Each row's filtersApplied.actor names the filters set. freeCancellation: true and a non-empty amenities list are rejected as not supported yet.

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

search mode only. The order Google returns results in: relevance (default), price (lowest first), rating (highest first) or reviews (most reviewed). maxItems keeps the first hotels in that order.

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

search and hotel modes. Three-letter uppercase currency code, e.g. USD or GBP. Prices are shown as Google returns them in this currency; the Actor never converts.

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

search and hotel modes. Language code sent to Google as hl: two lowercase letters, optionally with a region, e.g. en or en-GB.

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

search and hotel modes. Two-letter lowercase country code sent to Google as gl, e.g. us. uk for the United Kingdom returned a normal Google results page when tested; gb has not been tested.

## `maxItems` (type: `integer`):

search and hotel modes. The most hotels returned in one run, across all searches, 1 to 1,000. Hotels removed by filters, and duplicates, do not count. History mode ignores it (up to 100 hotels).

## Actor input object example

```json
{
  "mode": "search",
  "searches": [
    {
      "destination": "Lisbon",
      "adults": 2,
      "rooms": 1
    }
  ],
  "hotels": [
    "ChgImbOjsv3Q9UkaDS9nLzExcnE5NTAxZmoQAQ"
  ],
  "dates": {
    "checkIn": "2027-03-10",
    "checkOut": "2027-03-12"
  },
  "includeVendorRates": false,
  "filters": {
    "minRating": 4,
    "hotelClass": [
      4,
      5
    ]
  },
  "sortBy": "price",
  "currency": "GBP",
  "language": "en-GB",
  "country": "us",
  "maxItems": 20
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

## `trackedHotels` (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": "search",
    "searches": [
        {
            "destination": "Lisbon",
            "adults": 2,
            "rooms": 1
        }
    ],
    "includeVendorRates": false,
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("yappman/google-hotels-prices").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": "search",
    "searches": [{
            "destination": "Lisbon",
            "adults": 2,
            "rooms": 1,
        }],
    "includeVendorRates": False,
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("yappman/google-hotels-prices").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": "search",
  "searches": [
    {
      "destination": "Lisbon",
      "adults": 2,
      "rooms": 1
    }
  ],
  "includeVendorRates": false,
  "maxItems": 5
}' |
apify call yappman/google-hotels-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yappman/google-hotels-prices"
        }
    }
}
```

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/PqDLqwvBbe5LoywAq/builds/AXMPTwOZWF7YdhsAV/openapi.json
