Booking.com Room-Level Hotel Price Scraper avatar

Booking.com Room-Level Hotel Price Scraper

Pricing

from $5.00 / 1,000 results

Go to Apify Store
Booking.com Room-Level Hotel Price Scraper

Booking.com Room-Level Hotel Price Scraper

Scrape and track Booking.com hotel prices by room type and date, up to 365 days ahead. Extract rate options, availability, sold-out dates, meal plans, discounts, refundable rates, and geo-pricing. Built for hotel revenue management and competitive rate analysis. Export JSON, CSV, Excel, or HTML.

Pricing

from $5.00 / 1,000 results

Rating

5.0

(2)

Developer

HotelSignals Lab

HotelSignals Lab

Maintained by Community

Actor stats

10

Bookmarked

312

Total users

52

Monthly active users

13 days ago

Last modified

Share

🏨 Booking.com Price Tracker — Room-Level Hotel Price Scraper

Extract complete room-level pricing from any Booking.com hotel — every room type, every rate option, every date — up to 365 days ahead.

Most Booking.com scrapers on Apify Store return one price per hotel. This Booking.com hotel price scraper goes deeper: it opens each property page and extracts granular room-by-room data — prices, rate options, availability signals, meal plans, discounts, facilities, and sold-out detection. Whether you search for a Booking.com price tracker, a Booking.com scraper for hotel prices, or a hotel room price API, this Actor covers all three: one tool, room-level output.

One run = complete pricing visibility for your competitive set.

💡 Paste hotel URLs, set your date range, hit Start. Results ready in JSON, CSV, Excel, or HTML.


🎯 Who is this Booking.com scraper for?

  • Revenue managers monitoring competitor hotel pricing daily
  • Hotel operators tracking market positioning at the room-type level
  • Travel tech companies feeding structured Booking.com pricing data into dashboards or algorithms
  • Market analysts studying seasonal hotel demand and pricing trends
  • Consultants building competitive intelligence reports for hospitality clients

Search terms this Actor answers: Booking.com price scraper, Booking.com room price tracker, hotel price scraper, hotel room availability scraper, Booking.com API alternative, hotel competitor rate tracking.


📥 Input

FieldTypeRequiredDefaultDescription
hotelUrlsarrayBooking.com hotel URLs (direct mode)
searchUrlstringA Booking.com search results URL — scrape every hotel matching your filters instead of listing URLs one by one
searchTextstringFree-text destination search (e.g. "riad with pool in fes") — the Actor builds the Booking.com search URL for you
daysAheadinteger10Number of days to scrape (1–365)
startDatestringTodayStart date in YYYY-MM-DD format
currencystringUSD23 currencies: USD, EUR, GBP, CNY, MAD, AED, SAR, CAD, AUD, JPY, KRW, CHF, PLN, BRL, INR, SGD, HKD, TWD, THB, MXN, ZAR, COP, IDR
adults / children / roomsinteger2 / 0 / 1Occupancy for the price search
includeSoldOutbooleantrueInclude sold-out dates in results
includeWholesaleRatesbooleanfalseCapture partner/wholesale rates (bbasic), tagged separately as is_wholesale
proxyCountriesarray["US"]Multi-market override. Leave empty to use the single country from proxyConfiguration. Fill it to scrape the same hotels once per country and compare geo-pricing — each result is tagged with proxy_country. Any code your Apify proxy offers, not a fixed list (FR, US, GB, DE, ES, IT, NL, BE, PL, CH, CN, HK, SG, JP, KR, AE, SA, MA, IN, ID, AU, CA, BR…). A code your plan lacks falls back to a weaker proxy, with a warning in the log
concurrencyinteger3How many hotels to scrape in parallel (1–5). Each hotel gets its own proxy session
delayBetweenRequestsinteger3Seconds between page loads (anti-blocking)
proxyConfigurationobjectResidentialStandard Apify proxy picker (Datacenter / Residential / Own proxies / No proxy). Residential is strongly recommended — Booking.com blocks datacenter and shared IPs within a request or two. The country selected here is used only when proxyCountries is empty
useApifyProxybooleantrueDeprecated, hidden from the form. proxyConfiguration replaces it and is read first. Still honoured when proxyConfiguration is absent, so saved tasks and API callers sending this key keep working — including false to run with no proxy
maxPropertiesinteger50Cap on how many hotels to take from searchUrl / searchText discovery
propertyTypestringFilter discovery by property type. Applies to destination search only, not to searchUrl
minReviewScore / maxReviewScorenumber0 / 0Filter discovered hotels by review score (0–10). 0 on either side means "no limit"
includeLosAnalysisbooleanfalseExtract multi-night pricing to detect length-of-stay discounts — adds los_prices and has_los_discount to every row
tieredCadencebooleanfalseCost saver: scrape near dates daily, mid dates every 2 days, far dates every 3. Cuts cost ~50% — best for your own ongoing monitoring rather than a one-off export
webhookUrlstringSlack/Discord webhook for error alerts

