# Google Hotels Scraper: Prices, Rate Parity & OTA Offers (`egra_van/google-hotels-prices`) Actor

Get hotel prices from Google Hotels for any city, hotel name or link and your dates: nightly and total price, taxes, rating, reviews, stars, GPS and, optionally, each booking site's rate (Booking.com, Expedia, Hotels.com, official site) for rate parity and OTA price monitoring.

- **URL**: https://apify.com/egra\_van/google-hotels-prices.md
- **Developed by:** [Argentin Vazdautan](https://apify.com/egra_van) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 hotels

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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, Rate Parity & OTA Offers

Get **hotel prices from Google Hotels** for any city, landmark, hotel name or Google Hotels link, **for your exact dates and guests**: price per night, price with taxes and fees, total for the stay, deals ("20% less than usual"), guest rating, number of reviews, star class, GPS, photos, and optionally **the price on every booking site** Google compares: Booking.com, Expedia, Agoda, Hotels.com, Trip.com, the hotel's official site and 30+ more.

Built for **revenue managers, rate-parity checks, travel startups, price comparison sites and analysts** who need data that arrives on every run.

### Why this scraper

- **Fast and light:** plain HTTP requests with a real Chrome TLS fingerprint. No browser unless Google forces one.
- **Self-healing:** if Google shows a captcha, it retries on a new IP. If it shows the EU cookie consent page or blocks HTTP, it **automatically switches to a real Chrome browser**, which clicks through the consent page.
- **Two independent data paths:** the Google Hotels results page and Google's own internal hotel search endpoint. If one stops working, the other is used.
- **Clear logs:** every request logs the HTTP status, whether a consent or captcha page was detected, and which parser found the data. Unexpected pages are saved to the key-value store for inspection.
- **Fair pricing:** you pay per hotel with a price. Sold-out hotels without any price are free, and failed runs cost nothing.

### What you can scrape

| Input | Example | You get |
|---|---|---|
| **Searches** | `hotels in Paris`, `hotels near Times Square` | Up to N hotels per search, in Google's order, across result pages |
| **Hotel names** | `Hotel Lutetia Paris` | The best-matching hotel |
| **Google Hotels links** | `https://www.google.com/travel/hotels/entity/ChoI…` | That exact hotel. This is the best input for daily price tracking. |

Set **check-in and check-out** (or a relative date like `+30 days`), **adults, children with ages, currency, language and country**. You can filter by **price per night, minimum rating and hotel class**.

### Output example

```json
{
  "query": "hotels in New York",
  "position": 1,
  "hotelName": "The Manhattan at Times Square Hotel",
  "entityId": "ChkIooCAqvyy0fDgARoML2cvMWhoZ18zbWdzEAE",
  "url": "https://www.google.com/travel/hotels/entity/ChkIooCAqvyy0fDgARoML2cvMWhoZ18zbWdzEAE/prices?...",
  "rating": 3,
  "reviews": 9928,
  "hotelClass": 4,
  "hotelClassText": "4-star hotel",
  "address": "790 7th Ave, New York, NY 10019",
  "lat": 40.7622856,
  "lng": -73.9826404,
  "priceLowest": 137.91,
  "pricePerNight": 137.91,
  "pricePerNightText": "$138",
  "pricePerNightWithTaxes": 161.75,
  "priceBeforeTaxes": 97.91,
  "taxes": 23.84,
  "fees": 40,
  "priceTotal": 161.75,
  "currency": "USD",
  "checkIn": "2026-04-27",
  "checkOut": "2026-04-28",
  "nights": 1,
  "dealLabel": "20% less than usual",
  "offersCount": 36,
  "cheapestProvider": "Vio.com",
  "officialSitePrice": 158,
  "offers": [
    { "provider": "Vio.com", "price": 138, "priceWithTaxes": 162, "priceTotal": 162, "isOfficialSite": false, "isSponsored": false, "url": "https://www.google.com/travel/clk?...", "directUrl": "https://deals.vio.com/..." },
    { "provider": "The Manhattan at Times Square Hotel", "price": 158, "priceWithTaxes": 185, "priceTotal": 185, "isOfficialSite": true, "directUrl": "https://www.ihg.com/..." },
    { "provider": "Booking.com", "price": 158, "priceWithTaxes": 184.81, "priceTotal": 184.81, "isOfficialSite": false }
  ],
  "thumbnail": "https://lh3.googleusercontent.com/...",
  "phone": "(212) 581-3300",
  "website": "https://www.ihg.com/spnd/hotels/us/en/new-york/nycat/hoteldetail",
  "checkInTime": "4:00 PM",
  "checkOutTime": "12:00 PM",
  "googleMapsUrl": "https://maps.google.com/?cid=16204309452407439394",
  "dataSource": "page/http+rpc:AtySUc",
  "scrapedAt": "2026-04-20T08:00:00.000Z"
}
```

#### Price fields explained

- `pricePerNight`: the nightly price Google shows in the list for your country (`gl`). In the US this is **before taxes, including resort/service fees**. In many other countries Google already includes taxes.
- `pricePerNightWithTaxes`: nightly price with all taxes and fees. `priceBeforeTaxes`, `taxes` and `fees` are its parts.
- `priceTotal`: `pricePerNightWithTaxes × nights`.
- `priceLowest`: the lowest of Google's price and all loaded offers.
- `offers[].price` / `priceWithTaxes`: that booking site's nightly price. `isOfficialSite` marks the hotel's own website. `isSponsored` marks paid ads.
- `address` and `phone` are filled when offers are loaded or a hotel link is used. The list view does not contain them.
- `amenities` holds amenity names when Google includes them in text. `amenityCodes` holds Google's internal amenity IDs as returned.

### Pricing (pay per event)

| Event | When | Price |
|---|---|---|
| `hotel` | each hotel with a price saved to the dataset | $0.0025 ($2.50 / 1,000 hotels) |
| `hotel-offers` | extra, when the per-booking-site offers of a hotel are loaded | $0.0035 ($3.50 / 1,000 hotels) |

Example: 1,000 hotels with every booking site's price cost $6.00. Hotels without any price are saved for free. Set **Maximum cost per run** in the run options: the scraper stops cleanly at that limit and never loads offers it cannot charge for.

### Proxies: important

Google blocks datacenter IPs quickly. What to expect:

| Proxy | Result |
|---|---|
| **GOOGLE\_SERP** (Apify proxy group) | Best value for Google. Requests are sent to `http://www.google.com` as this proxy requires. |
| **RESIDENTIAL** (Apify proxy group) | Very reliable, charged per GB. Pages are compressed and the browser does not load images, so traffic stays low. |
| Default (shared datacenter) | Works for small runs. Expect `captcha=YES` in the log after some requests. Every retry uses a new IP, but the pool is small. |

On the **Apify free plan** only a few shared datacenter IPs are available, and GOOGLE\_SERP and RESIDENTIAL are not included. Keep runs small (a few searches, 20-50 hotels) and schedule them apart.

### Tips

- **Tracking the same hotels daily:** run once with a search, copy the `entityId`s (or `url`s) of the hotels you care about into **Google Hotels links**, and schedule the run.
- **Rate parity:** turn on **Include prices of every booking site** and compare `officialSitePrice` with `priceLowest` / `cheapestProvider`.
- **Many dates:** run one task per date (the dates are part of each request). Schedules and the Apify API make this easy.
- **Children:** give their ages. Searches with children use Google's internal search endpoint, which accepts ages.

### Limitations

- Google Hotels is not an official API. Google can change its internal data format at any time, and the scraper is updated when that happens. The log always says which data path was used, so a change is easy to spot.
- Prices depend on the country you search from (`country`), dates, guests and currency, just like on google.com.
- The number of offers per hotel and their order are decided by Google.

### FAQ

**Is it legal?** The scraper collects publicly visible price information, like a person using Google Hotels. You are responsible for complying with Google's terms and the laws that apply to you, including when you republish data.

**Why did a run fail?** Open the log. Each request line shows `status`, `consent=`, `captcha=` and the parser used. `captcha=YES` on every retry means the proxy IPs are blocked: switch to the GOOGLE\_SERP or RESIDENTIAL proxy. Saved `DEBUG-…` pages in the key-value store show exactly what Google returned.

**Can I get more than ~20 hotels per search?** Yes. Raise **Max hotels per search**. The scraper follows Google's result pages (about 18-20 hotels each) up to **Max result pages per search**.

# Actor input Schema

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

What you would type into Google Hotels, one per line: a city, district or landmark, e.g. "hotels in Paris", "hotels near Times Square", "Lisbon Alfama". Each search returns up to "Max hotels per search".

## `hotelNames` (type: `array`):

Specific hotels to look up by name, e.g. "Hotel Lutetia Paris". Add the city for best matching. The best-matching result is returned (hotels without a confident match are skipped and listed in the log).

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

Links to hotels on Google Hotels (they contain "/travel/hotels/entity/..."), or the entity ID itself (the "entityId" output field). Most reliable way to track the same hotels every day.

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

Check-in date as YYYY-MM-DD, or relative like "+30 days". Default: 30 days from today.

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

Check-out date as YYYY-MM-DD, or relative like "+32 days". If empty, check-out = check-in + "Nights".

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

Length of stay used when no check-out date is given (1-30).

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

Number of adult guests (1-12). Prices depend on it.

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

Number of children (0-8). With children, searches use Google's internal search endpoint, which accepts child ages.

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

One age (0-17) per child, e.g. \["5", "9"]. If empty, every child is treated as 8 years old.

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

3-letter currency code for all prices, e.g. USD, EUR, GBP, MDL, RON.

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

Also load each hotel's offers list: Booking.com, Expedia, Agoda, Hotels.com, the hotel's official site and 30+ others, with price, price with taxes, official-site and sponsored flags and link. Needs one extra request per hotel and is charged as an extra event.

## `maxOffersPerHotel` (type: `integer`):

Keep only the N cheapest offers per hotel. 0 = keep all.

## `minPrice` (type: `integer`):

Skip hotels cheaper than this per night (in the chosen currency, before taxes, as shown on Google). Hotels without a price are skipped when a price filter is set.

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

Skip hotels more expensive than this per night (in the chosen currency, before taxes, as shown on Google).

## `minRating` (type: `string`):

Only hotels with at least this Google guest rating (out of 5).

## `hotelClass` (type: `array`):

Only hotels with these official star classes. Empty = any. Hotels without a class are skipped when set.

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

Stop each search after this many hotels (after filters). Google shows about 18-20 hotels per page.

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

Safety limit on result pages read per search (about 18-20 hotels each), useful with strict filters.

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

Google interface language (hl), e.g. "en", "de", "fr". Affects hotel class texts and descriptions.

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

Country the search is made from (gl), 2 letters, e.g. "us", "gb", "de". Google shows prices and taxes the way travelers from this country see them.

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

Google blocks datacenter IPs quickly. Best: Apify Proxy group GOOGLE\_SERP or RESIDENTIAL (paid Apify plans). The free plan's shared datacenter IPs work only for small runs and often get "unusual traffic" pages.

## `useBrowser` (type: `string`):

"fallback": fast HTTP requests first; if Google blocks them or shows a consent page, switch to a real Chrome browser for the rest of the run. "always": use the browser from the start (slower, more memory). "never": HTTP only.

## `searchMethod` (type: `string`):

Advanced. "auto": the search results page, with Google's internal RPC endpoint as a backup (and first choice when children are set). "page" or "rpc" force one method.

## `maxRetries` (type: `integer`):

How many times a blocked or failed request is retried, each time with a new session and proxy IP.

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

How many hotels' offers are loaded at the same time. Lower is gentler on Google (fewer blocks).

## `requestTimeoutSecs` (type: `integer`):

Timeout for one request to Google.

## `saveDebugPages` (type: `boolean`):

When Google returns something unexpected (captcha, consent, no data), save up to 5 of those responses to the key-value store (keys DEBUG-...) so they can be inspected.

## `baseUrl` (type: `string`):

For testing only: send all requests to this address instead of https://www.google.com (e.g. a local mock server). Leave empty.

## Actor input object example

```json
{
  "queries": [
    "hotels in Paris"
  ],
  "nights": 1,
  "adults": 2,
  "children": 0,
  "currency": "USD",
  "includeOffers": false,
  "maxOffersPerHotel": 0,
  "maxHotelsPerQuery": 20,
  "maxPagesPerQuery": 10,
  "language": "en",
  "country": "us",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "useBrowser": "fallback",
  "searchMethod": "auto",
  "maxRetries": 4,
  "maxConcurrency": 3,
  "requestTimeoutSecs": 45,
  "saveDebugPages": true
}
```

# Actor output Schema

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

All result items of this run.

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

Summary of the run with counts and download links.

# 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"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

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

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

```

## MCP server setup

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