Expedia Room Rates Scraper avatar

Expedia Room Rates Scraper

Pricing

from $2.10 / 1,000 hotel-priceds

Go to Apify Store
Expedia Room Rates Scraper

Expedia Room Rates Scraper

Scrape every room Expedia and Hotels.com are selling for a hotel and your dates. Just add property IDs or hotel links and get room name, size, nightly price, stay total with taxes, board, cancellation terms and payment policy. $3.50 per 1,000 priced hotel-stays.

Pricing

from $2.10 / 1,000 hotel-priceds

Rating

0.0

(0)

Developer

BonCross API

BonCross API

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

7 days ago

Last modified

Categories

Share

What is Expedia Room Rates Scraper?

A hotel on Expedia doesn't have one price. It has a grid: each room, sold several ways, refundable or not, with breakfast or without. Expedia Room Rates Scraper gets you that whole grid for a hotel and your dates, one row per rate, from Expedia or Hotels.com. Paste property IDs or hotel links, set the dates, and you're done.

Four things you won't get from a search-level price:

  • All the rates, not the cheapest one. Individual hotels returned 13 to 58 rows each in the measured runs, and 40 hotels over two stays returned 1,635.
  • Refundable against non-refundable, for the same room. Each row says whether the rate can be cancelled and until when, in the site's own words, next to its board and payment terms.
  • A price calendar on top. Switch it on and you get a nightly price for each date the hotel is bookable, around 217 dates or roughly eight months ahead.
  • Change tracking that saves you money. A hotel and stay whose cheapest price hasn't moved since your last run is skipped, and costs $0.0005.

It's priced per hotel and stay, not per row: $3.50 per 1,000 priced hotel-stays on the Free plan and $2.60 on Business, however many rates each one has. Proxy usage and platform usage are included.

BonCross Verified means we tested this actor against the live site before release and kept the record:

  • 36 of 36 live checks on its inputs and filters passed on 7 October 2026
  • 7 hotels run the same day on 3 stays each: 248 rates, 4 sold-out answers, 1 error row
  • A stay the site does not answer gets an error row saying so

What data is in a rate row?

🏨 Hotel name and property IDSite (Expedia or Hotels.com)
🛏 Room nameBed setup
📐 Room size👥 Sleeps (max occupancy)
⭐ Guest rating for that room typeRoom and rate IDs
💵 Price per night💰 Stay total with taxes and fees
Price as displayedPrice before discount
💱 CurrencyWhether tax is in the nightly rate, and the pre-tax nightly price
🍳 BoardWhether a meal is included
Cancellation terms and deadlineRefundable flag
💳 Payment termsSold-out flag
📅 Check-in, check-out and nightsAdults, child ages and rooms priced
Price calendar dates (optional)Market the prices came from
🔗 Link to the booking pageTimestamp of the run

Who uses a rate grid?

Revenue managers tracking a competitor set across a season, to see which rooms move first. Analysts putting a value on flexibility by comparing the refundable and non-refundable price of one room. Hotels checking that their own rates and board descriptions are listed as intended. Teams running a rate-parity check of one site's grid against another's, or building a price history from a daily run with change tracking on. The calendar answers a different question: how far ahead a hotel starts discounting.

How to scrape Expedia room rates

  1. Sign up for a free Apify account.
  2. Open Expedia Room Rates Scraper.
  3. Put property IDs or hotel page links into Hotels. If you have a long list, load it through Start URLs from a file or a Google Sheet.
  4. Choose Expedia or Hotels.com. A link you paste already names its own site.
  5. Set check-in and check-out, or leave them empty and set days ahead.
  6. Click Start. One hotel takes about 3 seconds. Download the rates as Excel, CSV or JSON.

⬇️ Input

A hotel is a property ID or a hotel page link, in any mix. The link https://www.expedia.com/Santorini-Hotels-Ira-Hotel-Spa.h6116875.Hotel-Information and the bare number 6116875 mean the same thing. Don't have the IDs? Expedia Hotel Search Scraper returns them for a whole destination.

Two hotels, one weekend

{
"hotels": ["12570", "https://www.expedia.com/Santorini-Hotels-Ira-Hotel-Spa.h6116875.Hotel-Information"],
"checkIn": "2026-11-13",
"checkOut": "2026-11-15",
"adults": 2,
"rooms": 1
}
{
"hotels": ["12570", "https://www.hotels.com/ho3388/"],
"checkIn": "2026-11-13",
"checkOut": "2026-11-15",
"freeCancellationOnly": true,
"maxRatePlansPerStay": 3
}

One hotel across three stays, with a row when a stay is sold out