Example input

{
"hotelUrls": [
"https://www.booking.com/hotel/ma/savoy-le-grand.html",
"https://www.booking.com/hotel/ma/longue-vie-hotels.html"
],
"daysAhead": 30,
"currency": "USD",
"adults": 2,
"rooms": 1,
"includeSoldOut": true,
"useApifyProxy": true,
"proxyCountries": ["FR", "US", "GB"]
}

💡 Tip: You only need the base hotel URL — query parameters like ?checkin=... are automatically handled. Works with any Booking.com property worldwide: hotels, riads, apartments, resorts, hostels, guesthouses.

💡 Multi-country tip: Booking.com applies geo-pricing based on visitor location. Scraping from multiple proxyCountries (e.g. ["US", "GB", "FR"]) reveals how the same room is priced for different source markets. Keep currency fixed across countries so the difference you see is the geo-pricing cohort, not a currency conversion.

💡 Why US + USD by default: they match. Booking.com shows a French visitor prices in EUR, so a FR proxy asking for USD returns a converted number — exactly the conversion artifact the tip above warns about. Pair your proxy country with its native currency (FREUR, GBGBP, CNCNY) when you want the price a local actually sees.

⚠️ About the proxy: Residential is the default for a reason — Booking.com actively rate-limits and blocks datacenter/shared IPs. Turning it off to save cost will very likely produce 0 results on any run beyond a couple of pages.


📤 Output

Each result represents one room type for one date. A hotel with 8 room types scraped across 30 days produces ~240 results — a complete pricing matrix.

Three views, one dataset

The room dataset ships with three table views. They are projections over the exact same records — no view filters, reduces, or duplicates the data. Pick the tab that matches what you're doing:

ViewWho it's forWhat it shows
Rate shopping (default)Revenue managers, competitor monitoring21 columns: price per room, per date, per proxy country — net rate, taxes and gross side by side, plus sold-out state, availability, cancellation terms and the capture timestamp so runs stack into a time series
Room comparisonTravelers picking a room23 columns: room attributes a guest actually chooses on — size, bed, view, private bathroom, amenities, review score — next to price and cancellation terms, with sold-out state shown before the price so a blank is explained
AI room pickerAI agents and automations41 columns: full provenance and request context — hotel ID, hotel URL, proxy country, capture time, requested occupancy, room ID, canonical prices, price_basis, occupancy_match_status, availability, stock_confidence and the occupancy-matched primary_rate_option — without the facilities / rate_options bulk that blows an LLM context window

Nothing is hidden. The complete schema — rate_options, benchmark_rate_option, occupancy context, the full facilities list, hotel context, all legacy price fields — is always available under the All fields tab, in the CSV/Excel exports, and at the plain API items endpoint. Add ?view=rate_shopping, ?view=room_comparison or ?view=ai_room_picker to that endpoint to get a specific projection instead:

https://api.apify.com/v2/datasets/<datasetId>/items?view=ai_room_picker

One note on the Rate shopping view specifically: the legacy price_public / discount_percent fields are deliberately not in it. They come from the fixed 2-adult benchmark path, so placing them beside nightly_total_price — which is occupancy-matched — would invite a discount calculation across two different rate options. Both remain available under All fields, and they will return to the view once they are derived from the same selected primary block.


🔍 Understanding key fields

price vs. price_primary vs. price_public

All three are legacy fields computed against a fixed 2-adult benchmark, regardless of the occupancy you searched for. They are kept unchanged for backward compatibility.

FieldWhat it isWhen to use
priceAbsolute cheapest option (may be 1-person or restricted)Quick market scan
price_primaryBest price for standard 2-guest occupancyA fixed 2-adult benchmark across hotels
price_publicNon-Genius public rate at 2-adult occupancy (strips Genius discounts)Comparing like-for-like against a published rack rate
nightly_total_priceThe rate matched to YOUR requested occupancyEverything else — this is the one to compare on

💡 Which should you actually use? If you searched for the occupancy you care about, use nightly_total_price (and primary_rate_option for its full context). Reach for price_public only when you deliberately want a fixed 2-adult yardstick that is comparable across properties and unaffected by Genius discounts (10–20%).

⚠️ Do not mix the two families. price_public and discount_percent come from the 2-adult benchmark path; nightly_total_price comes from your occupancy-matched rate. Computing (price_public − nightly_total_price) / price_public compares two different rate options and produces a meaningless discount. discount_percent is already calculated for you — against original_price, within the benchmark family. This is exactly why both are left out of the Rate shopping view; they will return to it once they are derived from the same selected primary block.

proxy_country — geo-pricing comparison

Booking.com prices the same room differently depending on the visitor's location. The proxy_country field tells you which source market each result was scraped from. Running multiple proxyCountries in one job lets you compare cohorts side by side — e.g. how a French visitor, a US visitor, and a UK visitor each see the same property.

availability_status

Since v2.5.0 the tier depends only on the room count, so it is monotonic — 4 rooms left is never reported as less urgent than 5. (Before v2.5.0 urgency keyed off whether Booking.com happened to render a scarcity banner, which produced exactly that inversion.)

StatusRuleRevenue insight
sold_outis_sold_out is true, or a confirmed zero countPeak demand — opportunity to optimize your own rates
low_availability_urgent1–3 rooms leftHigh demand — act on pricing
low_availability4–5 rooms leftDemand is building
available6+ rooms left, or the count is hiddenNormal conditions, or simply no signal — check stock_confidence before reading anything into it

⚠️ sold_out is never inferred from a missing price or a missing count — it comes only from the explicit is_sold_out flag. And whether Booking.com renders a scarcity banner now affects stock_confidence only (how much to trust the number), never the urgency tier.

stock_confidence

ValueSourceReliability
exactDOM scarcity badge visible on page, carrying a numberHigh — Booking.com shows this to create urgency
cappedrooms_left = 10 from internal JSONMedium — actual stock is ≥ 10, Booking caps its own counter
inferredA count came from the page JSON or the room registry, but no scarcity badge was shown for itMedium — the number is real but Booking didn't surface it
hiddenrooms_left = null — no count from any sourceNone — treat availability as unknown, not comfortable

⚠️ Before v2.5.0, inferred was only used on sold-out rows, so any row with a count but no badge was labelled hidden — producing output that read rooms_left: 3 beside stock_confidence: "hidden" in the same row. hidden now means there is genuinely no count.

facilities vs. room_amenities

facilities is the complete list — every feature Booking.com shows for the room (30–40+ items). room_amenities is premium features only — the differentiators that affect pricing (balcony, minibar, soundproofing, spa bath).


