Booking.com Price Scraper & Room Rate Tracker avatar

Booking.com Price Scraper & Room Rate Tracker

Pricing

from $3.50 / 1,000 results

Go to Apify Store
Booking.com Price Scraper & Room Rate Tracker

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

HotelSignals Lab

Maintained by Community

Actor stats

10

Bookmarked

345

Total users

36

Monthly active users

2 days ago

Last modified

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.

  1. Paste this input and run it. Three Perth motels, three nights, one adult: about 50 rows in a few minutes.
  2. 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.
  3. 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.

FieldWhat it tells youExample
nightly_total_priceThe 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_rateThe 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_labelWhether that price is the final cost. Only ✅ means final.⚠️ Additional taxes reported
availability_labelAvailability at a glance, with a colour.🔴 Only 2 rooms left · ⚫ Sold out
rooms_left + stock_confidenceRooms left, only when Booking.com states it, and how far to trust that number.2 + exact · null + hidden
booking_terms_summaryCancellation, breakfast and payment in one line.Free cancellation · Breakfast not included · Pay now
rate_selection_reasonWhy this rate was picked, and which cheaper one was not.…a cheaper non-refundable rate (180) was available but not selected…
price_move_labelWhat the price did since the last run.▲ +191 (+82.68%)
is_my_hoteltrue 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 Sep
4 properties · 10 nights (Fri 25 Sep – Sun 04 Oct) · 1 adult · AUD
Prices 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 out
COMPETITOR 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 out
Run: ✅ 40 of 40 pages, 240 rows.
PartWhat it says
HeaderThe 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 lineThe 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 hotelYour 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 nightsThe 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 watchNights where half or more of your competitors have nothing left to sell.
Competitor movesPrice 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.
⚠️ RunOnly 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 digestWebhookUrl and 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) and DIGEST_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 tieredCadence for long horizons;
  • turn off includeSoldOut, knowing you lose the demand read.

📦 What you get

OutputWhere to find itOne record perUse it for
Room rowsOutput tab: Rate shopping, Rate shopping V2, Full detail, CSV, Excelroom type × night × proxy countryEvery price, term and availability, room by room
Market by dateOutput tab: Market by date (market_summary dataset)property × nightYour rank, the median, the gaps, and the demand read
Daily digestEmail or webhook, and DIGEST (text) / DIGEST_DATA (JSON) in the run's key-value storerunThe morning read: one screen of text
Hotel metadatahotel_metadata datasetproperty × runThe full hotel snapshot: reviews, description, every facility
Run reportRUN_REPORT in the run's key-value store, and the run's status linerunWhat worked, what did not, and whose fault it was

The room rows come in four views of the same data:

ViewFieldsForThrough the API
Rate shopping25The working screen: price, tax, terms, availability?view=rate_shopping
Rate shopping V252The 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)41Handing the data to an AI model?view=ai_input
Full detail104Every field, in reading order?view=full_detail
Room comparison (legacy)27Kept so API calls made before the update keep working?view=room_comparison
AI room picker (legacy)47Kept 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

  • null is 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_label on each row and taxes_on_top on 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 same currency. 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_rate tells 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_pressure is 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 includeSoldOut off, 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 ✦ New again 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 historyStoreName and every row starts again as ✦ New. Adding a hotel or a country starts only that one. Changing daysAhead, startDate, concurrency or 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_capture says 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

FieldDefaultWhat 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.
maxProperties50The most hotels to take from a search.
propertyType—Filter a searchText search by type.
minReviewScore0Skip hotels below this score. 0 = no limit.
maxReviewScore0Skip hotels above this score. 0 = no limit.

Dates and party

FieldDefaultWhat it does
startDatetodayFirst check-in, YYYY-MM-DD. Empty = today at each hotel. Leave it empty on a schedule: a fixed date slides into the past.
daysAhead3How many nights to price (1–365). Each night is one page per hotel, so this drives time and cost.
adults2Adults in the party. It decides which rate is selected.
children0Children in the party.
childrenAges—One age per child (0–17). Booking.com prices a family by age.
rooms1Rooms. Capacity is checked per room.
currencyUSDOne of 23 currencies, the same for every price.

What to keep

FieldDefaultWhat it does
includeSoldOuttrueKeep 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.
trackPriceChangestrueCompare with the last run and fill the change fields. No extra requests.
historyStoreNamebooking-price-historyLeave it as it is. Give two jobs different names only if their history must not mix. Renaming starts the history over.
includeLosAnalysisfalseAdds los_prices and has_los_discount (booking several of a room at once). No extra page loads.
includeWholesaleRatesfalseAlso capture partner and wholesale rates, tagged apart from retail.
tieredCadencefalseNear nights daily, mid nights every 2 days, far nights every 3 days. About half the cost for long horizons.

