Booking.com Room-Level Hotel Price Scraper
Pricing
from $5.00 / 1,000 results
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
Maintained by CommunityActor stats
10
Bookmarked
312
Total users
52
Monthly active users
13 days ago
Last modified
Categories
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
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
hotelUrls | array | ✅ | — | Booking.com hotel URLs (direct mode) |
searchUrl | string | ❌ | — | A Booking.com search results URL — scrape every hotel matching your filters instead of listing URLs one by one |
searchText | string | ❌ | — | Free-text destination search (e.g. "riad with pool in fes") — the Actor builds the Booking.com search URL for you |
daysAhead | integer | ❌ | 10 | Number of days to scrape (1–365) |
startDate | string | ❌ | Today | Start date in YYYY-MM-DD format |
currency | string | ❌ | USD | 23 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 / rooms | integer | ❌ | 2 / 0 / 1 | Occupancy for the price search |
includeSoldOut | boolean | ❌ | true | Include sold-out dates in results |
includeWholesaleRates | boolean | ❌ | false | Capture partner/wholesale rates (bbasic), tagged separately as is_wholesale |
proxyCountries | array | ❌ | ["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 |
concurrency | integer | ❌ | 3 | How many hotels to scrape in parallel (1–5). Each hotel gets its own proxy session |
delayBetweenRequests | integer | ❌ | 3 | Seconds between page loads (anti-blocking) |
proxyConfiguration | object | ❌ | Residential | Standard 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 |
useApifyProxy | boolean | ❌ | true | Deprecated, 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 |
maxProperties | integer | ❌ | 50 | Cap on how many hotels to take from searchUrl / searchText discovery |
propertyType | string | ❌ | — | Filter discovery by property type. Applies to destination search only, not to searchUrl |
minReviewScore / maxReviewScore | number | ❌ | 0 / 0 | Filter discovered hotels by review score (0–10). 0 on either side means "no limit" |
includeLosAnalysis | boolean | ❌ | false | Extract multi-night pricing to detect length-of-stay discounts — adds los_prices and has_los_discount to every row |
tieredCadence | boolean | ❌ | false | Cost 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 |
webhookUrl | string | ❌ | — | Slack/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 (FR→EUR, GB→GBP, CN→CNY) 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:
| View | Who it's for | What it shows |
|---|---|---|
| Rate shopping (default) | Revenue managers, competitor monitoring | 21 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 comparison | Travelers picking a room | 23 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 picker | AI agents and automations | 41 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.
| Field | What it is | When to use |
|---|---|---|
price | Absolute cheapest option (may be 1-person or restricted) | Quick market scan |
price_primary | Best price for standard 2-guest occupancy | A fixed 2-adult benchmark across hotels |
price_public | Non-Genius public rate at 2-adult occupancy (strips Genius discounts) | Comparing like-for-like against a published rack rate |
nightly_total_price | The rate matched to YOUR requested occupancy | Everything 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.)
| Status | Rule | Revenue insight |
|---|---|---|
sold_out | is_sold_out is true, or a confirmed zero count | Peak demand — opportunity to optimize your own rates |
low_availability_urgent | 1–3 rooms left | High demand — act on pricing |
low_availability | 4–5 rooms left | Demand is building |
available | 6+ rooms left, or the count is hidden | Normal 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
| Value | Source | Reliability |
|---|---|---|
exact | DOM scarcity badge visible on page, carrying a number | High — Booking.com shows this to create urgency |
capped | rooms_left = 10 from internal JSON | Medium — actual stock is ≥ 10, Booking caps its own counter |
inferred | A count came from the page JSON or the room registry, but no scarcity badge was shown for it | Medium — the number is real but Booking didn't surface it |
hidden | rooms_left = null — no count from any source | None — 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_nightsis normally1). Multi-night rates are not modelled unless you enableincludeLosAnalysis, which addslos_prices. - Child ages are not collected. When
children > 0, true rate capacity cannot be fully verified, andoccupancy_match_statushonestly reportsuncertain_childrenrather than pretending otherwise. rooms_leftis capped at 10 by Booking.com, not by this Actor. A10usually means "10 or more" — that is whatstock_confidence: cappedis telling you. Anullmeans the count is hidden, not that inventory is comfortable.nightly_base_priceisnullwhen 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_timestampandproxy_countryare on every row so you can tell a real price move from a cohort difference. price/price_primary/price_public/original_price/discount_percentare 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_outandavailability_statusare 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=== 1JSON comparison has been widened to also accepttrue/"1"(a change that can only ever turn a false into a true), but whether the underlying key is right is unverified.taxes_and_feesinside individualrate_optionsentries — reported as always null, likely a stale DOM selector. Low impact: the top-leveltaxes_and_fees_totalis computed from the JSON-backedtaxes_included/taxes_excludedand 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 legacyprice_publiconly when you want a fixed 2-adult yardstick, and never compute a discount across the two families (see Understanding key fields) - Compare
proxyCountriesto see geo-pricing differences across your source markets — keepcurrencyfixed for a clean comparison
🔗 Integrations
| Integration | Use case |
|---|---|
| Apify Scheduler | Automate daily/weekly Booking.com price monitoring |
| Webhooks | Get notified when a run completes |
| API | Pull data programmatically (see API tab) |
| Google Sheets | Direct export for quick analysis |
| n8n / Make / Zapier | No-code automation pipelines |
| JSON / CSV / Excel / HTML | Download 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.