{
"hotels": ["375613"],
"checkIn": "2026-10-24",
"checkOut": "2026-10-25",
"stays": ["2026-12-31_2027-01-02", "2027-02-10_2027-02-13"],
"maxRatePlansPerStay": 25,
"includeUnavailable": true
}

The rate grid plus thirty days of calendar prices

{
"hotels": ["12570"],
"checkIn": "2026-11-13",
"checkOut": "2026-11-15",
"priceCalendar": true,
"calendarDaysMax": 30
}

A daily watch that returns only what moved

{
"hotels": ["12570", "375613"],
"checkInDaysAhead": 30,
"nights": 2,
"priceStoreName": "my-comp-set",
"onlyNewOrChanged": true
}

Three limits, three different jobs. "Maximum rates per hotel and stay" keeps only the cheapest N rates for each hotel and date range. "Maximum hotel-stays for the whole run" stops after that many hotel-stays, each with all its rates. "Maximum rows for the whole run" stops once that many rows exist, cutting the last hotel-stay to fit and keeping its cheapest rates. Price-calendar rows count towards it. 0 means no limit on each.

Filters. "Refundable rates only" keeps rates the site states can be cancelled. "Rates including a meal only" keeps rates with breakfast or another meal. "Maximum price per night" drops rates above a nightly price in the market's currency, and rates with no price.

Guests and rooms. Adults (1 to 14, default 2) and one age per child set the party. "Rooms" prices a booking of several rooms: the nightly rate stays per room and the stay total covers them all, which is how the site quotes it. Asking for more rooms can return fewer rows, because a room with one unit left can't be booked twice. That's the site telling you about availability, not rows going missing.

Market decides the money. Expedia runs 40 country sites and each quotes its own currency. Pick Japan and you get JPY, pick the United States and you get USD, for the same hotel on the same dates. Nothing is converted afterwards: these are the prices that market shows a traveller. Leave Market on Auto and the first pasted link's own site sets it for the run. An expedia.se link is quoted in SEK, and a list of bare property IDs uses the US site in USD. "Currency" defaults to USD wherever the market offers it. Clear it to take the market's own. Each row reports the currency its price is really in.

A nightly price is only comparable within one market. In 18 of the 40 markets the nightly rate already includes tax, and the stay total is exactly the nightly rate times the nights. In the other 22 tax is added on top. priceTotal is what you'd pay either way. Compare on that.

Hotels.com is read on the United States market only.

For developers

  • startUrls takes the same links as hotels, and the two are combined. The party is adults and childrenAges. The limits are maxRatePlansPerStay, maxItems (hotel-stays) and maxRows. The filters are freeCancellationOnly, mealIncludedOnly and maxPricePerNight.
  • stays takes CHECKIN_CHECKOUT lines, and every hotel is priced for every stay. With no checkIn, the window is checkInDaysAhead (default 30) and nights (default 1), counted from the day the run starts.
  • priceCalendar adds one row per bookable date on top of the rate grid. calendarDaysMax keeps only the first N dates per hotel.
  • priceStoreName names the store that remembers each hotel-stay's cheapest price. With onlyNewOrChanged on, a hotel-stay whose cheapest price hasn't moved is skipped and billed as hotel-checked only.
  • includeUnavailable writes a single row with isSoldOut: true for a stay with nothing for sale.
  • language: "site-default" labels rooms and rates in the market's own language. Refundability, meals, room size, capacity, beds and the room's rating are published only in English wording. In this mode the grid is read once more in English and those values are copied across by rate. A rate missing from the English copy (2 of 40 on the markets measured) keeps them empty, with no guessing. Where the regional site leaves out a rate's cancellation deadline, the English wording of it is used.
  • maxConcurrency (default 4, up to 8) is how many hotel-stays are fetched at once. delayMs (default 0) pauses after each request, per parallel lane, and values above 2000 are treated as 2000.
  • If the platform moves or restarts a run part-way, it carries on where it stopped. Stays already in the dataset aren't fetched, delivered or billed again. Restarting an 80-stay run part-way gave the same 1,635 rows as an uninterrupted run.

⬆️ Output

One row per rate. A room appears once for each way it's sold. This is a refundable rate at the Hilton Chicago, from a run on 7 October 2026:

