# Expedia Review Scraper⭐️ (`mrdoe/expedia-review-scraper`) Actor

Expedia review scraper for guest reviews, ratings, sentiment, and author details. Search by hotel URL, hotel name, or destination - no API needed. Get review text, dates, liked/disliked themes, and photos other Expedia scrapers miss, for any hotel worldwide.

- **URL**: https://apify.com/mrdoe/expedia-review-scraper.md
- **Developed by:** [MrDoe](https://apify.com/mrdoe) (community)
- **Categories:** Travel, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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?

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

## Expedia Review Scraper

Extract guest reviews from **Expedia.com** hotel pages — review text, ratings, author, stay dates, and sentiment — for any hotel, in bulk. Pair it with the [Expedia Hotel Scraper](https://apify.com/mrdoe/expedia-hotel-scraper) for rooms and pricing.

### What does Expedia Review Scraper do?

Paste one or more Expedia hotel URLs (or an Expedia share link) and get back individual review records — one row per review — for your chosen dates. You can also pull reviews for every hotel matching a destination in one run, or just type a hotel's name if you don't have its URL — the Actor finds that exact property and pulls its reviews the same way. If the name doesn't match anything closely enough, nothing is scraped and the run explains why instead of pulling reviews for the wrong hotel; a close-but-imperfect match still gets used, flagged on every output row.

### Key features

| Feature            | Description                                                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------ |
| One row per review | Review ID, title, text, rating, date, author                                                                 |
| URL mode           | Accepts specific hotel URLs, so you only pay for the hotels you care about                                   |
| Deeplink support   | Expedia share links (`expe.onelink.me` and similar) are auto-resolved to the canonical hotel URL             |
| Destination mode   | Pull reviews across every hotel matching a city, capped by `maxItems`                                        |
| Find by hotel name | Type a hotel's name instead of hunting for its URL; low-confidence matches are flagged, not silently guessed |
| Sentiment          | Liked/disliked theme lists extracted from each review                                                        |
| Per-hotel cap      | `maxReviewsPerHotel` controls cost on high-volume properties                                                 |

### What data can you extract?

- Review ID, title, text
- Rating value (0–10) and label
- Author name
- Review and stay date
- Sentiment: liked and disliked lists
- Guest photo URLs
- Linked hotel ID, URL, overall rating, and category ratings (Cleanliness, Service, etc.)

### How to use Expedia Review Scraper

#### Reviews for a specific hotel

```json
{
    "mode": "url",
    "startUrls": [{ "url": "https://www.expedia.com/Example-City-Hotels-Sample-Hotel.h11111.Hotel-Information" }],
    "checkIn": "2026-09-10",
    "checkOut": "2026-09-13",
    "maxReviewsPerHotel": 100,
    "maxItems": 100
}
```

#### Reviews for several hotels

```json
{
    "mode": "url",
    "startUrls": [
        { "url": "https://www.expedia.com/Example-City-Hotels-Sample-Hotel.h11111.Hotel-Information" },
        { "url": "https://www.expedia.com/Example-City-Hotels-Another-Sample.h22222.Hotel-Information" }
    ],
    "maxReviewsPerHotel": 50
}
```

#### Find a hotel by name (no URL needed)

```json
{
    "mode": "hotelName",
    "hotelName": "Hilton Garden Inn Dubai Mall Of The Emirates",
    "destination": "Dubai",
    "maxReviewsPerHotel": 50,
    "maxItems": 50
}
```

`destination` is optional here — add it when the hotel name alone could match more than one property (e.g. a hotel chain with several locations). If nothing matches closely enough, the run finishes with zero records and a message explaining why, rather than pulling reviews for an unrelated hotel; a close-but-imperfect match is still used, with the assumption noted in `extra.hotelNameMatch` on every returned row.

#### Paste a deeplink (expe.onelink.me share link)

Share links generated by the Expedia mobile app's "Share" button, as well as marketing and social deeplinks, are accepted as-is. The Actor follows the redirect, identifies the hotel, and extracts its reviews like any other URL.

```json
{
    "mode": "url",
    "startUrls": [{ "url": "https://expe.onelink.me/hnLd/9o2lnq8o" }]
}
```

Supported deeplink hosts: `expe.onelink.me` (primary), `expe.app.link`, `trip.expedia.com`. Only Expedia hotel deeplinks are handled.

#### Reviews across a destination

```json
{
    "mode": "search",
    "destination": "New York",
    "maxReviewsPerHotel": 50,
    "maxItems": 500
}
```

### Input parameters

| Parameter            | Type     | Default            | Description                                                       |
| -------------------- | -------- | ------------------ | ----------------------------------------------------------------- |
| `mode`               | string   | `url`              | `url`, `search`, or `hotelName`                                   |
| `startUrls`          | object\[] | Example hotel      | Expedia hotel URLs or deeplinks (URL mode)                        |
| `destination`        | string   | `Paris`            | City name (search mode); optional narrowing hint (hotelName mode) |
| `hotelName`          | string   |                    | Hotel name to look up (hotelName mode)                            |
| `destinationId`      | string   |                    | Optional numeric region ID. Skips auto-resolution                 |
| `checkIn`            | string   | tomorrow           | `YYYY-MM-DD` or a relative offset like `1 day`                    |
| `checkOut`           | string   | day after tomorrow | `YYYY-MM-DD` or a relative offset like `2 days`                   |
| `adults`             | integer  | `2`                | Adult guests, 1 to 14                                             |
| `children`           | string\[] | `[]`               | Ages of children, 0 to 17 (one entry per child)                   |
| `rooms`              | integer  | `1`                | Rooms to book, 1 to 8                                             |
| `currency`           | string   | `USD`              | 3-letter ISO code                                                 |
| `locale`             | string   | `en_US`            | Language and region locale                                        |
| `maxItems`           | integer  | `5`                | Hard cap on total review records                                  |
| `maxReviewsPerHotel` | integer  | `25`               | Cap reviews per hotel                                             |
| `proxyConfiguration` | object   | Apify Residential  | Proxy configuration                                               |

### Output example

```json
{
    "mode": "reviews",
    "placeName": "Example Hotel Paris",
    "hotelId": "11111",
    "hotelUrl": "https://www.expedia.com/Example-City-Hotels-Sample-Hotel.h11111.Hotel-Information",
    "hotelOverallRating": 9.2,
    "hotelRatingLabel": "Wonderful",
    "hotelTotalReviews": 1843,
    "hotelCategoryRatings": {
        "Cleanliness": 9.4,
        "Service": 9.3
    },
    "locale": "en_US",
    "provider": "expedia",
    "reviewId": "abc123def456",
    "reviewTitle": "Memorable stay",
    "reviewText": "Beautiful hotel with great service and a convenient location.",
    "reviewRating": 10,
    "reviewRatingLabel": "Excellent",
    "reviewDate": "Oct 14, 2025",
    "stayDuration": "Stayed 3 nights in Oct 2025",
    "authorName": "Sarah",
    "sentiment": {
        "liked": ["cleanliness", "staff and service", "location"],
        "disliked": []
    },
    "photos": [],
    "scrapedAt": "2026-04-18T07:30:00.000Z",
    "extra": { "hotelNameMatch": null }
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel from the Apify Console or API.

`extra.hotelNameMatch` is only set (non-null) when the run used `mode: "hotelName"`. It records what you typed, which hotel name it was matched to, a confidence score, and — when the match wasn't exact — a plain-language note explaining the assumption:

```json
"hotelNameMatch": {
    "query": "Hilton Garden Inn Dubai Mal",
    "matchedName": "Hilton Garden Inn Dubai Mall Of The Emirates",
    "score": 0.9,
    "confident": false,
    "note": "Hotel name \"Hilton Garden Inn Dubai Mal\" didn't match any hotel exactly (best match 90% confidence). Assumed you meant \"Hilton Garden Inn Dubai Mall Of The Emirates\" and scraped that hotel. If this is wrong, use a more exact name, add a destination to narrow the search, or use a direct hotel URL instead."
}
```

### Tips

- Use `maxItems` for total cost control and `maxReviewsPerHotel` to avoid one high-volume property eating your whole run.
- Pasting a specific hotel URL is the fastest, cheapest way to get exactly the reviews you want — destination mode is for bulk collection across a city.
- In `hotelName` mode, add `destination` whenever the hotel name alone could match a chain's multiple locations (e.g. "Hilton Downtown" exists in many cities) - it narrows the lookup instead of relying on the name alone.

### FAQ & support

**Is this legal?** This Actor extracts publicly available information from Expedia.com pages that anyone can view in a browser. You are responsible for using the collected data in compliance with applicable laws and Expedia's terms of service.

**I typed a hotel name and got zero results — why?** The name didn't match any hotel closely enough for the Actor to be confident it's the right one, so it scraped nothing rather than guessing. Check the run log for the exact warning, try a more complete/exact name, add `destination` to narrow the search, or switch to a direct hotel URL if you have one.

**Found a bug or have a feature request?** Open an issue on the Actor's Issues tab, or reach out for a custom data extraction solution tailored to your needs.

# Actor input Schema

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

"search" pulls reviews for hotels matching a destination. "url" pulls reviews for specific Expedia hotel URLs. "hotelName" looks up one specific hotel by name and pulls its reviews.

## `hotelName` (type: `string`):

Exact-ish name of one hotel to look up, e.g. "Hilton Garden Inn Dubai Mall Of The Emirates". Used only in hotelName mode. If the name doesn't match any result closely enough, nothing is scraped and a warning explains why - it's never guessed against an unrelated hotel. A close-but-imperfect match is still used, with a note on every output row saying which hotel name was assumed.

## `destination` (type: `string`):

City or region name to search, e.g. "Paris". Used in search mode. In hotelName mode, an optional hint (e.g. the city) to narrow the name lookup when the hotel name alone is ambiguous.

## `destinationId` (type: `string`):

Optional numeric Expedia region ID (the segment after ".d" in an Expedia URL). Skips destination-name resolution.

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

Expedia hotel URLs or share/deeplinks (expe.onelink.me, expe.app.link, trip.expedia.com). Used only in url mode.

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

YYYY-MM-DD. Defaults to tomorrow.

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

YYYY-MM-DD. Defaults to the day after tomorrow.

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

Adult guests per booking, 1 to 14.

## `children` (type: `array`):

Ages of children, 0 to 17 (one entry per child).

## `rooms` (type: `integer`):

Rooms to book, 1 to 8.

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

3-letter ISO currency code.

## `locale` (type: `string`):

Language and region locale, e.g. en\_US.

## `maxItems` (type: `integer`):

Hard cap on total review records saved, across all hotels combined.

## `maxReviewsPerHotel` (type: `integer`):

Cap on reviews saved per hotel.

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

Required - Apify Proxy must be enabled for this Actor to run.

## Actor input object example

```json
{
  "mode": "url",
  "destination": "Paris",
  "startUrls": [
    {
      "url": "https://www.expedia.com/Dubai-Hotels-Hilton-Garden-Inn-Dubai-Mall-Of-The-Emirates.h12949919.Hotel-Information"
    }
  ],
  "adults": 2,
  "children": [],
  "rooms": 1,
  "currency": "USD",
  "locale": "en_US",
  "maxItems": 5,
  "maxReviewsPerHotel": 25,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "startUrls": [
        {
            "url": "https://www.expedia.com/Dubai-Hotels-Hilton-Garden-Inn-Dubai-Mall-Of-The-Emirates.h12949919.Hotel-Information"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrdoe/expedia-review-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 = { "startUrls": [{ "url": "https://www.expedia.com/Dubai-Hotels-Hilton-Garden-Inn-Dubai-Mall-Of-The-Emirates.h12949919.Hotel-Information" }] }

# Run the Actor and wait for it to finish
run = client.actor("mrdoe/expedia-review-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 '{
  "startUrls": [
    {
      "url": "https://www.expedia.com/Dubai-Hotels-Hilton-Garden-Inn-Dubai-Mall-Of-The-Emirates.h12949919.Hotel-Information"
    }
  ]
}' |
apify call mrdoe/expedia-review-scraper --silent --output-dataset

```

## MCP server setup

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