⚠️ Limitations & known caveats

Stated plainly, because the Actor's whole contract is "never guess" — knowing where the data stops is part of trusting the data that's there.

  • One night per record. Each row prices a single night (number_of_nights is normally 1). Multi-night rates are not modelled unless you enable includeLosAnalysis, which adds los_prices.
  • Child ages are not collected. When children > 0, true rate capacity cannot be fully verified, and occupancy_match_status honestly reports uncertain_children rather than pretending otherwise.
  • rooms_left is capped at 10 by Booking.com, not by this Actor. A 10 usually means "10 or more" — that is what stock_confidence: capped is telling you. A null means the count is hidden, not that inventory is comfortable.
  • nightly_base_price is null when Booking.com exposes no tax breakdown. It is never back-computed from an assumed tax rate.
  • Booking.com A/B tests prices and layouts. Two runs minutes apart from the same market can legitimately differ. scrape_timestamp and proxy_country are on every row so you can tell a real price move from a cohort difference.
  • price / price_primary / price_public / original_price / discount_percent are a fixed 2-adult benchmark, not your requested occupancy. See Understanding key fields.
  • Sold-out rows carry fewer fields. Static room attributes (bed_type, room_size_sqm, room_view, room_amenities) are not currently emitted on sold-out rows, so those columns render blank there. is_sold_out and availability_status are shown before the price in both room-facing views so a blank is never unexplained.
  • A proxy is effectively required. Without useApifyProxy, Booking.com blocks most sessions after 1–2 requests.

Fields under investigation. These came in as a bug report from a diverged copy of this scraper and are not confirmed here — Booking.com's markup could not be inspected live during the fix session, so they were deliberately left unpatched rather than changed on a guess:

  • hotel_neighborhood — reported as always null. One of this Actor's selectors is a hashed CSS-module class (span.f419a93f12), which is exactly the kind that stops matching when Booking redeploys. The breadcrumb trail is a likely sturdier source. Needs a live listing to confirm before changing.
  • min_stay — reported as always null, but may simply be correct: most dates have no minimum-stay restriction. Needs a listing that visibly shows one.
  • book_now_pay_later — reported as always false. The strict === 1 JSON comparison has been widened to also accept true / "1" (a change that can only ever turn a false into a true), but whether the underlying key is right is unverified.
  • taxes_and_fees inside individual rate_options entries — reported as always null, likely a stale DOM selector. Low impact: the top-level taxes_and_fees_total is computed from the JSON-backed taxes_included / taxes_excluded and is unaffected.

💡 Tips for best results

  • Start with 7–30 days to validate your setup, then scale to 365
  • Keep useApifyProxy: true — without it, Booking.com blocks most runs after 1–2 requests
  • Schedule daily runs with Apify Scheduler — hotel prices change constantly
  • Monitor up to 10 hotels per run for optimal speed and reliability
  • Compare on nightly_total_price — it matches the occupancy you searched for. Use the legacy price_public only when you want a fixed 2-adult yardstick, and never compute a discount across the two families (see Understanding key fields)
  • Compare proxyCountries to see geo-pricing differences across your source markets — keep currency fixed for a clean comparison

🔗 Integrations

IntegrationUse case
Apify SchedulerAutomate daily/weekly Booking.com price monitoring
WebhooksGet notified when a run completes
APIPull data programmatically (see API tab)
Google SheetsDirect export for quick analysis
n8n / Make / ZapierNo-code automation pipelines
JSON / CSV / Excel / HTMLDownload in any format

🚀 Use cases

Daily competitor monitoring — Track what your competitive set charges across every room type, every day, using this Booking.com hotel price tracker as a standing job.

Revenue management — Feed room-level market data into pricing decisions. Compare your rates against competitors using matching rate options (same occupancy, same cancellation policy, same meal plan).

Geo-pricing analysis — Scrape the same properties from multiple proxyCountries to see how Booking.com prices rooms for visitors from different source markets.