Digest and alerts

FieldDefaultWhat 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

FieldDefaultWhat it does
proxyConfigurationResidentialYour 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.
useApifyProxytrueOld field, hidden from the form. proxyConfiguration is read first.
concurrency3Hotels scraped at once (1–5), each in its own browser. Lower it on a small Apify plan: fewer browsers use less memory.
delayBetweenRequests3Seconds 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.

verdictMeaning
completeEvery page returned data.
partialSome nights failed. Every row you got is real. The report lists the hotels and nights to re-run, and the digest names them.
failedNothing came back. The headline names the cause and what_to_do gives the fix.
crashed / nothing_attemptedThe 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

FieldWhat it tells youExampleEmpty when…
hotel_name_cleanThe property's name, without the extras Booking.com puts in its page title.Flag Motor LodgeNever
is_my_hoteltrue on every row of the property you gave as myHotelUrl, false on the others.truemyHotelUrl was left empty. Not yours and you never said which one is yours are different answers
check_in_dateThe night this row prices (YYYY-MM-DD).2026-09-30Never
check_out_dateThe morning after. Every row prices one night.2026-10-01Never
lead_time_daysNights between the run and arrival, counted from the hotel's own date. 0 = tonight. Segment on this before comparing anything.5The date could not be read
day_of_weekArrival weekday.WedThe date could not be read
is_weekendArrival on a Friday or Saturday, the hotel weekend. Use day_of_week where your market's weekend differs.falseThe date could not be read
room_name_cleanThe room's name, without the bed phrase and deal tags, so the same room reads the same across nights.Standard RoomNever
room_idBooking.com's id for the room type. Stable across runs.117422001Booking.com did not expose it

Room rows: the price ⭐

FieldWhat it tells youExampleEmpty 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.217Sold out, or no rate fits your party (availability_label says which)
nightly_total_price_exactThe same rate, unrounded, from Booking.com's own data. Use it when cents matter.217.27Same 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_termsThe terms of that cheapest rate.Non-refundable · Breakfast not included · Pay nowSame as above
nightly_base_priceThe 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_totalAll the tax Booking.com reports for the night: inside the price plus on top.21.73Booking.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 reportedSold out
primary_tax_labelThe main tax line of the selected rate, in Booking.com's words.10 % TaxNo tax line on the rate
total_stay_pricenightly_total_price × number_of_nights. With one night per row, the same number.217Same as nightly_total_price
currencyThe currency of every price on the row.AUDNever
price_basisWhat 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_outNever
is_genius_ratetrue when the price is a Genius (member) rate, not the public price. Never compare your public rate with a competitor's Genius rate.falseNo rate selected
original_priceThe struck-through price, when Booking.com shows one on the room.nullNo strikethrough shown
discount_percentThe discount against original_price, computed on the legacy price. Do not recompute it against nightly_total_price.nullNo 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.

FieldWhat it tells youExampleEmpty 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_statusHow 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_compatibleNever
max_guestsThe capacity of the selected rate: what it will take, not what you asked for.2No rate fits your party
required_guests_per_roomYour party split across your rooms: the capacity a rate must have to count.1Never
rate_options_countHow many rate plans Booking.com offers for this room tonight. 1 = the room is not fenced at all.1Never (0 when sold out)
rate_spreadDearest 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_premiumCheapest refundable rate minus cheapest non-refundable rate: what flexibility costs here.nullThe room does not offer both
primary_rate_optionThe 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_optionThe 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_optionsEvery 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.

FieldWhat it tells youExampleEmpty 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 nowNo rate selected
cancellation_policyfully_refundable, partially_refundable, non_refundable or unknown.fully_refundableNo rate selected
is_refundabletrue only for fully_refundable. A partial refund is not free cancellation.trueNo rate selected
free_cancellation_untilThe date in "Free cancellation before …": cancel before it and the guest pays nothing.2026-09-29Non-refundable, or no date printed
free_cancellation_days_before_arrivalThe same deadline as days before arrival: the policy itself, comparable across properties and dates.1No free-cancellation date
meal_planThe meal line of the rate, in Booking.com's words.Good breakfast AUD 12The rate carries no meal line
is_breakfast_includedWhether breakfast is in the price.falseNo rate selected
book_now_pay_laterWhether the guest can book now and pay later.falseNo rate selected
min_stayMinimum nights for the rate.nullBooking.com does not show it on the property page