{
"id": "12570-2026-11-13-403873-266071239",
"brand": "expedia",
"market": "us",
"hotelId": 12570,
"hotelName": "Hilton Chicago",
"url": "https://www.expedia.com/en/h12570.Hotel-Information?chkin=2026-11-13&chkout=2026-11-15",
"checkIn": "2026-11-13",
"checkOut": "2026-11-15",
"nights": 2,
"roomId": "403873",
"ratePlanId": "266071239",
"roomName": "Room, 1 King Bed",
"roomRating": 8.4,
"roomRatingScale": 10,
"roomSizeSqm": 24,
"roomSizeText": "260 sq ft",
"maxOccupancy": 2,
"bedConfiguration": "1 King Bed",
"priceTotal": 817.16,
"pricePerNight": 319,
"priceText": "$319 nightly",
"priceTotalText": "$817",
"priceListedPerNight": null,
"priceListedTotal": null,
"currency": "USD",
"isTaxIncludedInNightly": false,
"pricesIncludeTax": false,
"pricePerNightExcludingTax": 319,
"paymentType": "PAY_NOW",
"paymentText": "Pay the total now",
"isRefundable": true,
"cancellation": "Fully refundable before Tue, Nov 10",
"board": "No extras",
"isMealIncluded": false,
"adults": 2,
"childrenAges": [],
"rooms": 1,
"isSoldOut": false,
"scrapedAt": "2026-10-07T20:27:07.856Z"
}

Reading the prices

pricePerNight and priceTotal aren't multiples of each other. The nightly figure is the one the site displays, and the total is what you'd pay with taxes and fees. Two nights at $319 comes to $817.16 here, not $638, because the US adds tax and fees on top. The same room's non-refundable rate was $271 a night and $703.44 for the stay in a run a minute earlier.

priceTotal always includes taxes and fees. pricesIncludeTax says whether pricePerNight does too. It's true in markets such as the UK, Germany, France and Australia and false in markets such as the US, Japan and Canada. Where it's false, the nightly rate is also in pricePerNightExcludingTax. isTaxIncludedInNightly is the older name for the same flag, kept for compatibility, and the two are always equal. On 2 October 2026 both were filled on every rate in the US, Japan, the UK, Germany and France, in English and in the site's own language.

priceListedPerNight and priceListedTotal are the crossed-out price a discounted rate is reduced from. At most one is filled, because a market's headline price is either nightly or for the stay and the listed figure follows it: the Japanese market fills the nightly one, the US market the total. Both are null on a rate with no discount.

Cancellation, sold-out stays and errors

Cancellation terms are stated when a rate is refundable and absent when it isn't. On a non-refundable rate isRefundable is false and cancellation is null, because the site says nothing there. It doesn't print "non-refundable".

A hotel and stay with nothing for sale writes no rows unless you turn on "Include sold-out stays", which gives you a single row with isSoldOut: true. A hotel the site never answered is written as a row with an error, and is never charged.

childrenAges echoes the ages you asked for and is an empty list when you asked for none.

Price-calendar rows

Calendar rows are marked isCalendarDay: true. Each has one date, its nightly price and whether it's available, with the same hotelId, market and link:

{
"id": "12570-cal-2026-10-07",
"brand": "expedia",
"market": "us",
"hotelId": 12570,
"date": "2026-10-07",
"pricePerNight": 388,
"priceText": "$388",
"currency": "USD",
"isAvailable": true,
"pricesIncludeTax": null,
"isCalendarDay": true,
"url": "https://www.expedia.com/en/h12570.Hotel-Information?chkin=2026-10-07&chkout=2026-10-07",
"scrapedAt": "2026-10-07T20:26:31.054Z"
}

pricesIncludeTax is null on calendar rows, because the calendar doesn't say.

How fast is it?

One request covers one hotel and one date range and returns the full set of rooms and rates. Several hotel-stays are fetched at the same time. Measured on Apify on 2 October 2026, on Tokyo hotels:

RunRowsRun time
1 hotel, 1 stay, 8 cheapest rates83 s (10 s once, on a slow connection)
6 hotels × 2 stays, every rate478 to 4978 to 9 s
10 hotels × 2 stays, every rate (18 priced, 2 sold out)5019 s
40 hotels × 2 stays, every rate1,63525 s

That's about 190 hotel-stays a minute on a larger run, and around 11,000 fit in the default one-hour run limit. There's no cap on rooms or rates. The limits you set are the only ones.

How much does it cost to price a hotel?

EventFree planStarterScaleBusiness
actor-start, once per run$0.002$0.002$0.002$0.002
hotel-checked, per hotel and date range the site answered$0.0005$0.0005$0.0005$0.0005
hotel-priced, per hotel and date range that came back with rates$0.003$0.0027$0.0024$0.0021
calendar-day, per price-calendar date, only with Include a price calendar on$0.0005$0.00045$0.0004$0.00035

Enterprise plans pay the Business price.

