# Google Hotels Scraper — Prices, Offers & Ratings (`tortuga/google-hotels-scraper`) Actor

Scrape Google Hotels for any destination and dates: nightly and total prices, every booking site's offer, ratings, reviews, amenities, location scores and coordinates.

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

## Pricing

from $3.00 / 1,000 property scrapeds

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 — Prices, Offers & Ratings

Scrape **Google Hotels** for any destination and stay: every hotel's **lowest price per night and for the whole
stay** (with and without taxes), deal badge, hotel class, **guest rating and review count**, rating distribution,
what reviewers mention, amenities, **location scores**, nearby places, check-in/out times, coordinates and photos.
Optionally open each hotel's Google page to get **every booking site's offer** (Booking.com, Expedia, Agoda,
Hotels.com, the hotel's official site, ...) with price per night and total, free cancellation, official-site flag
and the **direct booking link**, plus address, phone and ratings on other sites with a review snippet.

Use it for hotel rate shopping and rate-parity checks, revenue management, travel price comparison, market
research and price alerts.

### What data does it extract?

| Field | Example | Notes |
|---|---|---|
| `name`, `type`, `category` | Pullman Paris Tour Eiffel, `hotel`, `hotel` | `type`: `hotel` or `vacation_rental` |
| `propertyToken`, `hotelId`, `url` | `ChgIxfW9593Qkbu3ARoLL2cvMXRnczA3ZHQQAQ`, `5592324419017481846` | Google's property token (stable; used in Google Hotels links) and the hotel's Google page for your dates |
| `starRating`, `hotelClass` | `4`, `4-star tourist hotel` | |
| `rating`, `reviewCount`, `ratingDistribution` | `4.6`, `17329`, `{"5": 13846, "4": 1940, ...}` | Google reviews, 1-5 |
| `reviewTopics` | `[{"topic": "Service", "mentions": 185, "positive": 129, "negative": 46}, ...]` | what reviews mention |
| `pricePerNight`, `pricePerNightWithTaxes` | `335`, `386` | Google's lowest price for your dates and guests |
| `price`, `priceWithTaxes`, `taxesAndCharges` | `670`, `773`, `103.25` | for the whole stay |
| `currency`, `checkIn`, `checkOut`, `nights`, `adults`, `childrenAges` | `USD`, `2026-11-10`, `2026-11-12`, `2`, `2`, `[]` | |
| `deal`, `dealBadge` | `27% less than usual`, `GREAT DEAL` | |
| `amenities` | `["Free Wi-Fi", "Breakfast ($)", "Fitness center", ...]` | as Google prints them |
| `locationRating`, `locationScores` | `4.2`, `{"overall": 4.2, "thingsToDo": 4.6, "restaurants": 4, "transit": 4.1, "airportAccess": 3.9}` | Google's location scores |
| `nearbyPlaces` | `[{"name": "Eiffel Tower", "type": "attraction", "travelTimes": [{"mode": "walk", "time": "9 min"}]}]` | |
| `checkInTime`, `checkOutTime` | `4:00 PM`, `11:00 AM` | |
| `latitude`, `longitude`, `countryCode`, `website` | `48.8556`, `2.2929`, `FR`, hotel website | |
| `description`, `thumbnail`, `images` | short description, photo URLs | |
| `propertyDetails` | `["Entire apartment", "Sleeps 4", "1 bedroom"]` | vacation rentals |
| `position`, `page`, `searchQuery`, `destination`, `scrapedAt` | `1`, `1`, `Paris`, `Paris` | where it appeared |

With **Include all booking offers** on, each property also gets:

| Field | Example |
|---|---|
| `offers` | `[{"provider": "Agoda", "pricePerNight": 91, "pricePerNightWithTaxes": 100, "totalPrice": 181, "totalPriceWithTaxes": 199, "currency": "USD", "freeCancellation": false, "freeCancellationUntil": null, "isOfficialSite": false, "bookingUrl": "https://www.agoda.com/partners/partnersearch.aspx?...&CkInDay=10&CkInMonth=11...", "googleLink": "https://www.google.com/travel/lodging/clk?...", "providerId": 100337601, "logo": "..."}, ...]` |
| `offersCount`, `lowestOfferPrice`, `officialSitePrice` | `24`, `86`, `86` |
| `address`, `phone`, `placeId`, `googleMapsUrl` | `3 Rue Verniquet, 75017 Paris, France`, `+33 1 43 80 26 30`, ... |
| `neighborhood`, `neighborhoodDescription`, `fullDescription` | `Plaine-Monceau`, ... |
| `reviewSources` | `[{"source": "Tripadvisor", "rating": 4.4, "ratingScale": 5, "reviewCount": 344, "url": "...", "snippet": {"author": "...", "date": "a month ago", "rating": 5, "text": "..."}}]` |
| `sponsoredProviders` | booking sites with a paid ad slot on the page |

`bookingUrl` is the booking site's own link for your dates, taken from Google's click link (Google embeds it); the
booking site is never visited and no ad click is made.

### How to scrape Google Hotels

1. Enter one or more **destinations** (`Paris`, `hotels near Times Square`, `Bali`) or paste Google Hotels URLs.
2. Set the **check-in** date (`2026-12-20`, or `+30 days` for a rolling date), **nights** or check-out, **adults**
   and **children's ages**, the **currency** and the **country** you price for.
3. Optional filters: price per night, hotel class, guest rating, amenities, free cancellation, sort order,
   vacation rentals.
4. Set **Max properties** and run. Download JSON, CSV or Excel, or read the dataset through the API.

```json
{
  "queries": ["Paris"],
  "checkIn": "+30 days",
  "nights": 2,
  "adults": 2,
  "currency": "USD",
  "hotelClass": ["4", "5"],
  "maxPrice": 300,
  "sortBy": "lowest_price",
  "maxItems": 100
}
```

Example output (abridged):

```json
{
  "propertyToken": "ChgIxfW9593Qkbu3ARoLL2cvMXRnczA3ZHQQAQ",
  "name": "Pullman Paris Tour Eiffel",
  "type": "hotel",
  "starRating": 4,
  "rating": 4.6,
  "reviewCount": 17329,
  "pricePerNight": 335,
  "pricePerNightWithTaxes": 386,
  "price": 670,
  "priceWithTaxes": 773,
  "currency": "USD",
  "checkIn": "2026-11-10",
  "checkOut": "2026-11-12",
  "amenities": ["Breakfast ($)", "Free Wi-Fi", "Fitness center", "Bar", "Restaurant"],
  "locationRating": 4.2,
  "latitude": 48.8556488,
  "longitude": 2.2928584,
  "checkInTime": "4:00 PM",
  "checkOutTime": "11:00 AM",
  "url": "https://www.google.com/travel/hotels/entity/ChgIxfW9593Qkbu3ARoLL2cvMXRnczA3ZHQQAQ?q=Paris&hl=en&gl=us&curr=USD&ts=..."
}
```

### How many hotels does a search return?

Google Hotels shows 18-20 properties per page (about 2 of them vacation rentals, left out unless you turn them on)
and the actor follows Google's own "Next" page token, so a city search goes 30+ pages deep (tested to results
541-560 for Paris). Google ranks every request slightly differently, so consecutive pages repeat a few properties;
they are skipped and never charged, so a page adds 12-19 new properties. The actor stops at **Max properties**, at
**Max pages per destination** or when two pages in a row add almost nothing.