Room-type matching — Use facilities, room_size_sqm, and room_view to match comparable rooms across different hotels.

Seasonal trend analysis — Scrape 365 days to visualize demand curves, identify shoulder seasons, and plan promotions around events and holidays.

Portfolio benchmarking — Manage multiple properties? Compare each one against its local market at the room-type level.

Travel platform data feeds — Power booking engines, metasearch tools, or dashboards with structured, validated Booking.com pricing data.


❓ FAQ

Does this Booking.com scraper work with any property? Yes — any hotel, resort, riad, apartment, or boutique property listed on Booking.com, in any country. Works with small riads (5 rooms) and large hotels (100+ rooms).

What's the difference between this and other Booking.com price scrapers? Most Booking.com scrapers on Apify Store return one listing-level price per hotel (like a search-results snapshot). This Actor is room-level: every room type, every rate option, every date, with sold-out detection and geo-pricing — built for revenue management, not just a price check.

What currencies are supported? 23 currencies, including USD, EUR, GBP, CNY, JPY, KRW, CHF, PLN, MAD, AED, SAR, INR, SGD, HKD, TWD, THB, IDR, BRL, MXN, ZAR. Keep it fixed across proxyCountries so what you're comparing is the geo-pricing cohort, not a currency conversion.

What does rooms_left: null mean? It means unknown, not "plenty available". Booking.com simply isn't exposing a stock number for this room — stock_confidence will read hidden. The room is still bookable (that's what is_sold_out: false tells you), but don't infer a comfortable inventory level from a null. Likewise rooms_left: 10 usually means "10 or more" — Booking.com caps its own counter, which is why stock_confidence reads capped there.

Why are sold-out dates valuable? Sold-out dates reveal peak demand periods. If a competitor is fully booked, that's a high-demand window where you can optimize your own rates.

What are Genius rates and why does price_public matter? Booking.com Genius gives 10–20% discounts to frequent bookers. price_public always shows the non-Genius price at a fixed 2-adult occupancy, which makes it a stable yardstick across properties. Note it is a legacy benchmark field: it ignores the occupancy you searched for, so it is not interchangeable with nightly_total_price and the two must never be subtracted from one another. If you want Genius status on the rate you'd actually book, read primary_rate_option.is_genius.

What are bbasic rates and can I include them? Booking.com sometimes shows "Tarif Prestataire" (partner/wholesale rates) from third-party resellers. Skipped by default; set includeWholesaleRates: true to capture them, tagged with is_wholesale: true.

Can I scrape prices as they appear in China / Hong Kong / Poland / anywhere else? Yes. proxyCountries is not a fixed whitelist — the code you enter is passed straight to Apify Residential proxy, so any country it offers works, CN included. CNY is available as a currency too. If a country isn't on your Apify plan the run falls back to an un-targeted residential IP and says so in the log, rather than failing.

Do I need a proxy? Yes, for anything beyond a quick test. useApifyProxy: true is the default and strongly recommended — Booking.com blocks datacenter/shared IPs quickly.

How often should I run this? For active revenue management, daily runs are recommended — Booking.com prices and availability change frequently, sometimes multiple times a day.

Can I scrape more than 10 hotels? For optimal reliability, each run handles up to 10 hotels directly, or use searchUrl/searchText with maxProperties for broader discovery. For very large sets, schedule multiple runs or batch via the Apify API.


📝 Changelog


💬 Support & feature requests

Have a question, found a bug, or want a new feature? → Open an issue on the Issues tab.

Want a custom solution for your revenue management needs? → Reach out via Issues.


Built by NoraView Intelligence — AI-powered revenue management for independent hotels.

Booking.com price scraper · room-level price tracker · hotel rate scraper · sold-out and availability tracking · Genius rate detection · geo-pricing across multiple countries · CSV, JSON, HTML, Excel output.