$3.50 per 1,000 hotel-stays on the Free plan, $2.60 on Business, with all rooms and rates in each. Proxy usage and platform usage are part of that price, and there's no proxy to set up.

  • 100 hotel-stays, all priced: $0.002 + 100 × $0.0005 + 100 × $0.003 = $0.352 ($0.262 on Business), for roughly 3,800 rows.
  • 1,000 hotel-stays, 900 priced and 100 sold out: $0.002 + 1,000 × $0.0005 + 900 × $0.003 = $3.202 ($2.392 on Business).
  • Ten hotels for one stay, with the price calendar on (around 217 dates each): $0.002 + 10 × ($0.0005 + $0.003) + 2,170 × $0.0005 = $1.122 ($0.7875 on Business).

Apify's Free plan includes $5 of credits a month. In runs of ten hotels that's about 1,350 priced hotel-stays a month for nothing.

A sold-out stay, and a stay skipped because its price hasn't changed, costs only the $0.0005 checking fee. It's a real answer that took real work, but it returned no rates. The same goes for a stay whose rates your filters all remove: it writes one row with an error saying how many rates were found, and pays the checking fee. A hotel the site never answered costs nothing at all.

Limitations

  • Nightly prices aren't comparable between markets. Compare on the stay total.
  • Star ratings aren't returned. The site doesn't publish a star class on this record.
  • Cancellation deadlines are text, not dates. They read as the site writes them ("Fully refundable before Tue, Nov 10"), which is what a guest is shown.
  • Prices are what the site shows an anonymous visitor. Member-only and logged-in rates aren't included.
  • Board is described in the site's own words, which vary by property ("No extras", "Breakfast included"). isMealIncluded turns that into a flag.
  • Pay-at-property rates are unconfirmed. paymentType is often null, and no pay-at-property rate has been seen in testing.
  • Hotels.com is available on the United States market only, where it answered 12 of 12 hotels in testing. Hotels.com Japan isn't supported: a run asking for it returns one error row saying so and costs nothing. Expedia Japan (expedia.co.jp) is fully supported, with the same property IDs.
  • A Hotels.com stay the site doesn't answer returns an error row, uncharged, that says Hotels.com didn't answer on this run. Run it again later.

FAQ

How do I compare the refundable and non-refundable price of the same room?

Group the rows by roomId. Each rate for that room is its own row with its own ratePlanId, isRefundable, cancellation and prices. If refundable rates are all you want, switch on "Refundable rates only".

Why is the cancellation column empty on some rates?

Those rates can't be cancelled. Expedia states the terms on a refundable rate and says nothing on the others, and the row mirrors that with isRefundable: false and a null cancellation.

Can I read Expedia and Hotels.com in one run?

Site sets which one bare property IDs are read from, and a pasted link overrides it. A list mixing Expedia and Hotels.com links is read correctly, each hotel from its own site, and brand on each row says which.

How do I get only what changed since yesterday?

Give "Price memory store" a name and turn on "Only changed prices". A hotel and stay whose cheapest price hasn't moved is skipped, and you pay the $0.0005 checking fee for it.

Which currency do the prices come back in?

The one the market quotes in: Japan JPY, the United States USD. A row is never labelled with money it isn't in. To compare one hotel across currencies, run it once per market. The property IDs are the same on each.

What does a sold-out hotel look like?

Nothing, by default: a sold-out stay writes no rows. That's an answer, not a failure. Turn on "Include sold-out stays" and you get one row per sold-out stay with isSoldOut: true, which lets a schedule see that the stay was checked.

Do I need proxies or an account?

No to both. There's no login, no cookie of yours is used, and proxy usage is included in the price.

The actor reads only pages Expedia and Hotels.com serve to a logged-out visitor, with no account access and no private data. Public data scraping is generally lawful in the UK, EU and US. How you use the rates, and whether that fits the site's own terms, is yours to check.

Automating it

For a rate monitor, schedule the run in Apify with the dates left empty and days ahead set, and the window moves with the schedule. Add a price memory store and each run reports only the hotels whose price moved, which a webhook can pass to Slack or a Google Sheet.

From your own code, one request starts a run. Your API token is under Integrations in Apify Console.

curl -X POST "https://api.apify.com/v2/acts/boncross~expedia-room-rates/runs?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hotels":["12570"],"checkIn":"2026-11-13","checkOut":"2026-11-15"}'

Agents reach it through the Apify MCP server at mcp.apify.com, under the name boncross/expedia-room-rates, and get the rate grid back as structured rows.

More from BonCross

Your feedback

If a rate on the Expedia page isn't in your rows, or a refundable flag looks wrong, open an issue on the Issues tab in Apify Console and include the hotel and dates. We read each one and reply there.