# Google Hotels Rate Monitor - Price History & Competitors (`surefeed/hotel-rate-monitor`) Actor

Compare Google Hotels prices across dates and destinations. Track observed price changes, competitor medians and sample rankings. Export CSV/JSON and retain your own history. Use existing SureFeed datasets or collect fresh prices. Room conditions are not matched.

- **URL**: https://apify.com/surefeed/hotel-rate-monitor.md
- **Developed by:** [gianluca de dominicis](https://apify.com/surefeed) (community)
- **Security:** Full permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 hotel-date analyses

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

## SureFeed Hotel Rate Monitor

Compare observed Google Hotels prices for your chosen stays, see how a hotel compares with a selected competitor sample, and keep your own price history. Export the analysis as CSV, JSON or Excel.

**Choose your starting point:** collect fresh prices with the small live example below, or reuse your existing SureFeed datasets to avoid a new collection charge. The output shows observed prices, available comparisons and missing-data flags. [See the first-run cost example](#first-run-cost-example).

**Initial release.** This is an analysis tool built on `surefeed/google-hotels-scraper`. It does not estimate occupancy, bookings, demand or revenue. Prices do not imply comparable room types, meals or cancellation terms.

### Quick start: live prices

Copy this input into the Actor's JSON input and click **Start**. It requests up to 5 hotels in Rome for 2 adults, in EUR, for a two-night stay starting 14 days from today (UTC). Empty `searches` selects these moving dates, so there are no dates to edit for a first test. Live collection and monitor analysis are billed separately.

```json
{
  "mode": "live",
  "datasetIds": [],
  "searches": [],
  "currency": "EUR",
  "country": "IT",
  "adults": 2,
  "maxHotels": 5,
  "sourceBudgetUsd": 0.05,
  "minPeers": 3
}
```

Before starting, add a `historyName` if you want to preserve this observation; reuse the same name in later runs. After the run, inspect the output dataset and export the results before increasing the sample.

**For repeat price monitoring, use fixed stay dates.** Set explicit `query`, `checkIn` and `checkOut` values in `searches`, then repeat the same search with the same guest count, currency and search country. Keep the same `historyName`. The moving default dates change each day and therefore do not track the same stay over time. A first observation establishes a baseline; a later compatible observation is needed to measure a price change.

#### First-run cost example

Live mode collects basic hotel results with optional hotel details disabled. At the published base rates, the example below applies **if the run returns 5 charged source hotel results and produces 5 analysis rows**, with the monitor running at 512 MB:

| Component | Calculation | Cost |
| --- | --- | --- |
| Google Hotels source collection | 5 hotel results × $0.003 | $0.015 |
| Monitor analysis | 5 analysis rows × $0.001 | $0.005 |
| Monitor start | 1 start × $0.00005 | $0.00005 |
| **Combined example total** | | **$0.02005 — about 2 US cents** |

This is a worked example, not a fixed fee or a guarantee of 5 results. A two-night stay does not double the analysis count: each row describes a hotel observation for the whole stay. Source results and analysis rows are separate billing units; use the actual charged counts. Missing-price observations also count as analysis rows. Rates are in USD, before plan discounts; the requested hotel-price currency (`EUR` here) does not change the billing currency. Platform usage is included under the currently published pricing of both Actors. Check both Pricing tabs for your plan's current rates.

With `mode: "datasets"`, there is no new source collection: analyzing 5 existing observations costs **$0.00505 for the monitor** under the same assumptions. Earlier collection charges are separate, and reanalyzing the same data is a new charged analysis.

The `sourceBudgetUsd: 0.05` in the input is a source-event charge cap, not the expected charge and not an all-in spending limit. Set the monitor's maximum run charge separately. [See full pricing and limits](#pricing).

### Start with existing data

Set `mode` to `datasets` and supply your own source dataset IDs in `datasetIds`. The source package also contains `examples/input-datasets.json`. The included example refers to 20 observations in Rome collected on 29 October 2026 for 20–22 November 2026, 2 adults, EUR, Italy. They are historical observations, not current offers. The dataset ID is an account-specific example; other accounts should use their own source datasets.

`datasetIds` accepts up to 10 datasets produced by the SureFeed Google Hotels scraper. Records must include `entityId`, `name`, `query`, dates, adults, currency, country, type and observation timestamp. Invalid records or conflicting duplicate prices stop the analysis instead of biasing results. Up to 5000 rows per source dataset and 10000 total per run.

### Collect fresh prices

The Store example starts in live mode. Leave `searches` empty for a two-night Rome stay starting 14 days from today (UTC). For your own destinations, switch `mode` to `live`, clear `datasetIds`, and set `searches`: each object has `query`, `checkIn`, `checkOut`. Up to 10 searches and 100 hotels per search. `examples/input-live.json` compares two weekends. Update past dates before running.

Live mode starts the existing SureFeed Google Hotels Actor once per search, with a 300-second timeout per source run. The combined `sourceBudgetUsd` cap is divided evenly across searches. This is a source Actor event-charge cap, not a guarantee on total platform/compute/storage/proxy costs. Monitor platform usage is included in the monitor price shown below; the source Actor has separate billing. A source failure or wrong returned date/currency stops the analysis. Source run IDs remain in `SOURCE_RUNS`. A forcibly terminated parent can leave a source running until its own 300-second timeout; no automatic POST retries are used.

### Comparisons

- `price`: total stay price divided by nights (preferred), or the nightly headline if no total is present. Rounded to 2 decimals. No currency conversion.
- Groups must match query, check-in/out, adults, currency, search country, tax basis, property type and observation timestamp. Price basis must also match. Unknown taxes suppress comparisons.
- `competitorIds`: exact `entityId` values. Empty means all compatible observed peers. The current hotel and optional `targetHotelId` are excluded from its peer benchmark.
- `peerMedianPrice`, `vsPeerMedianPct`, `sampleRank` require at least `minPeers` priced peers (default 3). Tied prices share the same rank. Rank refers only to the observed sample.
- `priceChange`, `changePct` compare the same hotel and stay context to its immediately previous observation. A missing previous price does not produce a fabricated change. Different dates or occupancy never share a time series.
- Missing price means `price_not_observed`, not sold out. Missing hotels are not inferred to be unavailable. Search samples may omit target hotels or competitors.
- `roomConditionsVerified` is always false: headline offers can differ in room, meal and cancellation terms.

### History and exports

Supply `historyName` to append source observations to an account-specific named dataset. The same account and monitor name reuse that archive. The last 5000 appended rows are loaded for comparisons; `historyWindowTruncated` explicitly identifies older data outside the comparison window. The full archive remains available as a dataset. Existing observations are not appended again within the loaded window. Avoid overlapping runs for one monitor: concurrent runs can append duplicate observations, but subsequent analysis deduplicates them.

The default output dataset contains flat analysis records, exportable through Apify as CSV, JSON or Excel. `SUMMARY` in the default key-value store contains counts, history dataset ID and source dataset/run references. No email, messaging or recurring schedule is activated by this Actor. A first run establishes a baseline; real changes require subsequent observations for the same stays.

### Local development

Node.js 22 or later. No third-party npm packages or installation step required.

```sh
npm test
npm run demo
```

Demo writes `output/demo-analysis.json` using the included observed data without network calls or charges. Tests use synthetic fixtures for change scenarios and mock API responses for orchestration.

Deploy the source ZIP to a new Apify Actor (ZIP source or import into its source editor), then Build with a fresh base image. Docker uses `apify/actor-node:22`; rebuild without cache when Apify publishes security updates. No API token is included in the code or examples. On Apify the runtime supplies `APIFY_TOKEN` and default storage IDs. The account token must permit source Actor runs and dataset storage; scoped tokens may require appropriate permissions.

Validated on Apify with 20 archived observations, an idempotent repeat, a 5-result live search and a 60-result multi-search run across Rome, Milan and two weekends. These are functional tests, not a guarantee of future availability or full market coverage.

### Technical references

- https://docs.apify.com/api/v2/actors-runs-post
- https://docs.apify.com/api/v2/dataset-items-get
- https://apify.com/surefeed/google-hotels-scraper

Developed for SureFeed. No promise of increased revenue or full market coverage.

### Permissions

Full permissions are required to read your source datasets, run the SureFeed source Actor and maintain your named history dataset. Do not enter your API token into the input; the Apify runtime supplies it. History is stored in your account. No messages or schedules are created.

### Pricing

Base monitor rate: $1 per 1000 analysis rows ($0.001 each), plus the automatic Actor start event ($0.00005 at up to 1 GB). Monitor platform usage is included. Check the Pricing tab for your plan’s current rate; the Store’s “from” price may reflect a plan discount. Missing-price observations are also analysis rows and count toward billing. In live mode, the SureFeed Google Hotels source Actor is billed separately under its current pricing; sourceBudgetUsd limits its event charges only. Example: 20 analyzed observations cost $0.02005 for this monitor at 512 MB, plus source collection if requested. Reanalyzing an existing dataset is a new charged analysis. History archival itself is not an additional monitor event. Set the Apify maximum run charge as well as the source budget; these are separate limits.

# Actor input Schema

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

Existing datasets avoid a new scraping charge. Live mode starts the SureFeed Google Hotels Actor.

## `datasetIds` (type: `array`):

Up to 10 datasets produced by SureFeed Google Hotels. Their dates, country and currency are preserved. Clear this field for live mode.

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

Optional in live mode: up to 10 query/checkIn/checkOut objects. Empty starts a 2-night Rome search 14 days from today (UTC).

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

Live collection only. Existing dataset currencies are not converted.

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

Live collection only.

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

Live collection only.

## `maxHotels` (type: `integer`):

Returned search sample; does not guarantee a particular hotel is included.

## `sourceBudgetUsd` (type: `number`):

Divided equally across live searches. Covers source Actor charged events; platform compute/storage/proxy usage can be additional.

## `historyName` (type: `string`):

Lowercase letters, digits and hyphens, max 40 characters. Same account + name appends to the same private history dataset. Leave blank to avoid persistent history. Last 5000 rows used for comparisons.

## `targetHotelId` (type: `string`):

Exact entityId from the source results. No fuzzy name matching.

## `competitorIds` (type: `array`):

If empty, use observed peers in the same search, date, guests, currency, country, tax basis and property type. A sample, not the entire market.

## `minPeers` (type: `integer`):

Suppress median and rank below this sample size.

## `changeThresholdPct` (type: `number`):

Flags in the output only. No email or Telegram messages are sent.

## Actor input object example

```json
{
  "mode": "live",
  "datasetIds": [],
  "searches": [],
  "currency": "EUR",
  "country": "IT",
  "adults": 2,
  "maxHotels": 20,
  "sourceBudgetUsd": 0.5,
  "historyName": "hotel-rate-history",
  "competitorIds": [],
  "minPeers": 3,
  "changeThresholdPct": 5
}
```

# Actor output Schema

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

Observed nightly prices, compatible peer benchmarks and changes for the same stay. Room conditions are not matched.

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

Counts, source run references, history archive and comparison-window warnings.

# 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": "live",
    "historyName": "hotel-rate-history"
};

// Run the Actor and wait for it to finish
const run = await client.actor("surefeed/hotel-rate-monitor").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": "live",
    "historyName": "hotel-rate-history",
}

# Run the Actor and wait for it to finish
run = client.actor("surefeed/hotel-rate-monitor").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": "live",
  "historyName": "hotel-rate-history"
}' |
apify call surefeed/hotel-rate-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,surefeed/hotel-rate-monitor"
        }
    }
}
```

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/H1uVXZyYKyckrGxPi/builds/YbZ3HtwAROKKSJ5kv/openapi.json
