Booking.com Hotel Price Calendar Scraper avatar

Booking.com Hotel Price Calendar Scraper

Pricing

from $2.75 / 1,000 hotel dates

Go to Apify Store
Booking.com Hotel Price Calendar Scraper

Booking.com Hotel Price Calendar Scraper

Track a hotel and its comp set on Booking.com across every check-in date: nightly rate, stay total, pre-discount price, taxes, room type, cancellation policy, rooms left. Sold-out dates flagged; currency, language and tax regime verified in every response. Not affiliated with Booking.com.

Pricing

from $2.75 / 1,000 hotel dates

Rating

0.0

(0)

Developer

Aleksandr Kobtsev

Aleksandr Kobtsev

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 hours ago

Last modified

Share

Booking.com Hotel Price Calendar Scraper

Track your own property and your comp set on Booking.com across every check-in date in a range, and get prices and availability by date: nightly rate, total for the stay, pre-discount price, taxes, room type, cancellation and prepayment policy, and how many rooms are left. One dataset row per hotel and per date, up to 365 days ahead, in 12 currencies and 7 languages.

You bring the hotel page URLs. There is no login, no Booking.com account and no partner API involved, and this Actor does not search for hotels: it prices the ones you name. Run it once, put it on a schedule, or call it from the API, and forward the result to Google Sheets, Slack, Make, Zapier or an MCP client.

Who is this Booking.com scraper for?

You areWhat you runWhat it costs
A revenue manager watching a comp set10 hotel URLs, 90 dates, dailyabout $0.98 per run
A short-let or small hotel owneryour own property, 60 dates, dailyabout $7.82 per month
Building a rate-shopping or BI productany number of URLs through the API$3.00 per 1,000 hotel-dates
Researching travel pricesone pull, 365 dates$1.18

If you need to find hotels rather than price known ones, this is the wrong tool. Start from a Booking.com search scraper, then feed the URLs it returns into this one.

What makes this different from other Booking.com scrapers?

Most Booking.com Actors answer "which hotels are in this city, and what is the headline price today". They bill one row per property. This one answers "what does this property cost on the 10th, the 11th, the 12th of next month, and what were the terms", and it bills one row per hotel and date, with every rate on that date inside it.

  • It verifies before it bills. Booking.com switches currency and language to match the visitor's address. Every response is checked against what you asked for: the currency on every single rate, the language, and the check-in date the page echoes back. A page that answers for the wrong dates is rejected and never charged.
  • It tells sold-out from broken. Four page states are recognised on real pages: rooms with prices, genuinely sold out, the decoy page Booking.com serves to suspected bots, and a delisted property whose URL now redirects to a city page. Only the first two reach your dataset. The decoy is retried on a fresh session, the delisted URL is named in the run output, and neither is billed.
  • A run that collects nothing fails. It does not finish green with an empty dataset, which is the failure that costs you a day before you notice it.
  • It is watched. A scheduled job re-parses live Booking.com pages against a recorded baseline three times a day and alerts on a markup change, so a break surfaces in hours and not in your Issues tab a week later.
  • It survives layout changes without waiting for a release. Every CSS selector can be overridden from the input, for example {"price": ".new-price-class"}.

What data can you extract from a Booking.com hotel page?

One row per hotel and check-in date. The flat cheapest* fields repeat the cheapest rate so a CSV or Excel export stays readable; offers holds every rate the page showed.

Row fieldWhat it is
hotelUrl, hotelId, hotelNameThe property, as Booking.com identifies it. hotelId is the stable join key.
checkIn, checkOut, nightsThe stay this row priced.
adults, rooms, childrenAgesThe occupancy it was priced for. Change it and the prices change.
currency, languageConfirmed against the response, not just requested.
soldOuttrue when the property has no availability for that date. A real answer, and a billed row.
minPricePerNightThe headline number: cheapest rate divided by nights.
pricesIncludeTaxesWhether these prices already contain local tax. See the section below.
offersCountHow many rates the page offered. Typically 20 to 48.
cheapestRoomType, cheapestTotalPrice, cheapestStrikeoutPriceThe cheapest rate, flattened.
cheapestTaxesAndCharges, cheapestCancellationPolicy, cheapestPrepaymentPolicy, cheapestRoomsLeftIts terms, flattened.
offersEvery rate on the date. Fields below.
scrapedAtWhen it was collected, ISO-8601 UTC.

Each entry in offers carries roomType, totalPrice, pricePerNight, strikeoutPrice, currency, taxesAndCharges, cancellationPolicy, prepaymentPolicy, occupancy and roomsLeft.