Room rows: availability and stock

FieldWhat it tells youExampleEmpty when…
is_sold_outBooking.com's own sold-out flag for this room and night.falseNever
availability_statusavailable, 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.availableNever
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 readNever
rooms_left ⭐Rooms left, only when Booking.com prints "Only N left". null means unknown, not plenty. 0 on sold-out rows.2On 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.exactNever
bookable_now_labelHow 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_bookingThe 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.8No quantity list on the page
max_nights_bookableBooking.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.2Not in the page's rate data
los_pricesBooking.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_discounttrue when booking several of this room costs less per room than one. Only with includeLosAnalysis.falseincludeLosAnalysis 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.

FieldWhat it tells youExampleEmpty when…
price_move_label ⭐The move at a glance: an arrow and its size.▲ +191 (+82.68%) · – Unchanged · ✦ NewSold out now or at the last run: no price to compare
price_directionThe same as a token: up, down, unchanged, or new the first time a room-night is seen, sold out or not.upSold out now or at the last run, after its first sighting
price_changeToday's nightly_total_price minus the last one.191First sighting (never 0, which would say the price held)
price_change_pctThe change as a percentage of the last price.82.68Same as above
previous_priceThe price at the last capture.231First sighting
previous_captured_atWhen that last capture was.2026-09-24First sighting
days_since_previous_captureDays 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.1First sighting
days_since_price_changeHow long the price had held up to this run. A competitor that has not moved in twelve days is a finding.3First sighting
first_seen_priceThe first price ever recorded for this room-night.217Never priced yet
first_seen_atWhen that first price was recorded.2026-09-22Never priced yet
availability_changenew, unchanged, became_sold_out or came_back. A room selling out often moves before any price does.came_backPrice tracking off, or error rows
days_sold_outHow 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.3On sale and never sold out
sold_out_episodesHow many separate times this room-night has gone off sale. 3 or more: it keeps going on and off sale.1Price tracking off

Room rows: the room

These are read from the room's own listing. Sold-out rows carry fewer of them.

FieldWhat it tells youExampleEmpty when…
room_nameThe room's name exactly as Booking.com shows it.Standard RoomNever
bed_typeThe beds, as Booking.com lists them.1 extra-large double bedNo bed line for the room
room_capacityHow many the room takes. When the page does not say, the largest capacity a rate proves: read it as at least.2Not stated and no rate proves it
room_size_sqmSize in square metres.16Not stated
room_viewThe view.Garden viewNot stated
has_private_bathroomPrivate or ensuite bathroom.trueNot stated
room_amenitiesThe amenities that change the price: balcony, minibar, spa bath…["air conditioning"]Empty list when none
facilitiesThe room's full facility list, often 30–40 items.["Air conditioning", "Ensuite bathroom", "Flat-screen TV", "Free WiFi", …]Empty list when none
photo_countHow many photos Booking.com holds for this room type.7The page exposes none (never a false 0)
room_photo_urlBooking.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.

FieldWhat it tells youExampleEmpty when…
hotel_nameThe name as Booking.com's page title gives it. Use hotel_name_clean for grouping.Flag Motor Lodge (Motel) (Australia) dealsNever
hotel_urlThe property page that was scraped.https://www.booking.com/hotel/au/flag-motor-lodge.htmlNever
booking_hotel_idBooking.com's id for the property.1174220Not exposed
hotel_review_scoreReview score, 0–10.7.5Not shown
hotel_review_countReviews behind that score.4265Not shown
hotel_star_ratingOfficial star rating.3No stars shown
hotel_neighborhoodThe area, from Booking.com's location data.RivervaleBooking.com gives no area
hotel_addressThe full address.129 Great Eastern Highway, Rivervale, 6103 Perth, AustraliaNot shown
property_typeHotel, motel, riad, apartment…MotelNot stated
hotel_latitudeLatitude.-31.9526Not exposed
hotel_longitudeLongitude. Also used to find the hotel's own "today".115.9159Not exposed
hotel_checkin_fromCheck-in start time as Booking.com states it.2:00 PMNot shown when the page is read
hotel_sustainability_certifiedBooking.com's Travel Sustainable badge.trueHotel details could not be read

Room rows: the search and the run

FieldWhat it tells youExampleEmpty when…
requested_adultsThe adults this row was searched for.1Never
requested_childrenThe children this row was searched for.0Never
requested_children_agesThe children's ages sent to Booking.com.[4, 9]No ages given
requested_roomsThe rooms this row was searched for.1Never
number_of_nightsNights covered by the row. Always 1.1Never
proxy_countryThe country the page was loaded from. Booking.com prices by visitor country, so compare rows from the same country.USNever
scrape_timestampWhen the run started (UTC).2026-09-25T12:59:15ZNever
sort_keyhotel · 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|117422001Never

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

