Expedia Hotels Scraper - Prices, Ratings & Reviews
Pricing
from $1.00 / 1,000 results
Expedia Hotels Scraper - Prices, Ratings & Reviews
From $1/1K. Scrape Expedia.com hotel listings and reviews for any destination. Extract prices, ratings, review text, photos, location details, hotel information, and more in clean, structured data.
Pricing
from $1.00 / 1,000 results
Rating
5.0
(2)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
68
Total users
17
Monthly active users
14 days ago
Last modified
Categories
Share
Expedia Hotel Scraper: Prices, Ratings & Reviews
Expedia Hotel Scraper turns Expedia hotel listings and guest reviews into structured data. Search any destination or paste property and search URLs, and get back hotel cards with prices, star ratings, guest ratings and photos for your chosen dates, or flip a toggle to pull individual review records instead. Filter by price, star rating and sort order, cap review depth per hotel, and export to JSON, CSV or Excel, or read the results straight through the API.
Why This Scraper?
- Two ways in. Search by destination, or paste Expedia property and search URLs. Share links from the Expedia app (expe.onelink.me and similar) are resolved automatically to the canonical hotel page first.
- Two outputs from the same input. Aggregate hotel cards by default, or turn on Scrape reviews only for individual review records, one row per review.
- Every filter guests use. Check in and check out dates, adults, rooms, children with ages, minimum and maximum nightly price, star rating, and sort order.
- Review depth on demand. Cap reviews per hotel, embed a sample of reviews inside each hotel card, or pull every review as its own record with sentiment, stay details and management replies.
- 30 locales and 36 currencies. Cards and reviews come back in the guest's own language and currency.
- Built for schedules. Incremental mode returns only new, updated and reappeared hotels on repeat runs against the same search or URLs, and a blocked run fails loudly instead of quietly returning an empty dataset.
- Resume support. Continue an interrupted large destination pull without paying twice for hotels already collected in an earlier run.
Use Cases
- Rate and price monitoring: track nightly prices, discounts and availability for a set of properties across a date range.
- Reputation and sentiment analysis: pull every review for a property to find recurring compliments, complaints and how management responds.
- Scheduled market watching: run the same destination or property list on a schedule with incremental mode and only pay for what changed.
- Travel content and comparison sites: build destination guides with hotel photos, star ratings and pricing pulled straight from search results.
- Competitive benchmarking: compare star rating, guest rating and price band for a shortlist of properties in one destination.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
mode | "search" (also "url", "reviews") |
placeName | "Hotel Example Paris" |
hotelId | "11111" |
hotelUrl | Expedia link to the property |
hotelOverallRating | 9.2 (0 to 10 scale) |
hotelRatingLabel | "Wonderful" |
hotelTotalReviews | 1843 |
hotelCategoryRatings | {"Cleanliness": 9.4, "Staff": 9.3} (reviews mode) |
provider | "expedia" |
locale | "en_US" (reviews mode) |
scrapedAt | "2026-04-18T07:30:00.000Z" |
extra.priceFrom / extra.priceFormatted | 420 / "$420 nightly" |
extra.priceStrikeout | "$600" (present when discounted) |
extra.starRating | 5 |
extra.description | short property description |
extra.images | gallery image URLs |
extra.searchDestination / extra.checkIn / extra.checkOut / extra.adults | the search and stay context used for this record |
extra.reviews | up to Max Reviews per Hotel inline review objects, when Include reviews in hotel cards is on |
reviewId / reviewTitle / reviewText | review identity and body (reviews mode) |
reviewRating / reviewRatingLabel | 10 / "Excellent" |
authorName | "Sarah" |
reviewDate / stayDate | "Oct 14, 2025" / "Stayed 3 nights in Oct 2025" |
sentiment | {"liked": ["location", "staff"], "disliked": []} |
hotelResponse / hotelResponseAuthor / hotelResponseDate | management reply text, author and date |
photos | guest submitted photo URLs (reviews mode) |
Scrape reviews only mode returns one record per review using the review fields above. Incremental mode adds changeType, changedFields, firstSeenAt and lastSeenAt to hotel card records so you can tell what changed since the previous run.
How to Use
- Pick a mode:
search(by destination) orurl(paste hotel or search URLs). - Set Check in and Check out dates (required), plus adults, rooms, children, and any price, star rating or sort filters.
- Turn on Scrape reviews only for individual review records, or leave it off for hotel cards, and set Max items to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search a destination:
{"mode": "search","destination": "Paris","checkIn": "2026-11-10","checkOut": "2026-11-13","adults": 2,"maxItems": 50}
Filter by price and star rating:
{"mode": "search","destination": "Tokyo","checkIn": "2026-11-10","checkOut": "2026-11-13","starRatings": ["4", "5"],"minPrice": 150,"maxPrice": 400,"sortBy": "REVIEW"}
Fetch specific hotels by URL:
{"mode": "url","startUrls": [{ "url": "https://www.expedia.com/Example-City-Hotels-Sample-Hotel.h11111.Hotel-Information" }],"checkIn": "2026-11-10","checkOut": "2026-11-13"}
Reviews only for a destination:
{"mode": "search","destination": "New York","checkIn": "2026-11-10","checkOut": "2026-11-13","reviewsOnly": true,"maxItems": 500}
Property URLs must be Expedia Hotel-Information pages, and search URLs must be Expedia Hotel-Search pages. Expedia app share links (expe.onelink.me, expe.app.link, trip.expedia.com) are followed automatically to the matching hotel; Hotels.com and Vrbo links are not supported by this actor.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/expedia-universal-scraper").call(run_input={"mode": "search","destination": "Paris","checkIn": "2026-11-10","checkOut": "2026-11-13",})for hotel in client.dataset(run["defaultDatasetId"]).iterate_items():print(hotel["placeName"], hotel["extra"].get("priceFrom"))
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/expedia-universal-scraper').call({mode: 'search',destination: 'Paris',checkIn: '2026-11-10',checkOut: '2026-11-13',});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips hotels already collected there, so you don't pay twice. - Incremental mode (
incrementalMode) is for scheduled runs over the same destination search or URL list. Each hotel is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEARED, orEXPIRED(only after a run that scanned the whole tracked scope, and only withemitExpired).stateKeynames or shares the stored state. Scrape reviews only runs do not participate in incremental mode. With incremental mode off, output is exactly as before.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (by destination) or url (paste hotel or search URLs). |
destination | string | (none) | City name for search mode, for example Paris, New York, Tokyo. |
destinationId | string | (none) | Optional numeric Expedia region ID, the number after .d in an Expedia URL. Skips destination resolution. |
startUrls | array | (none) | Expedia property or search URLs for url mode. |
checkIn | string | 1 day | Check in date. Absolute (YYYY-MM-DD) or relative, e.g. 1 day. Required. |
checkOut | string | 2 days | Check out date. Absolute or relative, e.g. 2 days. Required. |
adults | integer | 2 | Adult guests, 1 to 14. |
children | array | [] | Ages of children, 0 to 17, one entry per child. |
rooms | integer | 1 | Rooms to book, 1 to 8. |
minPrice | integer | (none) | Minimum nightly price filter. |
maxPrice | integer | (none) | Maximum nightly price filter. |
starRatings | array | [] | One or more of 1 to 5. Empty means all star ratings. |
sortBy | string | RECOMMENDED | RECOMMENDED, PRICE_LOW_TO_HIGH, PRICE_HIGH_TO_LOW, DISTANCE, PROPERTY_CLASS, or REVIEW. |
reviewsOnly | boolean | false | Output individual review records instead of hotel cards. |
includeReviews | boolean | false | Embed up to Max Reviews per Hotel reviews inside each hotel card. Ignored when Scrape reviews only is on. |
maxReviewsPerHotel | integer | 25 | Cap on reviews embedded per hotel card. |
reviewsFrom | string | (none) | YYYY-MM-DD. Only keep reviews on or after this date. |
includeCategoryRatings | boolean | true | Attach aggregate category ratings (cleanliness, staff, and similar) to review records in Scrape reviews only mode. |
currency | string | USD | 3 letter currency code for prices on hotel cards. |
locale | string | en_US | Language and region for card content and review language. |
maxItems | integer | 20 | Hard cap on total records saved to the dataset. |
resumeFromRunId | string | (none) | Continue one interrupted run; skips hotels already collected there. |
incrementalMode | boolean | false | Return only new, updated and reappeared hotels on repeat runs against the same search or URLs. |
stateKey | string | (none) | Name or share an incremental mode state. Left empty, the actor derives one from the search or URLs and filters. |
emitUnchanged | boolean | false | Also return (and bill) hotels unchanged since the last incremental run. |
emitExpired | boolean | false | Also return (and bill) hotels no longer found, after a complete scan. |
proxy | object | (none) | Proxy configuration. Prefilled to Apify's residential proxy group. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which records are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"mode": "search","placeName": "Hotel Example Paris","hotelId": "11111","hotelUrl": "https://www.expedia.com/Example-City-Hotels-Sample-Hotel.h11111.Hotel-Information","hotelOverallRating": 9.2,"hotelRatingLabel": "Wonderful","hotelTotalReviews": 1843,"provider": "expedia","scrapedAt": "2026-04-18T07:30:00.000Z","extra": {"searchDestination": "Paris","starRating": 5,"description": "Recently renovated boutique hotel steps from the city center","priceFrom": 420,"priceFormatted": "$420 nightly","priceStrikeout": "$600","currency": "USD","images": ["https://images.example.com/lodging/sample-1.jpg","https://images.example.com/lodging/sample-2.jpg"],"checkIn": "2026-11-10","checkOut": "2026-11-13","adults": 2}}
Plan Requirement
This actor is prefilled to use a residential proxy connection, which Expedia access typically needs for a reliable run. Residential access comes from your Apify proxy settings; no separate third-party account is required. Adjust the proxy group under Connection if a run has trouble reaching results.
FAQ
How much does it cost?
You pay per record returned, plus a small surcharge for each hotel returned from a property URL in URL mode. Destination-search runs that only return hotel cards do not trigger that charge. The Pricing tab shows the current rates. Use Max items to cap the cost of any run.
Is it legal to scrape Expedia?
This actor collects only publicly available listing and review data. You are responsible for how you use it: follow Expedia's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and availability change constantly and are not guaranteed to still be bookable by the time you read them.
What is the difference between Resume and Incremental mode?
They solve different problems and are the actor's most common point of confusion. Resume (resumeFromRunId) continues one specific interrupted run so it doesn't restart from scratch. Incremental mode is for running the same destination search or hotel/search URLs again and again, for example daily, and getting back only the hotels that are new, changed, or gone since the last run.
Can I get only new or changed hotels on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Repeat runs then return only new, updated and reappeared hotels, and unchanged ones are not billed unless you turn on Emit unchanged.
Why did my run return fewer hotels than expected?
Price, star rating and destination filters narrow the result set, and Max items caps the total regardless of filters. A destination that doesn't resolve, or a search with no matching hotels for the chosen dates and filters, also returns fewer records without failing the run.
Why did my run fail instead of returning an empty dataset?
The run fails when Expedia blocks every single request in that run, even after retrying with a different session, or when Resume points at a run or dataset that can't be read (or clashes with existing incremental state for the same search). In the blocked case you get a clear failure message instead of a silent empty dataset. Retry in a few minutes, or switch the proxy to Residential with a different country. An unresolved destination or a search with genuinely zero matching hotels still succeeds, just with fewer or zero records.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more travel data?
Pair this actor with these related scrapers from the same team:
| 🏨 Trip.com Hotels, Rooms & Reviews Scraper Pull structured hotel listings from trip.com with prices, room types, amenities... | 🏨 Traveloka Hotel Scraper Scrape Traveloka hotels across Singapore, Malaysia, Indonesia, Thailand, Vietnam and the... |
| 🏨 Booking.com Hotels, Prices and Reviews Scraper Scrape booking.com hotels, apartments, villas and hostels. Search any destination with... | 🏨 Vrbo Scraper Extract vacation rental data from Vrbo.com by location, region, coordinates, property... |
| 🏨 Yanolja Scraper Scrape Yanolja (NOL), Korea's largest accommodations marketplace, by region or URL. Every... | 🏨 Airbnb Scraper Scrape Airbnb stays by destination or link: nightly and total price, rating breakdown... |
💬 Support & custom scrapers
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.