{
"hotelUrl": "https://www.booking.com/hotel/ge/radisson-blu-batumi.html",
"hotelId": "320991",
"hotelName": "Radisson Blu Hotel Batumi",
"checkIn": "2026-11-10",
"checkOut": "2026-11-12",
"nights": 2,
"adults": 2,
"rooms": 1,
"childrenAges": [],
"currency": "USD",
"language": "en-gb",
"soldOut": false,
"minPricePerNight": 279.5,
"pricesIncludeTaxes": true,
"offersCount": 20,
"cheapestRoomType": "Premium Room",
"cheapestTotalPrice": 559,
"cheapestStrikeoutPrice": 621,
"cheapestTaxesAndCharges": "Includes taxes and charges",
"cheapestCancellationPolicy": "Non-refundable",
"cheapestPrepaymentPolicy": "Pay online",
"cheapestRoomsLeft": null,
"scrapedAt": "2026-11-01T09:14:02.113Z",
"offers": [
{
"roomType": "Premium Room",
"totalPrice": 559,
"strikeoutPrice": 621,
"pricePerNight": 279.5,
"currency": "USD",
"taxesAndCharges": "Includes taxes and charges",
"cancellationPolicy": "Non-refundable",
"prepaymentPolicy": "Pay online",
"occupancy": "Max persons: 2",
"roomsLeft": null
}
]
}

A sold-out date comes back with soldOut: true, offersCount: 0 and minPricePerNight: null, so a gap in your calendar means "no rooms", never "the scrape missed a day".

What to do with it. Put the run on a daily schedule, send the dataset to Sheets or BigQuery, and key rows on hotelId plus checkIn. Two columns then answer the morning question on their own: a change in minPricePerNight is a rate move, and a change in soldOut is your comp set filling up. scrapedAt keeps the history straight.

Do Booking.com prices include tax? 175 USD or 207 USD for the same room

Booking.com quotes a rate with or without local tax depending on the country it infers from the address the request came from. The gap is the whole VAT. Measured on one Georgian property, same date, same room, two runs minutes apart: 175 USD from one exit address and 207 USD from another, 18 percent apart, with 48 rates on the page both times.

Both are genuine Booking.com prices. The damage comes from mixing them without saying so, which is why every row states pricesIncludeTaxes.

On the default Apify datacenter proxies the flag reads false, and it reads false consistently, so a calendar built on them compares correctly with itself. If you need the tax-inclusive quote, run through your own proxies in the country you want to buy from. Booking.com takes the regime from the address and ignores the URL parameters that look like they should control it. That was verified rather than assumed: sb_price_type=total and cc1 both changed nothing.

How much does it cost to scrape Booking.com prices?

One billed row is one hotel on one check-in date, with every rate that property offered on it. That is the whole unit. Thirty dates are thirty rows on every property, always, so the bill is predictable before you start.

EventPriceWhen
Hotel date$0.003Per row that reaches the dataset
Actor start$0.02 per GBOnce per run. The Actor needs 4 GB, so $0.08
Result$0.00001Per stored item, a fraction of a cent on any real job

What that means for real jobs:

JobRowsTotal
One hotel, a month ahead30$0.17
One hotel, a quarter ahead90$0.35
One hotel, a year ahead365$1.18
Ten competitors, a quarter ahead900$2.79
One property, 60-date window, refreshed daily for a month1,800$7.82

The start charge is flat, so it dominates a tiny run and disappears on a large one: one date works out near $0.083 per row, thirty dates near $0.0057. Put many hotels into one run instead of starting a run per hotel.

Sold-out dates are charged, because "no rooms on that date" is a real answer and the reason people monitor availability. Nothing else is: not a blocked or challenged request, not a page in the wrong currency or language, and not a page the parser could not read. The $0.08 start charge is the exception, since it is taken before any of that is known.

Set Maximum total charge on the run to cap spending. The Actor stops cleanly at the budget and lists the dates it did not reach in the FAILED_CHUNKS key-value store record, so you can finish them later without paying twice for what you already have.

How this compares. Actors that return every rate per date bill per rate, not per date. For one hotel over 30 dates at roughly 34 rates a date, that is about 1,020 billable units against this Actor's 30. Priced on the Store today, the same job comes to roughly $3.06 and $4.08 at two of them, against $0.17 here. Actors that look cheaper per 1,000 are selling a search-listing row: one property, one headline price, no room types, no policies.

How to scrape Booking.com hotel prices by date

  1. Open the hotel on Booking.com and copy the page URL. Anything like https://www.booking.com/hotel/ge/radisson-blu-batumi.html works. Dates, language and tracking parameters already in the link are ignored.
  2. Paste one or more URLs into Hotel page URLs.
  3. Choose the first check-in date, how many dates to cover, nights per stay, guests, currency and language.
  4. Run it. Download the dataset as JSON, CSV or Excel, or read it through the API.
  5. For a price history, put the run on a daily schedule and keep every run's dataset.
