Booking.com Price Scraper & Room Rate Tracker
Pricing
from $3.50 / 1,000 results
Booking.com Price Scraper & Room Rate Tracker
Track Booking.com room prices by date. Compare competitor hotels, monitor daily price changes and receive a digest. Get selected and cheapest rates, reported taxes, cancellation terms and availability. One-night quotes for revenue managers and developers. Export JSON, CSV or Excel.
Pricing
from $3.50 / 1,000 results
Rating
5.0
(2)
Developer
HotelSignals Lab
Maintained by CommunityActor stats
10
Bookmarked
345
Total users
36
Monthly active users
2 days ago
Last modified
Categories
Share
🏨 What this Actor does
Track Booking.com hotel prices room by room: one row per room type, per night, for every property you give it, with the price, what that price covers, the booking terms, and whether the room can still be booked.
Put your hotel and its competitors in one run and it also ranks you against them night by night, remembers yesterday, and can email you the morning read.
A value Booking.com does not show stays empty (null); it is never filled with a guess. Ranks, medians and labels are computed from what was read, and the field reference says how.
Used this Actor before September 2026? Nothing you use was removed. See what changed.
⚡ Quick start
Start small, see the output, then grow it.
- Paste this input and run it. Three Perth motels, three nights, one adult: about 50 rows in a few minutes.
- Swap in your own hotels. Your hotel and its competitors in Hotel URLs, and your hotel again in My hotel URL. 7–14 nights is a good daily window.
- Schedule it daily, at the same hour. Use Actor → Schedule, or the Schedule this run bar above your results. The change columns and the competitor moves compare each run with the previous one, so the first run has nothing to compare with.
{"hotelUrls": [{ "url": "https://www.booking.com/hotel/au/flag-motor-lodge.html" },{ "url": "https://www.booking.com/hotel/au/great-eastern-motor-lodge.html" },{ "url": "https://www.booking.com/hotel/au/ibis-budget-perth-airport.html" }],"myHotelUrl": "https://www.booking.com/hotel/au/flag-motor-lodge.html","daysAhead": 3,"adults": 1,"rooms": 1,"currency": "AUD"}
⚠️ Put your hotel and its competitors in the same run. The ranking compares the properties inside one run. Scrape them one at a time and every rank, median and gap comes back empty.
⭐ Start here: the 12 fields that matter most
Every example below comes from a real run on four Perth airport motels, 25 September 2026, one adult, prices in AUD.
| Field | What it tells you | Example |
|---|---|---|
nightly_total_price | The price Booking.com shows for the rate that fits your party. Compare on this one, and read tax_clarity_label beside it. | 217 (rounded from 217.27; with the 21.73 reported tax on top: 239.00) |
lowest_bookable_rate | The cheapest rate your party can book for this room, whatever its terms: the price a guest sees first. | 213, beside a selected 235 on the same room |
tax_clarity_label | Whether that price is the final cost. Only ✅ means final. | ⚠️ Additional taxes reported |
availability_label | Availability at a glance, with a colour. | 🔴 Only 2 rooms left · ⚫ Sold out |
rooms_left + stock_confidence | Rooms left, only when Booking.com states it, and how far to trust that number. | 2 + exact · null + hidden |
booking_terms_summary | Cancellation, breakfast and payment in one line. | Free cancellation · Breakfast not included · Pay now |
rate_selection_reason | Why this rate was picked, and which cheaper one was not. | …a cheaper non-refundable rate (180) was available but not selected… |
price_move_label | What the price did since the last run. | ▲ +191 (+82.68%) |
is_my_hotel | true on every row of your own property. | true |
lead_in_rate (Market by date) | A property's cheapest bookable rate that night. The ranking is built on it. | 217 |
compset_rank / gap_to_market_median (Market by date) | Where the property sits that night: 1 = cheapest. | 2 of 4 · -50 |
demand_pressure (Market by date) | How many of the property's room types Booking.com shows unavailable that night, as a level. | high — "5 of 6 room types unavailable" |
📬 The daily digest
The morning read of a run, built only from that run's own figures. This is the real one from the Perth run of 25 September, shortened:
Booking.com rate shop · Fri 25 Sep4 properties · 10 nights (Fri 25 Sep – Sun 04 Oct) · 1 adult · AUDPrices are before tax, as Booking shows them from US: the guest pays the tax on top.👉 Wed 30 Sep: you are 100 under your competitors' median (217 vs 317).YOUR HOTEL — Flag Motor Lodge• Cheapest on 0 of 4 ranked nights; most expensive on 1.• Above the market Sat 03 Oct: 199 vs competitors' median 181 (+18).• Below the market Wed 30 Sep: 217 vs competitors' median 317 (−100).• You are sold out: Fri 25 Sep, Sat 26 Sep, Sun 27 Sep, Mon 28 Sep, Tue 29 Sep, Thu 01 Oct.NEXT NIGHTS — lead-in (cheapest bookable rate)• Mon 28 Sep · cheapest 180 (ibis Budget Perth Airport) · competitors' median 272 · you: sold out · 1 of 4 sold out• Wed 30 Sep · cheapest 180 (ibis Budget Perth Airport) · competitors' median 317 · you 217 (#2 of 4)NIGHTS TO WATCH — the market is filling• Sun 27 Sep: 3 of 3 competitors sold outCOMPETITOR MOVES SINCE THE LAST RUN🟢 Great Eastern Motor Lodge · Fri 25 Sep: Superior Double or Twin Suite back on sale at 317 (249 before it sold out)⚫ Sanno Marracoonda Perth Airport Hotel · Fri 25 Sep: King Room sold outRun: ✅ 40 of 40 pages, 240 rows.
| Part | What it says |
|---|---|
| Header | The hotel's own date, how many properties and nights, the party and the currency. A line under it says when the prices are before tax. |
| 👉 The one line | The strongest signal of the run: a night where most competitors are sold out while you still sell, otherwise the night you sit furthest under your competitors, otherwise furthest over. |
| Your hotel | Your position against your competitors' median, your own rate left out: a median that includes you moves when you move. It needs two competitor rates. |
| Next nights | The next 7 nights: the cheapest property, the competitors' median, your rate and rank, and how many are sold out. A night with a property that could not be read says so before its numbers. |
| Nights to watch | Nights where half or more of your competitors have nothing left to sell. |
| Competitor moves | Price moves, sell-outs and rooms back on sale since the last run. A room back on sale is shown as a return, with the price it had before it sold out. |
| ⚠️ Run | Only when pages failed: how many, which hotel and which nights, and what to do. |
Getting it
- By email: fill
digestEmail. On plans where Apify refuses to start its Send Email Actor from inside a run, the log says "Digest email NOT sent". - Anywhere, on any plan: put an n8n, Make or Zapier webhook in
digestWebhookUrland send the email from there. The JSON carries the text and every figure in structured form. - Always: the digest is also saved with the run, as
DIGEST(text) andDIGEST_DATA(JSON), and linked on the Output tab.
💰 Pricing
You pay per result: $3.50 to $5.00 per 1,000 rows, depending on your Apify plan, plus Apify platform usage. Every row is a result, sold-out rows included.
The Perth example, 4 motels × 10 nights, wrote 240 rows: $0.84 to $1.20 in result fees for the run, platform usage not included. The Pricing tab shows the price for your plan. To pay for fewer rows:
- price fewer nights (
daysAhead); - turn on
tieredCadencefor long horizons; - turn off
includeSoldOut, knowing you lose the demand read.
📦 What you get
| Output | Where to find it | One record per | Use it for |
|---|---|---|---|
| Room rows | Output tab: Rate shopping, Rate shopping V2, Full detail, CSV, Excel | room type × night × proxy country | Every price, term and availability, room by room |
| Market by date | Output tab: Market by date (market_summary dataset) | property × night | Your rank, the median, the gaps, and the demand read |
| Daily digest | Email or webhook, and DIGEST (text) / DIGEST_DATA (JSON) in the run's key-value store | run | The morning read: one screen of text |
| Hotel metadata | hotel_metadata dataset | property × run | The full hotel snapshot: reviews, description, every facility |
| Run report | RUN_REPORT in the run's key-value store, and the run's status line | run | What worked, what did not, and whose fault it was |
The room rows come in four views of the same data:
| View | Fields | For | Through the API |
|---|---|---|---|
| Rate shopping | 25 | The working screen: price, tax, terms, availability | ?view=rate_shopping |
| Rate shopping V2 | 52 | The daily screen: adds the movement since the last run, the lead-in rate and how many rooms are still bookable | ?view=rate_shopping_v2 |
| AI input (compact) | 41 | Handing the data to an AI model | ?view=ai_input |
| Full detail | 104 | Every field, in reading order | ?view=full_detail |
| Room comparison (legacy) | 27 | Kept so API calls made before the update keep working | ?view=room_comparison |
| AI room picker (legacy) | 47 | Kept so API calls made before the update keep working | ?view=ai_room_picker |
Add the view to the dataset's items URL, for example …/datasets/<id>/items?view=rate_shopping_v2. The Download as CSV / Excel links on the Output tab carry every field, with the rate plans flattened into columns. A download made from a view carries that view's columns only.
🔍 How to read the data
nullis never 0. An empty price, count or tax means Booking.com did not state it. It is never filled with a guess.- The price is what Booking.com shows from the proxy country. Booking.com shows US visitors the price before tax, with the tax on top. Elsewhere it depends on the country and the property. On the Perth runs every rate was 10 % under its total with the reported tax:
217.27(shown as 217) +21.73=239.00.tax_clarity_labelon each row andtaxes_on_topon each night say which one you are looking at. - Booking.com prices by visitor country. The same room can cost more or less for a French visitor than for a US one. Compare rows from the same
proxy_country, with the samecurrency. To see prices closer to what local guests see, load the pages from the hotel's own country, in its currency. - One night per row. Multi-night stays are not quoted.
- Genius rates are flagged, not hidden.
is_genius_ratetells you when a price is a member rate. - Sold out means not bookable on Booking.com for this search: this party, this one night. A room type can be closed on Booking.com, held for other channels, or need a longer stay. It is not the hotel's occupancy, and
demand_pressureis a label read from it. - A sold-out room still has a row, marked ⚫, so a filling date never looks cheaper than it is. With
includeSoldOutoff, those rows and the demand read disappear. - Two runs minutes apart can differ. Booking.com tests prices and layouts on its visitors. Every row carries its capture time and country.
📅 Scheduling and history
The change fields and the competitor moves compare each run with the previous one. The previous prices live in a named key-value store, booking-price-history, in your Apify Storage.
- Do not delete that store. It is the memory. Delete it and every row reads
✦ Newagain until the history rebuilds. Deleting a run's dataset changes nothing: the next run never reads it. - Run at the same hour every day. A second run on the same day compares with the earlier one, so it shows only a few hours of moves.
- A room-night is known by hotel + night + room + party + currency + proxy country. Change the party, the currency or
historyStoreNameand every row starts again as✦ New. Adding a hotel or a country starts only that one. ChangingdaysAhead,startDate,concurrencyor the toggles changes nothing. - A page that fails keeps its older baseline. The next run compares with the last time it was read, and
days_since_previous_capturesays how long ago that was. - The store cleans itself. An entry is dropped once its night has passed, or after 30 days without being seen.
📥 Input
One of hotelUrls, searchUrl or searchText is required.
What to scrape
| Field | Default | What it does |
|---|---|---|
hotelUrls | — | Booking.com hotel pages. The plain URL is enough; dates in the URL are ignored. |
myHotelUrl | — | Your own property, also kept in hotelUrls. Every row of yours gets is_my_hotel: true and the digest ranks you. |
searchUrl | — | A Booking.com search-results URL with your filters. Every hotel found is scraped. |
searchText | — | A free-text destination, such as riad with pool in fes. Used only when the two above are empty. |
maxProperties | 50 | The most hotels to take from a search. |
propertyType | — | Filter a searchText search by type. |
minReviewScore | 0 | Skip hotels below this score. 0 = no limit. |
maxReviewScore | 0 | Skip hotels above this score. 0 = no limit. |
Dates and party
| Field | Default | What it does |
|---|---|---|
startDate | today | First check-in, YYYY-MM-DD. Empty = today at each hotel. Leave it empty on a schedule: a fixed date slides into the past. |
daysAhead | 3 | How many nights to price (1–365). Each night is one page per hotel, so this drives time and cost. |
adults | 2 | Adults in the party. It decides which rate is selected. |
children | 0 | Children in the party. |
childrenAges | — | One age per child (0–17). Booking.com prices a family by age. |
rooms | 1 | Rooms. Capacity is checked per room. |
currency | USD | One of 23 currencies, the same for every price. |
What to keep
| Field | Default | What it does |
|---|---|---|
includeSoldOut | true | Keep a row for every sold-out room. Needed for the demand read. Every row is a result you pay for, and on busy dates most rows can be sold out. |
trackPriceChanges | true | Compare with the last run and fill the change fields. No extra requests. |
historyStoreName | booking-price-history | Leave it as it is. Give two jobs different names only if their history must not mix. Renaming starts the history over. |
includeLosAnalysis | false | Adds los_prices and has_los_discount (booking several of a room at once). No extra page loads. |
includeWholesaleRates | false | Also capture partner and wholesale rates, tagged apart from retail. |
tieredCadence | false | Near nights daily, mid nights every 2 days, far nights every 3 days. About half the cost for long horizons. |
Digest and alerts
| Field | Default | What it does |
|---|---|---|
digestEmail | — | Email the daily digest, through Apify's Send Email Actor. Some Apify plans cannot start it: use the webhook below instead. |
digestWebhookUrl | — | Slack or Discord webhook: the digest as a message. Any other URL (n8n, Make, Zapier): JSON {"text": …, "digest": {…}}. |
webhookUrl | — | Slack or Discord webhook for error alerts. |
Proxy and speed
| Field | Default | What it does |
|---|---|---|
proxyConfiguration | Residential | Your choice of Apify proxy: residential or datacenter. Datacenter costs less, but Booking.com blocks it sooner: some nights can come back empty, or the run can fail. |
proxyCountries | ["US"] | The countries to load the pages from. Several countries = the same hotels once per country, tagged with proxy_country. |
useApifyProxy | true | Old field, hidden from the form. proxyConfiguration is read first. |
concurrency | 3 | Hotels scraped at once (1–5), each in its own browser. Lower it on a small Apify plan: fewer browsers use less memory. |
delayBetweenRequests | 3 | Seconds between two nights of the same hotel. |
🧯 When a run goes wrong
Every run writes a report, RUN_REPORT, into its key-value store, and its headline becomes the run's status line.
verdict | Meaning |
|---|---|
complete | Every page returned data. |
partial | Some nights failed. Every row you got is real. The report lists the hotels and nights to re-run, and the digest names them. |
failed | Nothing came back. The headline names the cause and what_to_do gives the fix. |
crashed / nothing_attempted | The run stopped before scraping, or the input had nothing to scrape. |
A row that could not be read says why in availability_label, and error_type says whose failure it was: proxy, page or browser.
🤖 Use it from Claude or ChatGPT
With the Apify MCP server, an AI assistant can run this Actor and read its results. Add noraview/booking-price-scraper to the MCP server's tools in your AI client, with your own Apify API token, and keep that token private.
When you hand it the results, point it at the AI input (compact) view, and tell it: my hotel is the row where is_my_hotel is true.
📖 Field reference
Every field, grouped by the question it answers. Empty when… says when a field is null and why. null always means not known, never 0, never "no".
Room rows: which room, which night
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
hotel_name_clean | The property's name, without the extras Booking.com puts in its page title. | Flag Motor Lodge | Never |
is_my_hotel | true on every row of the property you gave as myHotelUrl, false on the others. | true | myHotelUrl was left empty. Not yours and you never said which one is yours are different answers |
check_in_date | The night this row prices (YYYY-MM-DD). | 2026-09-30 | Never |
check_out_date | The morning after. Every row prices one night. | 2026-10-01 | Never |
lead_time_days | Nights between the run and arrival, counted from the hotel's own date. 0 = tonight. Segment on this before comparing anything. | 5 | The date could not be read |
day_of_week | Arrival weekday. | Wed | The date could not be read |
is_weekend | Arrival on a Friday or Saturday, the hotel weekend. Use day_of_week where your market's weekend differs. | false | The date could not be read |
room_name_clean | The room's name, without the bed phrase and deal tags, so the same room reads the same across nights. | Standard Room | Never |
room_id | Booking.com's id for the room type. Stable across runs. | 117422001 | Booking.com did not expose it |
Room rows: the price ⭐
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
nightly_total_price ⭐ | The price Booking.com shows for the selected rate, as a guest sees it (whole number). Tax that Booking.com puts inside the price is included; tax it charges on top is not. Read tax_clarity_label beside it. | 217 | Sold out, or no rate fits your party (availability_label says which) |
nightly_total_price_exact | The same rate, unrounded, from Booking.com's own data. Use it when cents matter. | 217.27 | Same as above |
lowest_bookable_rate ⭐ | The cheapest rate your party can book for this room tonight, whatever its terms. Same tax basis as nightly_total_price. They differ when a cheaper rate is non-refundable or is priced for more guests. Rates too small for your party are never counted. | 213 (Sanno, King Room, Fri 02 Oct, where the selected rate was 235) | Sold out, or no rate fits your party |
lowest_bookable_terms | The terms of that cheapest rate. | Non-refundable · Breakfast not included · Pay now | Same as above |
nightly_base_price | The room rate without tax. Only filled when Booking.com states the tax inside the price; never worked out from an assumed rate. | null (tax charged on top in these runs) | Booking.com states no included tax |
taxes_and_fees_total | All the tax Booking.com reports for the night: inside the price plus on top. | 21.73 | Booking.com states no tax |
tax_clarity_label ⭐ | Whether the price is final. ✅ Taxes included: every reported charge is inside the price. ⚠️ Part included, more on top: add taxes_excluded for the real cost. ⚠️ Additional taxes reported: tax is charged on top and the amount is known. ⚠️ Final cost not confirmed: no breakdown was stated. | ⚠️ Additional taxes reported | Sold out |
primary_tax_label | The main tax line of the selected rate, in Booking.com's words. | 10 % Tax | No tax line on the rate |
total_stay_price | nightly_total_price × number_of_nights. With one night per row, the same number. | 217 | Same as nightly_total_price |
currency | The currency of every price on the row. | AUD | Never |
price_basis | What the price fields represent, as a short machine-readable token. The guest count is the selected rate's capacity (max_guests), not your party; for the tax, read tax_clarity_label. | 1_night_2_guests_taxes_not_confirmed · sold_out | Never |
is_genius_rate | true when the price is a Genius (member) rate, not the public price. Never compare your public rate with a competitor's Genius rate. | false | No rate selected |
original_price | The struck-through price, when Booking.com shows one on the room. | null | No strikethrough shown |
discount_percent | The discount against original_price, computed on the legacy price. Do not recompute it against nightly_total_price. | null | No strikethrough shown |
Room rows: why this rate
A room usually has several rate plans. The row reports one, picked by fixed rules: it must fit your party, a refundable rate beats a cheaper non-refundable one, and among equals the cheapest wins.
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
rate_selection_reason ⭐ | Why this rate was picked, in plain words, including the cheaper rate it did not take. Written by the same rules that pick the rate; never by an AI. | Selected the refundable rate compatible with 1 guest(s) per room; a cheaper non-refundable rate (180) was available but not selected, since a refundable rate at the same capacity is preferred. | No rate could be selected |
occupancy_match_status | How the rate was matched to your party: capacity_compatible (it fits), uncertain_children (children in the party, whose counting Booking.com does not state), no_compatible_rate (nothing fits). | capacity_compatible | Never |
max_guests | The capacity of the selected rate: what it will take, not what you asked for. | 2 | No rate fits your party |
required_guests_per_room | Your party split across your rooms: the capacity a rate must have to count. | 1 | Never |
rate_options_count | How many rate plans Booking.com offers for this room tonight. 1 = the room is not fenced at all. | 1 | Never (0 when sold out) |
rate_spread | Dearest minus cheapest rate plan for this room: how widely the property fences its rates. 0 = several plans at the same price. | null (one plan only) | The room has only one rate plan |
refundable_premium | Cheapest refundable rate minus cheapest non-refundable rate: what flexibility costs here. | null | The room does not offer both |
primary_rate_option | The selected rate as one object. Every value in it comes from the same Booking.com rate: price, public price, capacity, cancellation, free-cancellation date, meal plan, breakfast, pay later, Genius, wholesale, taxes and their breakdown. | { "price": 217, "max_guests": 2, … } | Sold out, or no rate fits your party |
benchmark_rate_option | The same object for a fixed 2-adult rate, whatever your search. For benchmarking only; not for "which room should this guest book". | { … } | Same as above |
rate_options | Every rate plan found for the room, in the page's raw shape, to inspect the alternatives. Also carries each plan's strikethrough price and tax. | [ { … }, { … } ] | Empty list when sold out |
Room rows: the booking terms
All of these describe the selected rate, the same one nightly_total_price comes from.
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
booking_terms_summary ⭐ | Cancellation, breakfast and payment in one line. A condition that is not known is left out, never written as "No". Breakfast extra (the page sells it) is told apart from Breakfast not included. | Free cancellation · Breakfast not included · Pay now | No rate selected |
cancellation_policy | fully_refundable, partially_refundable, non_refundable or unknown. | fully_refundable | No rate selected |
is_refundable | true only for fully_refundable. A partial refund is not free cancellation. | true | No rate selected |
free_cancellation_until | The date in "Free cancellation before …": cancel before it and the guest pays nothing. | 2026-09-29 | Non-refundable, or no date printed |
free_cancellation_days_before_arrival | The same deadline as days before arrival: the policy itself, comparable across properties and dates. | 1 | No free-cancellation date |
meal_plan | The meal line of the rate, in Booking.com's words. | Good breakfast AUD 12 | The rate carries no meal line |
is_breakfast_included | Whether breakfast is in the price. | false | No rate selected |
book_now_pay_later | Whether the guest can book now and pay later. | false | No rate selected |
min_stay | Minimum nights for the rate. | null | Booking.com does not show it on the property page |
Room rows: availability and stock
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
is_sold_out | Booking.com's own sold-out flag for this room and night. | false | Never |
availability_status | available, low_availability (4–5 left), low_availability_urgent (1–3 left) or sold_out. It only moves on a count Booking.com states, so it reads available whenever no count is shown. | available | Never |
availability_label ⭐ | The same fact for a human: a colour and words. Also says when a room is on sale but has no rate for your party, and why a row has no data. | 🔴 Only 2 rooms left · ⚫ Sold out · ⚪ No rate for 1 guest · ⚠️ Not scraped — the page could not be read | Never |
rooms_left ⭐ | Rooms left, only when Booking.com prints "Only N left". null means unknown, not plenty. 0 on sold-out rows. | 2 | On sale and no count shown (the usual case) |
stock_confidence ⭐ | How far to trust rooms_left: exact (Booking.com's own badge), hidden (on sale, no count shown), inferred (sold-out rows), capped (10, Booking.com's display ceiling). On the Perth run: 15 exact, 50 hidden, 175 inferred. | exact | Never |
bookable_now_label | How many of this room a guest can still book, said no stronger than the page proves. 4 left: Booking.com's badge, or a quantity list that stops below another room's. 10+: at least that many. | 2 left · 10+ | Sold out, or no quantity list on the page |
max_rooms_per_booking | The top of this room's quantity list: the most a guest can put in one booking now. It can be the property's own ceiling rather than the stock; bookable_now_label says which. | 8 | No quantity list on the page |
max_nights_bookable | Booking.com's own count of this room bookable at once, from the page's rate data. Rooms, not nights: the name is historical. Prefer max_rooms_per_booking and bookable_now_label. | 2 | Not in the page's rate data |
los_prices | Booking.com's price for booking 1, 2, 3… of this room at once, for this night (the quantity list). Only with includeLosAnalysis. Not length-of-stay prices. | {"1": 130, "2": 260, …} | includeLosAnalysis is off |
has_los_discount | true when booking several of this room costs less per room than one. Only with includeLosAnalysis. | false | includeLosAnalysis is off |
Room rows: what changed since the last run
These compare each room-night with the previous run of the same job. On the first run there is nothing to compare, so they read ✦ New or null. All of them are empty when trackPriceChanges is off.
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
price_move_label ⭐ | The move at a glance: an arrow and its size. | ▲ +191 (+82.68%) · – Unchanged · ✦ New | Sold out now or at the last run: no price to compare |
price_direction | The same as a token: up, down, unchanged, or new the first time a room-night is seen, sold out or not. | up | Sold out now or at the last run, after its first sighting |
price_change | Today's nightly_total_price minus the last one. | 191 | First sighting (never 0, which would say the price held) |
price_change_pct | The change as a percentage of the last price. | 82.68 | Same as above |
previous_price | The price at the last capture. | 231 | First sighting |
previous_captured_at | When that last capture was. | 2026-09-24 | First sighting |
days_since_previous_capture | Days since that capture. Read it before price_change: a move over five days is not a daily move. 0 on a second run the same day. | 1 | First sighting |
days_since_price_change | How long the price had held up to this run. A competitor that has not moved in twelve days is a finding. | 3 | First sighting |
first_seen_price | The first price ever recorded for this room-night. | 217 | Never priced yet |
first_seen_at | When that first price was recorded. | 2026-09-22 | Never priced yet |
availability_change | new, unchanged, became_sold_out or came_back. A room selling out often moves before any price does. | came_back | Price tracking off, or error rows |
days_sold_out | How long the room has been off sale. On the run it comes back, how long it was gone. On a come-back row the price is compared with the last price before the sell-out, so the baseline is this many days older. | 3 | On sale and never sold out |
sold_out_episodes | How many separate times this room-night has gone off sale. 3 or more: it keeps going on and off sale. | 1 | Price tracking off |
Room rows: the room
These are read from the room's own listing. Sold-out rows carry fewer of them.
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
room_name | The room's name exactly as Booking.com shows it. | Standard Room | Never |
bed_type | The beds, as Booking.com lists them. | 1 extra-large double bed | No bed line for the room |
room_capacity | How many the room takes. When the page does not say, the largest capacity a rate proves: read it as at least. | 2 | Not stated and no rate proves it |
room_size_sqm | Size in square metres. | 16 | Not stated |
room_view | The view. | Garden view | Not stated |
has_private_bathroom | Private or ensuite bathroom. | true | Not stated |
room_amenities | The amenities that change the price: balcony, minibar, spa bath… | ["air conditioning"] | Empty list when none |
facilities | The room's full facility list, often 30–40 items. | ["Air conditioning", "Ensuite bathroom", "Flat-screen TV", "Free WiFi", …] | Empty list when none |
photo_count | How many photos Booking.com holds for this room type. | 7 | The page exposes none (never a false 0) |
room_photo_url | Booking.com's first photo of the room, at the size Booking.com serves. No image is downloaded. | https://cf.bstatic.com/xdata/images/hotel/max1024x768/116836127.jpg?k=… | No photo exposed |
Room rows: the hotel
The same on every row of a property. The full snapshot is in the Hotel metadata dataset.
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
hotel_name | The name as Booking.com's page title gives it. Use hotel_name_clean for grouping. | Flag Motor Lodge (Motel) (Australia) deals | Never |
hotel_url | The property page that was scraped. | https://www.booking.com/hotel/au/flag-motor-lodge.html | Never |
booking_hotel_id | Booking.com's id for the property. | 1174220 | Not exposed |
hotel_review_score | Review score, 0–10. | 7.5 | Not shown |
hotel_review_count | Reviews behind that score. | 4265 | Not shown |
hotel_star_rating | Official star rating. | 3 | No stars shown |
hotel_neighborhood | The area, from Booking.com's location data. | Rivervale | Booking.com gives no area |
hotel_address | The full address. | 129 Great Eastern Highway, Rivervale, 6103 Perth, Australia | Not shown |
property_type | Hotel, motel, riad, apartment… | Motel | Not stated |
hotel_latitude | Latitude. | -31.9526 | Not exposed |
hotel_longitude | Longitude. Also used to find the hotel's own "today". | 115.9159 | Not exposed |
hotel_checkin_from | Check-in start time as Booking.com states it. | 2:00 PM | Not shown when the page is read |
hotel_sustainability_certified | Booking.com's Travel Sustainable badge. | true | Hotel details could not be read |
Room rows: the search and the run
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
requested_adults | The adults this row was searched for. | 1 | Never |
requested_children | The children this row was searched for. | 0 | Never |
requested_children_ages | The children's ages sent to Booking.com. | [4, 9] | No ages given |
requested_rooms | The rooms this row was searched for. | 1 | Never |
number_of_nights | Nights covered by the row. Always 1. | 1 | Never |
proxy_country | The country the page was loaded from. Booking.com prices by visitor country, so compare rows from the same country. | US | Never |
scrape_timestamp | When the run started (UTC). | 2026-09-25T12:59:15Z | Never |
sort_key | hotel · date · room · room id, lowercased. Sort on it and any two exports line up row for row. | flag motor lodge|2026-09-30|standard room|117422001 | Never |
Market by date: one row per property, per night
Built from the room rows of the same run, with no extra requests. With a single property in the run, every comparison field is null: rank 1 of 1 is not information.
The examples are Flag Motor Lodge on Wed 30 Sep, from the same Perth run.
Which property, which night
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
record_type | Always market_summary. | market_summary | Never |
hotel_name_clean | The property. | Flag Motor Lodge | Never |
hotel_url | Its page. | https://www.booking.com/hotel/au/flag-motor-lodge.html | Never |
booking_hotel_id | Booking.com's id for it. | 1174220 | Not exposed |
is_my_hotel | true for your property. | true | myHotelUrl empty |
check_in_date | The night. | 2026-09-30 | Never |
check_out_date | The morning after. | 2026-10-01 | Never |
lead_time_days | Nights between the run and arrival. | 5 | Date unreadable |
day_of_week | Arrival weekday. | Wed | Date unreadable |
is_weekend | Friday or Saturday arrival. | false | Date unreadable |
requested_adults | The party searched. | 1 | Never |
requested_children | Children searched. | 0 | Never |
requested_rooms | Rooms searched. | 1 | Never |
proxy_country | The country the prices were seen from. | US | Never |
currency | The currency of every price. | AUD | Never |
scrape_timestamp | When the run started (UTC). | 2026-09-25T12:59:15Z | Never |
sort_key | For stable sorting across exports. | flag motor lodge|2026-09-30|| | Never |
The property's own night
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
lead_in_rate ⭐ | The cheapest rate your party can book at the property that night, across its rooms and whatever the terms. The ranking is built on it. | 217 | Nothing bookable |
median_rate | The median nightly_total_price of its rooms on sale. | 217 | Nothing bookable |
highest_rate | Its most expensive room on sale. | 217 | Nothing bookable |
rate_range | highest_rate − lead_in_rate: how far it spreads its rooms that night. | null (one room on sale) | Fewer than two rooms on sale |
refundable_premium_median | The median cost of flexibility across its rooms. | null | No room offers both |
taxes_on_top | true when every rate on sale adds tax on top of the price shown, false when every rate has it inside. Compare properties on the same basis. | true | The rates disagree or do not say |
Demand
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
room_types_offered | Room types the property lists at all. | 6 | Sold-out tracking off |
room_types_available | Room types bookable that night for your party. | 1 | Never |
room_types_sold_out | Room types listed but not bookable. | 5 | Sold-out tracking off |
sold_out_ratio | Sold out ÷ offered: the share of the property's room types Booking.com shows unavailable for this search. Not occupancy. | 0.833 | Sold-out tracking off |
rooms_length_restricted | Room types on sale that are down to their last room. The name is historical. | 0 | Never |
restricted_ratio | That count ÷ rooms on sale. Reported, but does not move demand_pressure. | 0.0 | Nothing on sale |
demand_pressure ⭐ | A label read from sold_out_ratio: sold_out, high (half or more of the room types unavailable), building (some) or normal (none). It describes availability on Booking.com, not occupancy. | high | Sold-out tracking off |
demand_pressure_reason | The numbers behind the level, in words. | 5 of 6 room types unavailable | No room types at all |
Against the others that night
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
compset_size | Properties with a bookable rate that night. The comparison is only this wide. | 4 | Only one property in the run |
compset_rank ⭐ | This property's place by lead_in_rate, 1 = cheapest. A sold-out property takes no rank. | 2 | Sold out, or no one to compare with |
market_lead_in_rate | The cheapest lead_in_rate that night. | 180 | No one to compare with |
market_median_rate | The median lead_in_rate of every property that night, yours included. The daily digest compares you with your competitors only (317 that night). | 267 | No one to compare with |
gap_to_market_median ⭐ | This property minus that median. Positive = above the market. | -50 | Sold out, or no one to compare with |
gap_to_cheapest | This property minus the cheapest. | 37 | Same as above |
price_position | cheapest, below_median, at_median, above_median, most_expensive, or sold_out. | below_median | No one to compare with |
Coverage: did every property load?
| Field | What it tells you | Example | Empty when… |
|---|---|---|---|
compset_expected | Properties the run asked for. | 4 | Never |
compset_observed | Properties that produced data that night. Fewer means one failed, and its absence moves the median. | 4 | Never |
compset_coverage | Observed ÷ expected. Below 1.0, every market figure on the row is built on an incomplete set. | 1.0 | Never |
coverage_warning | A sentence naming how many properties are missing. | null | The set is complete |
Hotel metadata: one row per property, per run
| Field | What it tells you | Example |
|---|---|---|
record_type | Always discovery. | discovery |
hotel_url | The property page. | https://www.booking.com/hotel/au/flag-motor-lodge.html |
hotel_name | Its name, as the page title gives it. | Flag Motor Lodge (Motel) (Australia) deals |
scrape_timestamp | When the run started (UTC). | 2026-09-25T12:59:15Z |
proxy_country | The country it was loaded from. | US |
hotel_metadata | The full snapshot: review score, count and breakdown, photo count, neighbourhood, address, property type, description, every hotel facility, star rating, Booking.com id, coordinates, check-in time, sustainability badge. | { "review_score": 7.5, "hotel_facilities": [ … ], … } |
❓ FAQ
Which price should I compare on?
nightly_total_price for a like-for-like comparison, lowest_bookable_rate for the price a guest sees first. Read tax_clarity_label before treating either as final.
Why is the price different from what I see on Booking.com?
Usually one of four things: you see it from another country (proxy_country), with tax included (tax_clarity_label), as a Genius member (is_genius_rate), or in another currency.
Why are so many rows sold out?
Because Booking.com shows those room types as unavailable for that night and party. On busy nights most room types of a small property are, and those rows are what the demand read is built on. Turn off includeSoldOut if you only want what is on sale.
What does rooms_left: null mean?
Unknown, not plenty. Booking.com shows a count only when it wants to push a booking.
How often should I run it?
Once a day, at the same hour. tieredCadence keeps long horizons affordable.
Can it scrape more than 10 hotels?
Yes. Use searchUrl or searchText with maxProperties. Around 10 per run is the sweet spot for speed and reliability.
📝 What's new
👋 Used this Actor before September 2026?
This Actor had a large update in September 2026. Nothing you use was removed: every field, every input and your saved tasks keep working. What you will notice:
- New on every row: what changed since the last run (
price_move_label,price_change,availability_change,days_since_price_change…), the cheapest bookable rate (lowest_bookable_rate),is_my_hotelandsort_key. 31 new fields in all, each in the field reference above. - New outputs: Market by date (your rank against the others, night by night) and the daily digest by email or webhook.
- New inputs, all optional:
myHotelUrl,trackPriceChanges,historyStoreName,digestEmail,digestWebhookUrl,childrenAges. - Read differently now:
rooms_leftis filled only when Booking.com prints "Only N left", so it is empty on most rows, andavailability_statusreadsavailableunless such a count is shown.nightly_total_priceis the price as Booking.com displays it:tax_clarity_labelsays whether tax is added on top.photo_countis empty when unknown, instead of 0.los_pricesholds the prices for booking 1, 2, 3… of a room at once.- With no
startDate, the first night is today at the hotel.
- Your first run after the update shows
✦ Newin the change columns: they compare each run with the previous one. - The screens: Rate shopping still opens first, Rate shopping V2 is the daily screen, and the API views
room_comparisonandai_room_pickerstill answer.
The last three releases
CHANGELOG.md
💬 Support
A question, a number that looks wrong, or an idea? Open an issue on the Issues tab. Every message is read.
Built by HotelSignals Lab.