FieldWhat it tells youExampleEmpty when…
record_typeAlways market_summary.market_summaryNever
hotel_name_cleanThe property.Flag Motor LodgeNever
hotel_urlIts page.https://www.booking.com/hotel/au/flag-motor-lodge.htmlNever
booking_hotel_idBooking.com's id for it.1174220Not exposed
is_my_hoteltrue for your property.truemyHotelUrl empty
check_in_dateThe night.2026-09-30Never
check_out_dateThe morning after.2026-10-01Never
lead_time_daysNights between the run and arrival.5Date unreadable
day_of_weekArrival weekday.WedDate unreadable
is_weekendFriday or Saturday arrival.falseDate unreadable
requested_adultsThe party searched.1Never
requested_childrenChildren searched.0Never
requested_roomsRooms searched.1Never
proxy_countryThe country the prices were seen from.USNever
currencyThe currency of every price.AUDNever
scrape_timestampWhen the run started (UTC).2026-09-25T12:59:15ZNever
sort_keyFor stable sorting across exports.flag motor lodge|2026-09-30||Never

The property's own night

FieldWhat it tells youExampleEmpty 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.217Nothing bookable
median_rateThe median nightly_total_price of its rooms on sale.217Nothing bookable
highest_rateIts most expensive room on sale.217Nothing bookable
rate_rangehighest_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_medianThe median cost of flexibility across its rooms.nullNo room offers both
taxes_on_toptrue 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.trueThe rates disagree or do not say

Demand

FieldWhat it tells youExampleEmpty when…
room_types_offeredRoom types the property lists at all.6Sold-out tracking off
room_types_availableRoom types bookable that night for your party.1Never
room_types_sold_outRoom types listed but not bookable.5Sold-out tracking off
sold_out_ratioSold out ÷ offered: the share of the property's room types Booking.com shows unavailable for this search. Not occupancy.0.833Sold-out tracking off
rooms_length_restrictedRoom types on sale that are down to their last room. The name is historical.0Never
restricted_ratioThat count ÷ rooms on sale. Reported, but does not move demand_pressure.0.0Nothing 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.highSold-out tracking off
demand_pressure_reasonThe numbers behind the level, in words.5 of 6 room types unavailableNo room types at all

Against the others that night

FieldWhat it tells youExampleEmpty when…
compset_sizeProperties with a bookable rate that night. The comparison is only this wide.4Only one property in the run
compset_rank ⭐This property's place by lead_in_rate, 1 = cheapest. A sold-out property takes no rank.2Sold out, or no one to compare with
market_lead_in_rateThe cheapest lead_in_rate that night.180No one to compare with
market_median_rateThe median lead_in_rate of every property that night, yours included. The daily digest compares you with your competitors only (317 that night).267No one to compare with
gap_to_market_median ⭐This property minus that median. Positive = above the market.-50Sold out, or no one to compare with
gap_to_cheapestThis property minus the cheapest.37Same as above
price_positioncheapest, below_median, at_median, above_median, most_expensive, or sold_out.below_medianNo one to compare with

Coverage: did every property load?

FieldWhat it tells youExampleEmpty when…
compset_expectedProperties the run asked for.4Never
compset_observedProperties that produced data that night. Fewer means one failed, and its absence moves the median.4Never
compset_coverageObserved ÷ expected. Below 1.0, every market figure on the row is built on an incomplete set.1.0Never
coverage_warningA sentence naming how many properties are missing.nullThe set is complete

Hotel metadata: one row per property, per run

FieldWhat it tells youExample
record_typeAlways discovery.discovery
hotel_urlThe property page.https://www.booking.com/hotel/au/flag-motor-lodge.html
hotel_nameIts name, as the page title gives it.Flag Motor Lodge (Motel) (Australia) deals
scrape_timestampWhen the run started (UTC).2026-09-25T12:59:15Z
proxy_countryThe country it was loaded from.US
hotel_metadataThe 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_hotel and sort_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_left is filled only when Booking.com prints "Only N left", so it is empty on most rows, and availability_status reads available unless such a count is shown.
    • nightly_total_price is the price as Booking.com displays it: tax_clarity_label says whether tax is added on top.
    • photo_count is empty when unknown, instead of 0.
    • los_prices holds 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 ✦ New in 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_comparison and ai_room_picker still 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.