# Google Hotels Search Scraper (`khadinakbar/google-hotels-search-scraper`) Actor

Scrape Google Hotels search results — hotel name, rating, reviews count, price, check-in/out dates, URL, address. Search by query+location+dates or direct URL. MCP-ready.

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

## Pricing

from $3.00 / 1,000 hotel results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Hotels Search Scraper

Extract hotel listings from [Google Hotels](https://www.google.com/travel/hotels) — names, star ratings, review counts, nightly prices, check-in/check-out dates, property URLs, addresses, amenities, and thumbnail images. Search by keyword + location + dates, or paste a direct Google Hotels URL. Cookieless, no login required. MCP-ready.

### Best fit for this actor

Use this actor when you need structured hotel data from Google Hotels: price monitoring, destination research, competitor rating analysis, travel inventory pipelines, or AI-agent hotel lookups.

For reviews and sentiment analysis of specific hotels, pair this actor with [tripadvisor-scraper](https://apify.com/khadinakbar/tripadvisor-scraper) — run this actor first to discover hotels, then feed the property names to TripAdvisor for review data.

### Workflow story

A travel startup monitors hotel prices for 10 destination cities weekly. They run this actor once per city (query: `hotels`, location: `Barcelona`, checkIn/Out for the target weekend, maxResults: 40). The output dataset is piped via Make into Google Sheets, where a formula flags properties with price changes above 15% from the prior week. The whole flow runs in under 3 minutes per city; see the Pricing tab for current event and compute rates.

### Why use this actor?

Google Hotels aggregates live prices across dozens of OTAs and brand sites in one place. This actor gives you that data programmatically for price monitoring, competitor benchmarking, travel market research, and AI-agent pipelines — without a browser or Google account.

| Use case | What you get |
|---|---|
| Hotel price monitoring | Track nightly rates across dates and markets on a schedule |
| Competitor benchmarking | Compare star ratings and review volumes for properties in a destination |
| Travel data pipelines | Structured hotel inventory for BI tools, dashboards, or LLM pipelines |
| Lead generation | Find hotels by city for hospitality vendors or tour operators |
| Research | Price and rating datasets for academic or market-research work |

### What data does it return?

| Field | Type | Description |
|---|---|---|
| `hotelName` | string | Property name as listed on Google Hotels |
| `rating` | number | Average guest rating (e.g. 4.3) |
| `reviewsCount` | number | Total guest reviews count |
| `price` | string | Nightly price (numeric string, no commas) |
| `currency` | string | Currency symbol or code (e.g. `$`, `EUR`) |
| `checkIn` | string | Check-in date YYYY-MM-DD (from your input) |
| `checkOut` | string | Check-out date YYYY-MM-DD (from your input) |
| `hotelUrl` | string | Google Hotels property page URL |
| `sourceUrl` | string | Search page URL the row was extracted from |
| `location` | string | City or locality from structured data |
| `address` | string | Street address from JSON-LD when available |
| `amenities` | array | Up to 10 amenity labels from structured data |
| `image` | string | Thumbnail image URL |
| `scrapedAt` | ISO 8601 | Timestamp of extraction |

### How to use

1. Open the **Input** tab in Apify Console.
2. Fill in **Search query** (e.g. `boutique hotels`) and **Location** (e.g. `London`) — or paste a full **Google Hotels URL** to override both.
3. Set optional **Check-in** and **Check-out** dates (YYYY-MM-DD) and **Adults** count to get nightly price data.
4. Set **Max results** — each saved row triggers one `hotel-result` Pay per event charge.
5. Click **Start**. Download results from the Dataset tab in JSON, CSV, Excel, or XML.

### Input example

```json
{
    "query": "hotels",
    "location": "New York",
    "checkIn": "2026-10-01",
    "checkOut": "2026-10-03",
    "adults": 2,
    "maxResults": 20,
    "maxPages": 2,
    "language": "en",
    "countryCode": "us",
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
}
```

Or supply a direct URL and omit `query`/`location`:

```json
{
    "googleHotelsUrl": "https://www.google.com/travel/hotels?hl=en&gl=us&q=hotels+in+paris&checkin=2026-12-20&checkout=2026-12-23&adults=2"
}
```

### Output example

```json
[
    {
        "hotelName": "The Plaza Hotel",
        "rating": 4.5,
        "reviewsCount": 8423,
        "price": "795",
        "currency": "$",
        "checkIn": "2026-10-01",
        "checkOut": "2026-10-03",
        "hotelUrl": "https://www.google.com/travel/hotels/entity/...",
        "sourceUrl": "https://www.google.com/travel/hotels?q=hotels+New+York&...",
        "location": "New York",
        "address": "768 5th Ave, New York",
        "amenities": ["Free WiFi", "Pool", "Spa"],
        "image": "https://lh3.googleusercontent.com/...",
        "scrapedAt": "2026-10-01T09:23:14.000Z"
    }
]
```

### API example

Run via curl (replace `YOUR_API_TOKEN`):

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/khadinakbar~google-hotels-search-scraper/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "boutique hotels",
    "location": "Paris",
    "checkIn": "2026-12-20",
    "checkOut": "2026-12-23",
    "adults": 2,
    "maxResults": 20,
    "useApifyProxy": true
  }'
```

Or via the JavaScript client:

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('khadinakbar/google-hotels-search-scraper').call({
    query: 'boutique hotels',
    location: 'Paris',
    checkIn: '2026-12-20',
    checkOut: '2026-12-23',
    adults: 2,
    maxResults: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### AI agent / MCP prompt card

When calling this actor as a tool in Claude, ChatGPT, or another AI agent:

> **Tool:** `khadinakbar/google-hotels-search-scraper`
> **Goal:** Get hotel listings with prices for a destination and dates
> **Input:** `{ "query": "luxury hotels", "location": "Tokyo", "checkIn": "YYYY-MM-DD", "checkOut": "YYYY-MM-DD", "adults": 2, "maxResults": 30 }`
> **Returns:** Array of hotel rows — name, rating, reviewsCount, price, currency, hotelUrl, location, scrapedAt
> **Billing:** Pay per event — one `hotel-result` event per row + platform compute (see Pricing tab for current rates)
> **Dataset readback:** Use `client.dataset(run.defaultDatasetId).listItems()` after the actor finishes; allow 5-10 seconds for data to be fully available
> **Empty result:** `VALID_EMPTY` — Google served no structured hotel data for this query/date combination; try a broader query or different dates

### Pricing

This actor uses **Pay per event** + Apify platform usage compute (billed at standard rates). The **Pricing tab** on the actor page is always the live source of truth for current event prices and compute rates.

| Event | When charged |
|---|---|
| `apify-actor-start` | Once per run |
| `hotel-result` | Once per saved hotel row |

You can set a `maxTotalChargeUsd` cap in the API call options to bound total spend per run.

### Builder's Note

I built this actor around a discovery from testing Google Hotels directly: search result pages served to crawlers embed `@type: Hotel` JSON-LD structured data, which is the richest extraction path (hotel name, rating, review count, full address, and amenity list all arrive in one block). In my testing across 22 cloud runs on 20+ destinations, the JSON-LD path fired on the majority of responses, and the DOM fallback (`[role="heading"]` inside card containers + aria-label price/rating regex) caught the rest — New York runs filled price and rating on 18 of 18 rows, and even Japanese- and Spanish-language runs parsed prices correctly because the currency-symbol regex is language-independent. I learned two things the hard way: Google silently redirects the initial `google.com/travel/hotels` request to `google.com/travel/search` with hotel-mode parameters intact (the crawler follows it transparently), and deep-pagination links are embedded in JS rather than plain anchors, so large-capped runs in a single city often surface the first page's inventory — set a higher `maxPages` and re-run with varied queries for full coverage. Both extraction paths write to the same output schema, so downstream pipelines stay simple regardless of which path produced a given row.

### Extraction approach

The actor uses JSON-LD structured data (`@type: Hotel`) as the primary extraction path — richer, with name, rating, address, and amenities. When JSON-LD is absent, it falls back to DOM card parsing for name, rating, price, and image. Both paths write to the same output schema.

### Proxy requirements

Google Hotels requires residential proxies for reliable access. Default group: `RESIDENTIAL`. Keep `useApifyProxy: true` in production; set `false` only for local testing without a proxy subscription.

### Tips for best results

- **Specific queries return sharper results.** `boutique hotel Marrakech` works better than just `hotels`.
- **Add dates for price data.** Without checkIn/checkOut, Google may omit nightly prices.
- **Raise `maxPages`** (up to 20) to capture deep destination inventories.
- **Set low `maxResults`** (5-10) for quick scheduled price-check scripts.

### Scope and how it handles edge cases

- **HTTP-only, no browser required.** Fast and lightweight. For most Google Hotels queries, the actor retrieves server-rendered HTML directly. If Google returns a JavaScript-only page for a particular query, the run reports `VALID_EMPTY` — try RESIDENTIAL proxy or broader search terms.
- **Price data is best-effort.** When structured data is absent, DOM regex may miss some prices.
- **Amenities come from JSON-LD only.** The DOM fallback does not extract amenities.
- **EU and UK markets route through US proxy exits.** Google applies a consent wall to EU exit IPs; US exits combined with market-specific URL parameters keep result lists localized to the selected market, though nightly prices for EU markets may quote in USD.
- **RESIDENTIAL proxy delivers consistent access.** Residential IPs succeed reliably against Google; datacenter IPs see far lower success rates.

### Related actors

For a full travel data workflow, combine this actor with:

- [tripadvisor-scraper](https://apify.com/khadinakbar/tripadvisor-scraper) — Hotel and restaurant reviews from TripAdvisor for sentiment analysis on properties found here
- [airbnb-scraper](https://apify.com/khadinakbar/airbnb-scraper) — Short-term rental listings for price comparison alongside hotels
- [google-maps-leads-scraper](https://apify.com/khadinakbar/google-maps-leads-scraper) — B2B leads from nearby businesses when researching hotel destinations

### Legal disclaimer

This actor extracts publicly visible data from google.com/travel/hotels in the same way a user would browse the site. It does not bypass authentication, login walls, or paywalls, and does not access private or personally identifiable data. Use is subject to [Google's Terms of Service](https://policies.google.com/terms). The actor author and Apify bear no responsibility for how extracted data is used. Always verify compliance with applicable laws and regulations before production use.

# Actor input Schema

## `query` (type: `string`):

Hotel search phrase, for example 'luxury hotels' or 'hotels near central park'. Combine with location for best results. Leave blank if providing a direct googleHotelsUrl.

## `location` (type: `string`):

City, region, or landmark to search in, for example 'New York', 'Paris', or 'Times Square'. Combined with query to form the Google Hotels search.

## `googleHotelsUrl` (type: `string`):

Paste a full google.com/travel/hotels URL to override the query/location fields. Useful for replaying a specific search with filters already set.

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

Arrival date in YYYY-MM-DD format, for example 2026-10-01. Controls which nightly prices Google Hotels shows. Leave blank for anytime search.

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

Departure date in YYYY-MM-DD format, for example 2026-10-03. Must be after checkIn. Leave blank for anytime search.

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

Number of adult guests. Affects price and availability shown on Google Hotels. Default 2.

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

Number of child guests. Default 0.

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

Google search language code, for example 'en' for English or 'fr' for French. Controls the language of hotel names and review labels returned.

## `countryCode` (type: `string`):

Two-letter ISO country code for the Google Hotels market, for example 'us', 'gb', or 'de'. Affects currency, prices, and property availability shown. EU and UK markets route through US proxy exits for reliable access (Google consent wall); result lists stay localized to the selected market while nightly prices may quote in USD.

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

Maximum number of hotel rows to save per run. Default 60. Each saved row triggers one hotel-result PPE event. Raise for deeper coverage; lower for quick price checks.

## `maxPages` (type: `integer`):

Maximum number of Google Hotels result pages to process. Default 3. Each page typically surfaces 20-30 hotels. Raise for comprehensive city coverage.

## `useApifyProxy` (type: `boolean`):

Route requests through Apify proxy for anti-bot resilience. Recommended on. Disable only for local testing without a proxy subscription.

## `apifyProxyGroups` (type: `array`):

Apify proxy group(s) to use. Default \['RESIDENTIAL']. Use RESIDENTIAL for best Google compatibility.

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

Maximum parallel requests. Default 3. Lower if you see HTTP 429 blocks; raise for faster pagination.

## Actor input object example

```json
{
  "query": "hotels",
  "location": "New York",
  "checkIn": "2026-10-01",
  "checkOut": "2026-10-03",
  "adults": 2,
  "children": 0,
  "language": "en",
  "countryCode": "us",
  "maxResults": 20,
  "maxPages": 2,
  "useApifyProxy": true,
  "apifyProxyGroups": [
    "RESIDENTIAL"
  ],
  "maxConcurrency": 2
}
```

# Actor output Schema

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

All hotel records in the run's default dataset: hotelName, rating, reviewsCount, price, currency, checkIn, checkOut, location, address, amenities, hotelUrl, image, scrapedAt. One record per hotel listing card.

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

Detailed terminal diagnostics from the RUN\_SUMMARY record: outcome, itemsPushed, pagesProcessed, upstreamErrors, chargedEventCounts, warnings.

## `output` (type: `string`):

Stable machine-readable terminal outcome (COMPLETE | PARTIAL | VALID\_EMPTY | INVALID\_INPUT | UPSTREAM\_FAILED | CONFIG\_ERROR) and delivery counters from the OUTPUT record.

# 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 = {
    "query": "hotels",
    "location": "New York",
    "checkIn": "2026-10-01",
    "checkOut": "2026-10-03",
    "adults": 2,
    "children": 0,
    "language": "en",
    "countryCode": "us",
    "maxResults": 20,
    "maxPages": 2,
    "useApifyProxy": true,
    "apifyProxyGroups": [
        "RESIDENTIAL"
    ],
    "maxConcurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/google-hotels-search-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 = {
    "query": "hotels",
    "location": "New York",
    "checkIn": "2026-10-01",
    "checkOut": "2026-10-03",
    "adults": 2,
    "children": 0,
    "language": "en",
    "countryCode": "us",
    "maxResults": 20,
    "maxPages": 2,
    "useApifyProxy": True,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "maxConcurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/google-hotels-search-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 '{
  "query": "hotels",
  "location": "New York",
  "checkIn": "2026-10-01",
  "checkOut": "2026-10-03",
  "adults": 2,
  "children": 0,
  "language": "en",
  "countryCode": "us",
  "maxResults": 20,
  "maxPages": 2,
  "useApifyProxy": true,
  "apifyProxyGroups": [
    "RESIDENTIAL"
  ],
  "maxConcurrency": 2
}' |
apify call khadinakbar/google-hotels-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/google-hotels-search-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/xkqtMO7H11H36RPPp/builds/CZqRo1Ga3QDEdgJrO/openapi.json