### Filters and sorting

Hotel class, guest rating, amenities, free cancellation and the sort order (lowest price, highest rating, most
reviewed) are sent to Google exactly like its own filter chips. The price range is applied the way Google applies a
price typed into the search ("Paris hotels under 150 EUR") and every result is checked again, so nothing outside
your range is output. Hotel class and rating are checked again too.

### Monitor changes on a schedule

Turn on **Only new or changed items** and save the input as a task with a Schedule (e.g. daily). Each run then
outputs only properties that are new to this search or whose lowest price per night changed; unchanged properties
are not output and not charged. New items carry `changeType: "new"`, changed ones `changeType: "changed"` and
`previousValues` with the old price (`{"pricePerNight": 71}`). Use a relative check-in such as `+30 days` to watch a
rolling date, or a fixed date to watch one stay. Give each watch its own **Monitoring key** if several schedules
search the same destination.

### Does it need a login, a Google API key or a special proxy?

No. It reads the public Google Hotels pages through Apify's standard datacenter proxy: no login, no API key, no
paid SERP or residential proxy. A page of 20 properties is one ~250 KB request, so platform usage is small.

### Pricing

Pay per result: **$0.003 per property** and, only with **Include all booking offers**, **$0.0015 per hotel page**
that returned offers. Platform usage (compute, datacenter proxy) is billed by Apify and is about $0.02 per 1,000
properties (list only) or ~$0.1 per 1,000 with hotel pages.