FieldDefaultWhat it does
hotelUrlsrequiredBooking.com hotel page links, one per line.
startDatetomorrowFirst check-in date, YYYY-MM-DD (UTC). Today is allowed; a past date is moved to today.
days30Number of consecutive check-in dates, per hotel. Each hotel and date pair is one billable row, so 5 URLs over 30 dates is 150 rows.
nights1Length of stay for every check-in date.
adults2Adult guests. Occupancy changes prices, so compare like for like.
childrenAges[]Age of each child, e.g. [5, 9]. Booking.com prices children by age, so a count alone is not enough.
rooms1Rooms to price.
currencyUSDVerified in every response. A wrong currency means the date is rejected and not charged.
languageen-gbLanguage of room names and policies. Also verified.
datesConcurrency3Dates fetched in parallel per browser tab.
datesPerSession100Dates taken from one browser session before a fresh one starts. Measured stable at 100.
proxyApify datacenterMeasured on Booking.com: 10 of 10 dates, zero rejections. Apify residential groups are refused in the first seconds, before anything is charged. For a specific country, or a residential address, choose Custom proxies and supply your own. The country also decides pricesIncludeTaxes.
dryRunfalseOwner-only switch used by the maintainer's own monitoring. It is ignored for everyone else.
selectorOverrides{}Emergency hatch: map a selector name to a CSS selector, e.g. {"taxes": ".prd-taxes-new"}. Use it when the run log says a selector stopped matching.

How fast is it, and what does it not do?

Measured on the default Apify datacenter proxy: 14 dates for one hotel in 68 seconds, 30 dates in 250 seconds. A full year is therefore roughly 30 to 50 minutes per hotel, and ten hotels over a year is several hours. The run timeout ships at 3 hours, so raise it in the run options before a job that large and size it from the slow end of that range.

  • Prices are what Booking.com showed for that occupancy, currency and language at scrape time. They move constantly. Scrape on a schedule if you need a trend.
  • totalPrice is the price for the whole stay as displayed. pricePerNight is derived from it by dividing by nights.
  • taxesAndCharges, strikeoutPrice and occupancy are null when Booking.com does not print them. The tax line in particular depends on the point of sale, as described above.
  • roomsLeft is how many rooms the booking form lets you pick, and that list stops at nine. At the ceiling the field is null, because nine would read as an exact count and it is not one. Measured: half the rates on a live page sit at the ceiling.
  • datesPerSession has a floor of 10. Below it the hotel page is re-warmed for almost every date, which costs about eleven times the traffic and returns the same data.
  • English (US) is not offered. With the US locale Booking.com ignores the requested currency and prices the property in its local one, which would make every row unusable. Use English (UK). The room descriptions are identical.
  • Member-only Genius discounts need a logged-in session and are not included.
  • It does not collect reviews, photos, guest names or any personal data.

FAQ: scraping Booking.com hotel prices

Can I scrape several hotels at once? Yes, one URL per line. Each hotel gets its own browser session.

How far ahead can it price? Up to 365 check-in dates per hotel per run.

Do I need a Booking.com account or an API key? No. It reads publicly visible pages.

What if Booking.com changes the page? A scheduled watchdog re-parses live pages against a recorded baseline three times a day, so the break is usually known before you hit it. Parsing errors are reported in the log and never billed. You can also put the new selector into selectorOverrides and run again without waiting for a release.

What if a hotel URL is dead? Delisted properties redirect to a city page. That is detected on the first attempt, the URL is skipped without retries and named in the FAILED_CHUNKS key-value store record.

Does it work for apartments and hostels? Yes. Prices and policies are verified on hotels, hostels and guest houses in Georgia, Czechia and India. On apartments the parser is verified on a sold-out date in Spain, which proves the availability path but not the rate table. Booking.com serves the same room markup for all of them, so apartments are expected to work. If one does not, open an issue with the URL.

Can I get a currency that is not in the list? Not yet. The twelve supported currencies are the ones whose price notation on Booking.com has been verified, which is what makes a wrong-currency response catchable. Ask in Issues if you need another.

Is it legal to scrape Booking.com? This is an unofficial tool, not affiliated with, endorsed by or connected to Booking.com or Booking Holdings Inc. It reads publicly visible pages and collects no personal data. Booking.com's terms of service restrict automated access, and you are responsible for making sure your use complies with those terms, with the law in your jurisdiction and with the site's robots.txt. See Apify's stance on web scraping legality.

Found a problem?

Open an issue on the Issues tab with the hotel URL and the run ID. Breakages in Booking.com markup are the expected failure mode of any scraper, and this one is instrumented to catch them early. Reports get read.