# Hotel Reviews Scraper: Tripadvisor, Booking.com, Google (`arcadium/hotel-reputation`) Actor

Hotel reviews from Tripadvisor, Booking.com, Google Hotels, Agoda and Trip.com in one dataset, ratings on one 1-5 scale; $4.50 per 1,000 reviews. Free per-hotel summary row comparing every site with the city. Only-new mode, free error rows, no reviewer names.

- **URL**: https://apify.com/arcadium/hotel-reputation.md
- **Developed by:** [Arcadium Data](https://apify.com/arcadium) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 reviews

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

## Hotel Reputation: Tripadvisor and Booking.com Reviews

Give it a hotel name, or its Tripadvisor, Booking.com, Google Hotels, Agoda and Trip.com links, and get what guests say on every site in one dataset (a name alone is looked up on Tripadvisor, and on Google Hotels when `searchGoogleByName` is on; Booking, Agoda and Trip.com need their link or id): one row per review, plus a free summary row per hotel with each site's rating, review counts and category scores compared with the city. You only pay for the reviews you get back.

It is for hotel groups benchmarking against competitors, revenue and reputation teams, and travel analysts.

### Use with AI agents

Returns Tripadvisor, Booking.com, Google Hotels, Agoda and Trip.com reviews of the same hotel in one dataset with ratings on a common 1-5 scale. Agents can call it through the Apify MCP server (`https://mcp.apify.com`, open source as `apify/actors-mcp-server`) as `arcadium/hotel-reputation`: the input schema is read from the actor, the result is a dataset of JSON rows.

Input example (minimal):

```json
{
  "hotels": [
    {
      "name": "The Savoy London",
      "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html"
    }
  ],
  "maxReviewsPerSource": 20
}
```

What comes back: one `review` row per review (charged), a free `hotel-summary` row per hotel with both ratings, free `coverage` rows (stated total vs delivered) and a free `error` row for an input that could not be read. Filter on the `rowType` field. An input that fails is reported, not hidden (`includeErrorRows`, on by default): the error row carries `rowType: "error"`, a `code` (`invalid_input`, `not_found`, `blocked`, `unavailable`, `filter_unsupported` or `no_match`), a `message` and the `input` it is about. Retry on `blocked` and `unavailable` later; do not retry the other codes with the same input.

Cost of the call above: at most 40 reviews at $4.50 per 1,000 plus the $0.001 start fee is about $0.18 on the free plan; paid plans pay less. Free rows are never charged.

### What you get

| Field | What it holds |
| --- | --- |
| `platform` | `tripadvisor`, `booking`, `googlehotels`, `agoda` or `tripcom`. |
| `reviewId`, `date` | The site's own ID and the review date. |
| `title`, `text` | Tripadvisor text; Booking gives `pros` and `cons` as well, and `text` joins them. |
| `ratingOverall` | Normalized to 1 to 5. |
| `extras.rawRating`, `extras.rawScale` | The original rating and its scale (5 for Tripadvisor and Google, 10 for Booking, Agoda and Trip.com). Booking's raw score is also in `extras.score10`. |
| `extras.tripType` / `extras.travelerType` | Traveler type as each site names it. |
| `vendorResponse`, `language`, `helpfulVotes` | When the site provides them. |
| `country` | Booking, Agoda and Trip.com rows: the reviewer's country as a two-letter code. |

Output example (a Booking review, shortened):

```json
{
  "platform": "booking",
  "reviewId": "Booking___c6397d1e2a854aa3",
  "productName": "The Savoy",
  "date": "2026-09-30",
  "title": "Wonderful stay",
  "pros": "Staff and location.",
  "cons": "Breakfast queue.",
  "ratingOverall": 4.5,
  "language": "en",
  "country": "US",
  "extras": { "rawRating": 9, "rawScale": 10, "score10": 9, "travelerType": "COUPLES" }
}
```

#### Rating scales

Tripadvisor rates 1 to 5 and is kept as is. Booking scores 1 to 10, and `ratingOverall` is the score divided by two. The raw score always stays in `extras`. The star filter treats a Booking review as its score divided by two, rounded half up.

#### Summary row

`rowType: "hotel-summary"` per hotel (not charged):

- `tripadvisor`: rating 1 to 5, review count, reviews per star, how the hotel was matched.
- `booking`: `score10`, `rating` (score / 2), review count, stars, city, and `categoryScores` (staff, cleanliness and so on) each with the city range and `vsCity` (`above`, `within`, `below`).
- `combined`: both ratings weighted by review count, when both sites were read.

### Input

```json
{
  "hotels": [
    "Hyatt Grand Central New York",
    { "name": "The Savoy London", "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html" }
  ],
  "maxReviewsPerSource": 100
}
```

| Field | What it does |
| --- | --- |
| `hotels` | Names, or objects with `name`, `tripadvisorUrl`, `bookingUrl`, `bookingId`, `googleUrl`, `agodaUrl`, `agodaId`, `tripcomUrl`, `tripcomId` that tie one hotel's pages together. |
| `startUrls` | Tripadvisor, Booking, Agoda, Trip.com or Google Hotels links, routed by host. Each counts as its own hotel. |
| `searchGoogleByName` | Also look names up on Google Hotels and read Google's own reviews (off by default: one more source per hotel). |
| `bookingHotelIds` | Booking hotel ids (no hotel name or Booking rating in the output, so prefer links). |
| `ratings`, `languages` | Star and language filters on both sites. |
| `travelerTypes` | `BUSINESS`, `COUPLES`, `FAMILY`, `FRIENDS`, `SOLO`, mapped to each site's own names. Several types are read as separate passes: the date filter cannot stop early and only-new runs read every pass, so prefer one type for scheduled runs. |
| `dateFrom` | Only reviews from this date on. Reviews are always read newest first. |
| `onlyNewReviews`, `stateKey` | Only reviews not delivered by earlier runs with the same key. |
| `includeCoverageRows` | On by default. One free row per hotel and source with `"rowType": "coverage"`: the total the site states, how many were delivered, why the run stopped and whether anything is missing. |
| `markEdited` | With only-new runs, also deliver reviews seen before whose text changed, marked `extras.changeType: "edited"`. |
| `maxReviewsPerSource` | Cap per site and hotel. |
| `includeSummary` | The summary row (a few extra small requests per hotel). |

#### Names and Booking

Booking.com has no name search we can use. A hotel name is therefore looked up on Tripadvisor only. To read Booking for the same hotel, give its Booking link or id in the object form of `hotels`. Links in `startUrls` are read as separate hotels. A Tripadvisor name is accepted only when the hotel found has a matching name; otherwise you get a free `no_match` row.

#### Google Hotels

Give a Google Hotels link (`googleUrl` in the object form, or in `startUrls`), or set `searchGoogleByName` to look names up on Google too. Google's own reviews are read (newest first) with the same review shape (`platform: "googlehotels"`); the summary row gets a `google` block with the Google rating, review count, reviews per star and Google's topic breakdown. Google shows review dates as "3 months ago", so Google review dates are approximate (`extras.datePrecision`). Traveler types are filtered from the review's trip tags; the language filter does not apply to Google. For every Google option (partner-site reviews, topics, translations) use the Google Hotel Reviews actor.

#### Agoda and Trip.com

Give an Agoda or Trip.com link or id (`agodaUrl` / `agodaId`, `tripcomUrl` / `tripcomId` in the object form, or a link in `startUrls`). Agoda's own reviews (not the Booking.com ones Agoda also shows) and Trip.com's reviews (with Ctrip's) are read newest first in the same review shape (`platform: "agoda"` or `"tripcom"`, 1-10 score halved into `ratingOverall`, original text with the site's English translation in `extras.textTranslated`). The summary row gets an `agoda` block (Agoda score, rating count, stars, category scores against the city average) and a `tripcom` block (score, review count, sub-scores, positive and negative counts, advertised from-price). Rating, language and traveler-type filters are applied after download for both. For every option of these two sites use the Agoda and Trip.com Hotel Reviews actor.

### Use it from an AI assistant

```text
Use the Apify actor "hotel-reputation" to collect reviews.
Input: {"hotels":[{"name":"The Savoy London","bookingUrl":"https://www.booking.com/hotel/gb/the-savoy.html"}],"maxReviewsPerSource":100}
Rows with rowType "review" are reviews; "hotel-summary" has both ratings and Booking category scores.
Run it, then compare the two sites and name the categories where the hotel is below the city range.
```

### Use it from code

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("arcadium/hotel-reputation").call(run_input={"hotels": ["Hyatt Grand Central New York"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get("rowType"), item.get("platform"), item.get("ratingOverall"))
```

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('arcadium/hotel-reputation').call({ hotels: ['Hyatt Grand Central New York'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'rows');
```

### Pricing

You pay per review delivered, at the price of your Apify plan. Hotel summary rows, coverage rows, error rows and repeats in only-new mode are free.

| Apify plan | Per 1,000 reviews |
| --- | --- |
| Free | $4.50 |
| Starter (Bronze) | $1.75 |
| Scale (Silver) | $1.35 |
| Business (Gold) | $0.80 |
| Platinum | $0.45 |
| Diamond | $0.27 |

Plus a one-off $0.001 per GB of memory when a run starts.

Set a maximum cost per run in the Apify run options and the actor stops before it goes over.

### Limitations

- Public reviews only, no login.
- Booking shows about the last three years of reviews.
- With several traveler types or rating bands, Booking is read one pass after another. The review limit is shared by all passes, so the first pass can use it up; run them separately if you need a fixed number per type.
- Star filtering on Booking happens after download; rare ratings can mean reading many pages.
- A hotel with the same name in several cities needs its link, not its name.
- Google Hotels review dates are approximate (Google shows "3 months ago"), and Google reviews without a trip tag are dropped when a traveler type is asked for.
- Agoda and Trip.com have no name search here: give their link or id. A full Agoda read returns about 98% of the stated total (Agoda's pages overlap).

### Privacy

Reviewer names and profile photos are never collected. Booking, Agoda and Trip.com rows carry the reviewer's country as a two-letter code and nothing else about the person; Tripadvisor rows carry no reviewer location.

### FAQ

**Can I get only new reviews since my last run?**
Yes. Turn on `onlyNewReviews` and keep the same `stateKey`.

**Why did my hotel get no Booking, Agoda or Trip.com reviews?**
These sites are read only when you give their link or id for that hotel (no name search). Google Hotels needs a link or `searchGoogleByName`.

**Something looks wrong in the output. Who do I tell?**
Open an issue on the actor page with the run link.

# Actor input Schema

## `hotels` (type: `array`):

Hotel names, or objects that tie one hotel's pages together. A plain name is looked up on Tripadvisor (and Google Hotels with 'Also search Google by name'); Booking, Agoda and Trip.com have no name search, so for them or Google use the object form, for example {"name": "The Savoy", "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html"}. Object fields: name, tripadvisorUrl, bookingUrl, bookingId, googleUrl, agodaUrl, agodaId, tripcomUrl, tripcomId.

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

Tripadvisor, Booking.com, Agoda or Trip.com hotel pages, or Google Hotels pages. Each link goes to its site by host and counts as its own hotel; use the object form of 'Hotels' to pair pages into one summary. Example: https://www.booking.com/hotel/gb/the-savoy.html.

## `bookingHotelIds` (type: `array`):

Numeric Booking.com hotel IDs. Rows from an ID carry no hotel name or Booking rating, so prefer the hotel link when you have it.

## `searchGoogleByName` (type: `boolean`):

Also look each hotel name up on Google Hotels and read Google's own reviews: one more source per hotel, so up to twice the reviews (and cost) per name. Off by default; Google links in the object form or in the hotel links are always read.

## `maxReviewsPerSource` (type: `integer`):

Maximum number of reviews to collect from each site for each hotel (up to 50,000). Reviews are always read newest first. Booking shows about the last three years.

## `ratings` (type: `array`):

Only reviews with these star ratings (1 to 5). Tripadvisor filters on the site. Booking scores are converted to stars first (score divided by 2, rounded) and filtered after download. Leave empty for all.

## `languages` (type: `array`):

Two-letter language codes such as en or de. Applied to both sites. Leave empty for all.

## `travelerTypes` (type: `array`):

Only reviews from these kinds of travelers, applied to both sites (Tripadvisor trip type, Booking guest type). Leave empty for all.

## `dateFrom` (type: `string`):

Only keep reviews published on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). Paging stops once reviews are older.

## `onlyNewReviews` (type: `boolean`):

Return only reviews not delivered by earlier runs that used the same state key. The state is kept in a named key-value store in your account (change the state key to start over). Skipped reviews are not charged.

## `stateKey` (type: `string`):

Name of the incremental state used by 'Only new reviews' (letters, digits and dashes, up to 40 characters). Use different keys for independent schedules. Example: weekly-monitoring.

## `includeSummary` (type: `boolean`):

Add one row with rowType 'hotel-summary' (not charged) per hotel: the Tripadvisor rating and review count, the Booking score and count with Booking's category averages compared with the city, and the Google rating with its topic breakdown. Costs a few extra small requests per hotel.

## `includeErrorRows` (type: `boolean`):

Add one row with rowType 'error' (not charged) for each input or site that could not be read, e.g. no confident Tripadvisor match.

## `includeCoverageRows` (type: `boolean`):

Add one free row with rowType 'coverage' per hotel and source: the review total the site states, how many were delivered, why paging ended and whether anything is missing. Not charged.

## `markEdited` (type: `boolean`):

With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.

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

How many times a failed request is retried before that site is skipped.

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

Optional. By default the actor automatically picks the cheapest working proxy. Set this only to force your own proxy setup.

## Actor input object example

```json
{
  "hotels": [
    {
      "name": "The Savoy London",
      "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html"
    }
  ],
  "bookingHotelIds": [],
  "searchGoogleByName": false,
  "maxReviewsPerSource": 20,
  "ratings": [],
  "languages": [],
  "travelerTypes": [],
  "onlyNewReviews": false,
  "stateKey": "default",
  "includeSummary": true,
  "includeErrorRows": true,
  "includeCoverageRows": true,
  "markEdited": true,
  "maxRetries": 3
}
```

# Actor output Schema

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

All rows of this run in the default dataset.

## `view_overview` (type: `string`):

No description

## `view_summary` (type: `string`):

No description

## `view_errors` (type: `string`):

No description

## `view_coverage` (type: `string`):

No description

## `record_stats` (type: `string`):

Requests, data volume, per-source counts and cost per result for this run.

## `record_product` (type: `string`):

Hotel profiles (Booking details: stars, city, category scores) collected next to the reviews.

# 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 = {
    "hotels": [
        {
            "name": "The Savoy London",
            "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html"
        }
    ],
    "maxReviewsPerSource": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("arcadium/hotel-reputation").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 = {
    "hotels": [{
            "name": "The Savoy London",
            "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html",
        }],
    "maxReviewsPerSource": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("arcadium/hotel-reputation").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 '{
  "hotels": [
    {
      "name": "The Savoy London",
      "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html"
    }
  ],
  "maxReviewsPerSource": 20
}' |
apify call arcadium/hotel-reputation --silent --output-dataset

```

## MCP server setup

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

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/DEdSK1qqRlRLNPIID/builds/roCUEUVMLg25pGedy/openapi.json