### Limitations

- Google's hotel pages carry **no booking offers for stays with children**; with children, the list price (for your
  family) is still there, `offers` stays empty and the hotel-page event is not charged.
- Fewer booking sites appear for some countries (e.g. 5 for a Paris hotel priced from the UK, 24 from the US).
- Amenity names are English; other texts follow the language you choose.
- Google shows Google reviews' scores and topics, not the review texts themselves; `reviewSources` carry one
  snippet per external site, as Google shows them.

### Is it legal to scrape Google Hotels?

The actor collects publicly visible prices and hotel facts, without logging in, at a modest request rate. Hotel
names, addresses and prices are business information. Review snippets come with the public display names that
Google shows. Check the terms that apply to your use of the data, especially for republishing.

# Actor input Schema

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

One Google Hotels search per line, as you would type it on Google Hotels: a city ("Paris"), an area or landmark ("hotels near Times Square"), a region ("Bali"). Each search returns up to ~20 properties per page.

## `startUrls` (type: `array`):

Optional. Google Hotels search URLs (google.com/travel/search?q=...\&ts=...; the dates, guests and filters set in the URL are used) or hotel URLs (google.com/travel/hotels/entity/...; uses the dates below).

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

Check-in date: YYYY-MM-DD, or relative like "+30 days" or "in 2 weeks" (keeps a scheduled run on a rolling date). Empty = 30 days from today.

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

Check-out date: YYYY-MM-DD or relative to today ("+32 days"). Empty = check-in plus 'Nights'. Google Hotels allows stays of up to 30 nights.

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

Length of stay when Check-out is empty.

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

Number of adult guests (prices are for this party).

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

One line per child with the child's age (0-17), e.g. 5 and 9 for two children.

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

ISO currency code for all prices, e.g. USD, EUR, GBP, INR, JPY.

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

Two-letter country code of the visitor Google prices for (gl), e.g. us, gb, de, in. Affects which booking sites appear and whether headline prices include taxes.

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

Language of Google's texts (hl), e.g. en, de, fr, es. Amenity names of hotels are always English.

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

In the chosen currency. Applied by Google (as a price phrase in the search) and checked again on every result.

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

In the chosen currency. Applied by Google (as a price phrase in the search) and checked again on every result.

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

Only these star classes (Google's hotel class filter).

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

Only properties rated at least this on Google (1-5). Empty = any rating.

## `amenities` (type: `array`):

Only properties with all of these amenities (Google's amenity filter).

## `freeCancellation` (type: `boolean`):

Only properties with a free-cancellation rate (Google's filter).

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

Google Hotels' own sort order.

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

Google mixes about 2 vacation rentals (apartments, houses) into every page of hotels. Off = hotels, hostels, B\&Bs and other lodging only.

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

Also open each property's Google page: every booking site's price per night and total (with and without taxes), free cancellation, official-site flag and booking link, plus address, phone, third-party ratings with a review snippet. Charged as an extra event per property.

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

Stop after this many properties in total. Each results page has 18-20 properties.

## `maxPagesPerSearch` (type: `integer`):

Each page holds 18-20 properties; Google ranks each request slightly differently, so later pages repeat a few properties (they are skipped). Paging works to page 30 and beyond.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this search returned and output only new items or items whose price/status changed. Use with a Schedule; unchanged items are not charged. For Google Hotels: new properties and properties whose lowest nightly price changed; Max properties is then the number of properties checked per run.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input

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

Google Hotels loads through Apify's standard datacenter proxy (no paid SERP or residential proxy needed). The GOOGLE_SERP group does not serve Google Hotels and is replaced by datacenter.

## Actor input object example

```json
{
  "queries": [
    "Paris"
  ],
  "checkIn": "+30 days",
  "nights": 1,
  "adults": 2,
  "currency": "USD",
  "country": "us",
  "language": "en",
  "freeCancellation": false,
  "sortBy": "relevance",
  "includeVacationRentals": false,
  "includeDetails": false,
  "maxItems": 50,
  "maxPagesPerSearch": 10,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "queries": [
        "Paris"
    ],
    "checkIn": "+30 days",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

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

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

```

## MCP server setup

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