# Expedia & Hotels.com Reviews Scraper - Guest Reviews & Ratings (`clearpath/expedia-hotels-com-reviews-scraper`) Actor

Scrape guest reviews from Expedia, Hotels.com, Orbitz, Travelocity and Vrbo. Paste hotel links or IDs from any of them: review text, rating, stay length, liked and disliked themes, photos, verified status and the hotel's reply. One row per review.

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

## Pricing

from $0.99 / 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

## Expedia & Hotels.com Reviews Scraper | Guest Reviews, Ratings & Replies

<blockquote style="border-left:4px solid #006D77;background:#EDF6F9;padding:12px 16px;margin:12px 0">
<span style="font-size:16px;font-weight:700;color:#1C1917">27 fields per review, from five sites at once:</span> <span style="font-size:15px;color:#57534E">review text, score out of 10, what the guest liked and disliked, stay length, verified status, photos, and the hotel's own reply.</span>
</blockquote>

![Expedia and Hotels.com reviews scraper: review text, score, liked and disliked themes, stay length, verified status and the hotel's management response as structured JSON](https://api.apify.com/v2/key-value-stores/nz3QLP9PeVeaydEah/records/expedia-reviews-hero.png)

<blockquote style="border-left:4px solid #E29578;background:#FFF5F0;padding:12px 16px;margin:12px 0">
<span style="font-size:15px;color:#57534E">⚡ <b>One link, five brands.</b> A property's reviews are identical on Expedia, Hotels.com, Orbitz, Travelocity and Vrbo, so paste a link from whichever site you use. You never have to say which one it came from.<br><br><b>Reviews only.</b> This actor returns no prices, room rates or availability — for those use the <a href="https://apify.com/clearpath/expedia-hotels-scraper-pro">Expedia Hotels Scraper</a>.</span>
</blockquote>

#### Copy to your AI assistant

Copy this block into ChatGPT, Claude, Cursor, or any LLM to start using this actor.

```
clearpath/expedia-hotels-com-reviews-scraper on Apify. Scrapes guest reviews for a hotel from Expedia Group: Expedia, Hotels.com, Orbitz, Travelocity and Vrbo share one review corpus per property, so a link from any of them works and the rows are the same. Call: ApifyClient("TOKEN").actor("clearpath/expedia-hotels-com-reviews-scraper").call(run_input={...}), then client.dataset(run["defaultDatasetId"]).list_items().items for one row per review. A SECOND dataset holds one row per hotel (identity, location, overall score, reviews available vs collected): read run["output"]["hotels"], or open the "summary" dataset in Storage. Inputs: hotels (array, required - hotel page links or numeric hotel IDs, mixed freely; the ID is the number after ".h" in a link), maxReviews (integer, default 100, per hotel, 0 = every review the source serves). No date, rating or language filters: filter the rows yourself. Reviews only - this actor returns no prices, rates or availability. Full actor spec (input schema, dataset fields, README): GET https://api.apify.com/v2/acts/clearpath~expedia-hotels-com-reviews-scraper/builds/default (Bearer TOKEN) returns inputSchema, actorDefinition.storages.datasets, readme. Pricing: pay-per-event, $0.009 per run start plus $1.99 per 1,000 reviews. Get token: https://console.apify.com/account/integrations
```

### Key Features

- **Five brands, one actor**: Expedia, Hotels.com, Orbitz, Travelocity and Vrbo links all resolve to the same property and the same reviews
- **27 fields per review**: text, score out of 10, rating label, liked and disliked themes, author, stay length, verified status, helpful votes, photos and the hotel's reply
- **The hotel's side of the story**: `managementResponse` carries the reply text, who wrote it and when — present on 36 of 40 reviews in our test sample
- **Two inputs**: hotel links or IDs, and how many reviews per hotel. Nothing else to configure
- **Everything or a sample**: `maxReviews: 0` collects every review the source serves; a small number gives you a quick look first
- **Honest completion**: every hotel gets a summary row saying how many reviews exist, how many were collected and whether the source ran out early

### Pricing: Pay Per Event (PPE)

**$1.99 per 1,000 reviews**, no subscription.

| Event | Price |
|-------|-------|
| Review scraped | $0.00199 |

Example: 100 reviews costs about $0.20. A 1,000-review sweep of one hotel costs about $2.00.

Apify also charges $0.009 when a run starts, which covers the hotel lookup a run performs before any review is read.

### Quick Start

#### One hotel, first 100 reviews

```json
{
  "hotels": ["https://www.expedia.com/Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information"],
  "maxReviews": 100
}
```

#### A Hotels.com, Orbitz, Travelocity or Vrbo link works the same way

Only the `.h<id>` part of a link is read, so paste whatever your browser gives you:

```json
{
  "hotels": ["https://www.hotels.com/Hotel.h5951861.Hotel-Information"],
  "maxReviews": 100
}
```

#### Several hotels, by ID

The ID is the number after `.h` in any hotel link. Mix IDs and links freely; duplicates are collapsed.

```json
{
  "hotels": ["5951861", "9619719", "https://www.orbitz.com/Hotel.h868.Hotel-Information"],
  "maxReviews": 50
}
```

#### Every review a hotel has

```json
{
  "hotels": ["5951861"],
  "maxReviews": 0
}
```

`0` means everything the source will serve. On a 1,527-review property this returned 1,475 rows and reported the shortfall rather than pretending it was complete.

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `hotels` | array | — | Hotel page links or numeric hotel IDs, mixed freely. Required. |
| `maxReviews` | integer | `100` | Reviews per hotel, not per run. `0` collects every review available. |

### Output Data

Two datasets. Reviews go to the default dataset, one row per review. A second dataset named `summary` holds one row per hotel.

#### Review row (27 fields)

| Group | Fields |
|---|---|
| The review | `id` `text` `title` `highlightedText` `date` `dateRaw` |
| The score | `rating` `ratingScore` `ratingScaleMax` `ratingLabel` |
| Themes | `liked` `disliked` |
| The guest | `author` `authorLocation` `traveledWith` `stayDuration` `verified` `verificationLabel` |
| Signals | `helpfulVotes` `photos` `originalLanguage` `language` `brandType` |
| The hotel's reply | `managementResponse` (text, author, date, header) |
| Identity | `hotelId` `hotelName` `hotelUrl` |

#### Hotel row (`summary` dataset)

`hotelId` `hotelName` `hotelUrl` `address` `city` `countryCode` `latitude` `longitude` `totalScore` `reviewsCount` `reviewsFetched` `reviewStatus` `status`

`reviewsCount` is how many reviews the hotel has; `reviewsFetched` is how many this run delivered. `reviewStatus` tells you why they differ: `limit_reached` means your `maxReviews` stopped it, `exhausted` means the source ran out, `partial` means it stopped early and the row says so.

### Complete Output Example

One real review, every field as returned:

```json
{
  "id": "6a7fa1f71832b92a621e4d54",
  "title": null,
  "text": "No place to put luggage .",
  "highlightedText": null,
  "originalLanguage": null,
  "language": null,
  "brandType": null,
  "rating": "8/10 Good",
  "ratingScore": 8,
  "ratingScaleMax": 10,
  "ratingLabel": "Good",
  "author": "Saverio",
  "authorLocation": null,
  "date": "Aug 14, 2026",
  "dateRaw": "Aug 14, 2026",
  "stayDuration": "Stayed 3 nights in Aug 2026",
  "traveledWith": [],
  "verified": true,
  "verificationLabel": "Verified review",
  "liked": [
    "Cleanliness",
    "staff & service",
    "amenities",
    "property conditions & facilities"
  ],
  "disliked": [],
  "managementResponse": {
    "text": "Thank you for your feedback. We understand that you found the luggage space limited. We do offer larger room categories with more space, which may be more suitable for guests travelling with larger luggage. We hope to welcome you again soon!",
    "author": "Nicolas",
    "respondedOn": "Sep 17, 2026",
    "header": "Response from Nicolas on Sep 17, 2026"
  },
  "helpfulVotes": 0,
  "photos": [],
  "hotelId": "5951861",
  "hotelName": "Grand Hotel Leveque",
  "hotelUrl": "https://www.expedia.com/Hotel.h5951861.Hotel-Information"
}
```

And the hotel it belongs to, from the `summary` dataset:

```json
{
  "hotelId": "5951861",
  "hotelName": "Grand Hotel Leveque",
  "hotelUrl": "https://www.expedia.com/Hotel.h5951861.Hotel-Information",
  "address": "29 rue Cler, Paris, Paris, 75007",
  "city": "Paris",
  "countryCode": "FRA",
  "latitude": 48.857416,
  "longitude": 2.306071,
  "reviewsCount": 1002,
  "totalScore": 9.4,
  "reviewStatus": "limit_reached",
  "reviewsFetched": 40
}
```

### What You Can Do With It

- **Reputation tracking**: watch what guests praise and complain about, hotel by hotel, over time
- **Competitive sets**: pull the same review depth for a block of hotels in one district and compare the `liked` and `disliked` themes
- **Response auditing**: `managementResponse` shows which complaints a hotel answers and how fast
- **Training and sentiment data**: review text with a numeric score and the guest's own theme labels already attached

### FAQ

**Does a Hotels.com link work?**
Yes. A property's reviews are identical on Expedia, Hotels.com, Orbitz, Travelocity and Vrbo — we measured it across all four domains and every Expedia Group site ID, and the response was the same every time. Paste whichever link you have.

**Where do I find the hotel ID?**
It is the number after `.h` in a hotel link. In `.../Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information` the ID is `5951861`.

**How many reviews can I get for one hotel?**
Set `maxReviews: 0` for everything the source serves. It does occasionally stop short of the published count — on a 1,527-review property it returned 1,475 — and the summary row reports that as `partial` instead of hiding it.

**Does it include the hotel's reply?**
Yes, in `managementResponse`, with the responder's name and date. Most reviewed hotels reply to a large share of reviews; 36 of the 40 reviews in our sample carried one.

**Can I filter by date, rating or language?**
Not in the input. Every review comes back with `date`, `ratingScore` and `originalLanguage`, so filter the rows after export.

**Does it return prices or availability?**
No. This actor is reviews only. For rates, room types and availability use the [Expedia Hotels Scraper](https://apify.com/clearpath/expedia-hotels-scraper-pro).

**What happens if a hotel ID is wrong?**
The run continues. That hotel gets a summary row with `status: "unresolved"` and the other hotels still deliver their reviews.

### Related Actors

| Actor | What it does |
|---|---|
| [Expedia Hotels Scraper](https://apify.com/clearpath/expedia-hotels-scraper-pro) | Prices, rooms and live rates, property details, search by city |
| [Booking.com Reviews Scraper](https://apify.com/clearpath/booking-reviews-scraper) | Guest reviews, scores and replies from Booking.com |
| [Tripadvisor Reviews Scraper](https://apify.com/clearpath/tripadvisor-reviews-scraper) | Reviews for hotels, restaurants and attractions |
| [Yelp Reviews Scraper](https://apify.com/clearpath/yelp-reviews-scraper) | Business reviews in every language, with owner replies |

### Support

- 📧 **Email**: max@mapa.slmail.me
- 📖 **Found a bug?**: Use the issues tab and describe your issue
- 🔧 **Feature Requests**: Contact via email or issues tab

### Legal Compliance

This scraper extracts publicly available hotel review data. Users must comply with the source's terms of service and applicable data protection regulations for their intended use.

***

[🚀 **Start Scraping Expedia and Hotels.com Reviews**](https://console.apify.com/actors/clearpath/expedia-hotels-com-reviews-scraper)

# Actor input Schema

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

One hotel per line. Links and numeric IDs can be mixed, and duplicates are collapsed.<br><br>Accepted:<br>• Expedia link: <code>https://www.expedia.com/Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information</code><br>• The same link from Hotels.com, Orbitz, Travelocity or Vrbo<br>• Hotel ID on its own: <code>5951861</code><br><br>The ID is the number after <code>.h</code> in the link.

## `maxReviews` (type: `integer`):

How many reviews to collect for <b>each</b> hotel, not per run.<br><br><code>0</code> collects every review the source will serve, which on a large hotel is several thousand and is charged per review. Start small while you check the output.

## Actor input object example

```json
{
  "hotels": [
    "https://www.expedia.com/Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information"
  ],
  "maxReviews": 50
}
```

# Actor output Schema

## `reviews` (type: `string`):

One row per review: text, rating, themes, traveller, photos and the hotel's response.

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

One row per hotel: identity, location, overall score, how many reviews exist and how many were collected.

# 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": [
        "https://www.expedia.com/Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information"
    ],
    "maxReviews": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("clearpath/expedia-hotels-com-reviews-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 = {
    "hotels": ["https://www.expedia.com/Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information"],
    "maxReviews": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("clearpath/expedia-hotels-com-reviews-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 '{
  "hotels": [
    "https://www.expedia.com/Paris-Hotels-Grand-Hotel-Leveque.h5951861.Hotel-Information"
  ],
  "maxReviews": 50
}' |
apify call clearpath/expedia-hotels-com-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,clearpath/expedia-hotels-com-reviews-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/pDZrp0zWTsAgmXK7L/builds/1ySzFf38I757hujpE/openapi.json
