# Google Hotels Prices Scraper - Date-Aware Nightly Rates (`diopside/google-hotels-prices`) Actor

Scrape Google Hotels listings with real per-date nightly and total prices, ratings, review counts, coordinates and amenities. Uses Google's own pricing endpoint, so prices match the check-in/check-out window you ask for.

- **URL**: https://apify.com/diopside/google-hotels-prices.md
- **Developed by:** [DIOPSIDE AI](https://apify.com/diopside) (community)
- **Categories:** Travel, E-commerce, Agents
- **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 records

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 Prices Scraper

Scrape Google Hotels listings with **real per-date prices** — the nightly and
total rate for the exact check-in/check-out window you ask for, not a generic
"from" price.

### Why this one

Most Google Hotels scrapers read the server-rendered search page. That page
looks right, but its price cards are **date-independent**: Google always
renders the default one-night rate there, no matter which dates you pass. A
scraper built on it will happily accept `checkInDate` / `checkOutDate` and
return prices that have nothing to do with them.

This actor instead calls the same internal pricing endpoint the Google Hotels
web app calls, with your dates, currency and occupancy in the request. Every
record carries the `nights` value Google echoed back, so you can verify the
stay window was actually applied.

Measured on a live Paris search: **153 of 154** hotels returned a different
price for `2026-12-24 → 2026-12-31` than for `2026-10-15 → 2026-10-17`.

#### vs other Google Hotels scrapers

The two most-used alternatives, `google-hotels-search-scraper` (64 monthly users) and
`google-travel-hotel-prices` (19 monthly users), both report 0% failed runs — this is a
healthy niche, not a broken-incumbent one. The gap is the date bug above: read the server-rendered
page and you get *a* price, not necessarily *your* price. Every record here carries the `nights`
value Google echoed back so you can verify the window was actually applied.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `locations` | array | `["hotels in Paris"]` | City, area, landmark or hotel name. One search per entry. |
| `checkInDate` | string | — | `YYYY-MM-DD`. Required. |
| `checkOutDate` | string | — | `YYYY-MM-DD`. Required, after check-in. |
| `adults` | integer | `2` | Adults per room. |
| `children` | integer | `0` | Children. |
| `currency` | string | `USD` | ISO code, e.g. `USD`, `EUR`, `GBP`. |
| `languageCode` | string | `en` | Google UI language. |
| `countryCode` | string | `us` | Google market. |
| `maxResults` | integer | `20` | Max records per location (up to 300). |
| `requestTimeoutSecs` | integer | `30` | Per-request timeout. |
| `proxyConfiguration` | object | Apify Proxy on | See *Proxy* below. |

The input field names match the most widely used Google Hotels actor, so you
can switch without changing your code.

```json
{
  "locations": ["hotels in Paris"],
  "checkInDate": "2026-12-24",
  "checkOutDate": "2026-12-31",
  "adults": 2,
  "currency": "USD",
  "maxResults": 20
}
```

### Output

One record per hotel:

```json
{
  "name": "MEININGER Hotel Paris Porte de Vincennes",
  "entity_id": "ChoIuamlo-iVpL_OARoNL2cvMTFoNHRydHdmYxAB",
  "url": "https://www.google.com/travel/hotels/entity/ChoIuamlo-...",
  "property_type": "hotel",
  "hotel_class": 2,
  "review_score": 4.3,
  "review_count": 5917,
  "price_per_night": 53.12,
  "price_per_night_display": "$53",
  "price_per_night_with_taxes": 66.0,
  "total_price": 372.0,
  "total_price_with_taxes": 461.0,
  "currency": "USD",
  "currency_symbol": "$",
  "latitude": 48.8438355,
  "longitude": 2.4131508,
  "amenities": ["Breakfast ($)", "Free Wi-Fi", "Pet-friendly"],
  "check_in_date": "2026-12-24",
  "check_out_date": "2026-12-31",
  "nights": 7,
  "adults": 2,
  "children": 0,
  "search_query": "hotels in Paris",
  "scraped_at": "2026-09-18T10:43:14.606338+00:00"
}
```

`property_type` is `hotel` or `vacation_rental` (Google mixes apartments and
serviced flats into hotel results; this field lets you filter them out).

#### Field coverage

On a typical search of ~300 results:

- `name`, `url`, coordinates, dates — **100%**
- `review_score` / `review_count` — **~99%**
- prices — **~99%** (a hotel with no availability for your dates has `null`
  prices; the record is still returned rather than silently dropped)
- `amenities`, `hotel_class` — **partial**. Google only server-renders these
  for the handful of hotels on the first page, so most records have
  `amenities: []` and `hotel_class: null`. They are never guessed.

Not included in this version: per-OTA provider offers (Booking.com,
Expedia, …) and street address. Google serves those from a separate per-hotel
endpoint that costs one extra request per hotel.

### Proxy

Defaults to Apify Proxy (datacenter), which is the configuration we recommend.

Google soft-blocks some requests by returning a well-formed but **empty**
result rather than an error. The actor detects that, and retries on a fresh
exit IP (up to 3 attempts per location) instead of reporting an empty scrape.
In platform testing a datacenter request that came back empty succeeded on
retry, so these retries are what keep the success rate high.

`RESIDENTIAL` is **not** recommended here: those exit IPs are frequently
European and Google answers them with a cookie-consent interstitial instead of
hotel data. The actor sends consent cookies to mitigate this, but datacenter
remains the more reliable route for this particular target.

Running with no proxy at all also works for occasional single searches —
verified on the platform: the same Paris search returned the same 20 priced
records with `{"useApifyProxy": false}`. Proxy is on by default because it is
what makes the retry-on-a-fresh-IP path work when Google does start
soft-blocking; it is not needed to get a first result.

**Currency note:** Google localises prices to the exit IP. The `currency`
field is passed explicitly in the request so the returned prices are in the
currency you asked for, and `currency_symbol` records the symbol Google
actually rendered — if those ever disagree, trust `currency_symbol`.

### Run sizes and cost

Pay per result: `$0.003` per `hotel-record`, plus `$0.00005` per GB of actor
start. You are not charged for hotels that fail to parse, and the run stops as
soon as your charge limit is reached.

| Run | Records | Time | Cost |
|---|---|---|---|
| 1 city, `maxResults: 20` | 20 | ~8 s | $0.060 |
| 1 city, `maxResults: 100` | 100 | ~10 s | $0.300 |
| 10 cities, `maxResults: 50` | 500 | ~90 s | $1.500 |

One search request covers a whole location, so time scales with the number of
`locations` entries, not with `maxResults`.

### Use cases

- **Rate-parity and competitor monitoring** — re-run a fixed set of dates daily
  and diff `price_per_night` per `entity_id`.
- **Revenue management** — pull a forward calendar for your comp set by running
  one date pair per night you care about.
- **Travel content and deal sites** — rank a city's hotels by price for a
  specific weekend, with `review_score` and `review_count` to sort on.
- **Demand/price research** — `total_price_with_taxes` over holiday vs. shoulder
  windows, which is only meaningful because the prices really are date-specific.

### Reliability

- No headless browser — plain HTTP, so there is no browser crash surface.
- Session tokens are harvested fresh on every run, never hard-coded, so they
  cannot go stale between deploys.
- Up to 3 attempts per location with a new session each time.
- One failing location does not abort the run; the rest still produce data and
  the run status message names what failed.
- An empty result from Google is treated as a block and retried, never reported
  as a successful zero-hotel scrape.
- Missing upstream values become `null`; the record is still emitted.

# Actor input Schema

## `locations` (type: `array`):

Cities, areas, landmarks or hotel names to search, e.g. "hotels in Paris" or "Hilton Midtown New York". One search per entry.

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

Check-in date in YYYY-MM-DD format. Prices returned are for this exact stay window.

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

Check-out date in YYYY-MM-DD format. Must be after the check-in date.

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

Number of adults per room.

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

Number of children.

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

ISO currency code for the returned prices, e.g. USD, EUR, GBP.

## `languageCode` (type: `string`):

Google interface language, e.g. en, fr, de.

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

Google country/market code, e.g. us, gb, de.

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

Maximum hotel records to return for each location.

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

Per-request timeout.

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

Apify Proxy settings. Datacenter (the default) is recommended: the actor retries soft-blocked requests on a fresh IP. RESIDENTIAL often hits Google's cookie-consent wall and is not advised for this target.

## Actor input object example

```json
{
  "locations": [
    "hotels in Paris"
  ],
  "checkInDate": "2026-10-15",
  "checkOutDate": "2026-10-17",
  "adults": 2,
  "children": 0,
  "currency": "USD",
  "languageCode": "en",
  "countryCode": "us",
  "maxResults": 20,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All hotel records. Append ?format=csv for CSV.

## `datasetUrl` (type: `string`):

The default dataset.

# 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 = {
    "locations": [
        "hotels in Paris"
    ],
    "checkInDate": "2026-10-15",
    "checkOutDate": "2026-10-17"
};

// Run the Actor and wait for it to finish
const run = await client.actor("diopside/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 = {
    "locations": ["hotels in Paris"],
    "checkInDate": "2026-10-15",
    "checkOutDate": "2026-10-17",
}

# Run the Actor and wait for it to finish
run = client.actor("diopside/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 '{
  "locations": [
    "hotels in Paris"
  ],
  "checkInDate": "2026-10-15",
  "checkOutDate": "2026-10-17"
}' |
apify call diopside/google-hotels-prices --silent --output-dataset

```

## MCP server setup

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