Booking.com Hotel Scraper – Prices, Room Rates & Reviews
Pricing
from $2.54 / 1,000 search results
Booking.com Hotel Scraper – Prices, Room Rates & Reviews
Scrape Booking.com hotel prices, room rates and availability via the mobile API. Full per-room rate table with itemised taxes and fees, family pricing by child age, guest reviews and property details — one actor, not four. Search by destination, GPS radius or hotel id. No API key needed.
Booking.com API Scraper – Hotel Prices, Rooms, Reviews & Availability
Scrape Booking.com at scale — hotels, live room prices, availability, full rate breakdowns, facilities, photos and guest reviews as clean, structured JSON. No login, no captchas, no browser automation. Give it a destination and dates and export to CSV, Excel, JSON or the API.
Whether you track competitor prices, build a travel app, feed a rate-shopping dashboard or do market research, this Booking.com data extractor turns the world's largest accommodation site into a structured feed.
Fast and reliable — calls Booking.com's own mobile app API (
mobile-apps.booking.com) instead of rendering pages, so it needs no browser automation or login and is far faster and more stable than page-rendering scrapers. No partner contract needed — it does not call the partner-gated Demand API and needs no affiliate ID, API key or approval.
What you can extract
- Hotel search results – every property for a city, region, free-text destination or GPS radius, with live total price, currency and booking links.
- Live prices and itemised taxes – per-stay pricing with every tax and fee broken out (city tax, VAT, service charge, tourism fee, cleaning fee…), in your currency.
- Family stays priced by child age – pass the ages and get the rates Booking would actually sell your party, family rooms surfacing and non-fitting rooms flagged.
- Rooms and availability – every room type and bookable rate: occupancy, structured bed layout, bedrooms and bathrooms, size, board, cancellation policy, payment options, and how many rooms are left at each price.
- Availability status – for a fixed list of hotels you always get a record, saying why there is no price: sold out, minimum stay not met (with the nights required), closed, or unknown id.
- Guest reviews – paginated, with score, title, positive/negative text, date and reviewer country.
- Property details – name, star rating, review score and count, coordinates, address, 100+ named facilities, family amenities, languages, house rules, photos.
Example use cases
- Hotel price monitoring & rate shopping – track competitor and market prices over time for revenue management.
- Family & group travel products – price parties with children correctly, and show only rooms that fit.
- Travel & metasearch products – power a booking site, app or comparison tool with fresh inventory.
- Market & investment research – analyze pricing, occupancy signals, ratings and amenities across a destination.
- Review & reputation analysis – mine guest sentiment and review trends.
- Lead lists & data enrichment – build accommodation datasets with geo and facilities context.
Why this scraper, not a flat-listing one
Most Booking.com scrapers return one row per hotel with a single total price. Enough to see who is cheapest today; not enough for revenue management, a family stay, or explaining a number to a client. This actor returns the layer underneath:
- It tells you why a hotel has no price. Query a fixed list of ids and you always get a record back — sold out, minimum stay not met (with the nights required), closed, or unknown id — instead of the property vanishing.
- It searches by GPS radius or bounding box, not just destination name.
- It is one actor, not four. Search, details, rooms and reviews come from the same run, so you do not chain a listing scraper into a reviews scraper into a photos scraper.
- There is no per-run start fee. That matters more than it looks on a schedule: a start fee is charged whether a run returns 2 rows or 2,000, so it falls hardest on exactly the small, frequent runs price monitoring is made of. Most alternatives charge one.
How to use it
No coding required. Set your search up in the visual input form, then run it on demand, schedule it, or call the API and connect Make, Zapier, n8n, Google Sheets and more. Results land in a dataset you can download as JSON, CSV, Excel or HTML.
Family stays with children
Child ages are required for correct family prices. Booking prices a stay by each child's age — free cots, kids-stay-free rules, extra beds — so 2 adults with children aged 2 and 9 pays differently from the same party aged 12 and 15, and differently again from 4 adults.
{ "adults": 2, "rooms": 1, "childrenAges": [5, 9] }
The ages go out on every request, rooms that cannot host the party are marked
fitsParty: false, and each product echoes occupancy.requestedChildrenAges so
you can confirm they reached Booking. The alternative spellings, the per-room
allocation form and what happens when you omit the ages are in
the input reference.
Ready-made examples (one click, no setup)
Nine published tasks sit on this Actor's Examples tab: rate-shopping a fixed compset room by room, city-wide price monitoring, sell-out tracking for compression dates, full rate tables with taxes, reviews with per-category subscores, cheapest well-rated properties, family pricing by child age, self-catering supply with bedroom counts, and a landmark radius search. Open one, press run, and it works with the input already filled in; running it copies it into your account, so you can change anything afterwards without touching the original. None pins a date — check-in defaults to 30 days ahead, so they return live availability rather than going stale.
Dates, absolute or relative. Every date input — checkin, checkout,
attractionsStartDate, attractionsEndDate — takes either a chosen day
(2026-12-10) or a relative value (30 days, 2 weeks, 1 month). Prefer the
relative form in anything saved or scheduled: a fixed date is correct the day you
set it and then drifts into the past, and Booking only quotes future stays, so a
schedule built on one eventually rejects its own input. Both are resolved to a
real date before any request goes out, and every row carries that date, not the
words.
Recipes
Eight worked runs — family stays with child ages, price monitoring over a fixed list, cheapest well-rated hotels in a city, apartments only, reviews for competitive analysis, radius search around coordinates, full enrichment, and telling sold-out from minimum-stay — each with the exact input that produces it, are on recipes.
Input reference
Every input, with its type and default, is on the Input tab — it is generated from the schema, so it describes the deployed build. What a field description cannot carry is on the input reference: how the ten modes differ, which toggles add a charge event, where a run's limits come from, and what Deep search actually does.
The first decision is the mode, and it is also the one that sets the bill:
| mode | what it does | bills |
|---|---|---|
search | find properties and their prices | search-result |
rooms | room options for ids you have | room-details |
rates | re-check named product ids | rate-check or room-details |
reviews | guest reviews | review + review-scores |
reviewScores | score summary only | review-scores |
surroundings | nearby places | surroundings |
details | static property content | hotel-details |
attractions | tickets, tours, activities | attraction-row |
attractionDetails | one attraction in full | attraction-detail |
attractionAvailability | an attraction's dates | attraction-availability |
On search and rooms, each include* toggle adds its own event — that is
where a run's cost actually comes from. Charging happens on delivery, not on
request: a surface that fails or returns nothing is free. Units, the toggles
and the two events that are not priced yet are in
what each mode bills; the figures are on the
Pricing tab, which is generated and plan-tiered.
Upgrading: what changed for existing runs
Search-mode runs are unchanged. A saved task sending search, dates, guests and
the include* toggles produces the same records, fields and charges as
before — verified by diffing old and new builds on the same input, property by
property.
Breaking changes, all of them. Each one removes or refuses something that was wrong rather than merely unhelpful, and each is loud instead of silent:
| what | why |
|---|---|
attractions: latitude / longitude renamed to cityLatitude / cityLongitude | they are the destination centroid, not the product: 21 products in three cities gave three pairs, one per city. A product's own are on an attractionDetails row |
attractions: cancellationLabel removed | null on 143 of 143 products; freeCancellation, stillRefundable and cancellationUntil fill every time |
scrapedAt / scrapedAtUnix added (not a breaking change) | ISO-8601 UTC to milliseconds, and epoch seconds, on every row: when that row's price was fetched, not when it was pushed. Booking's own date strings are not ISO — do not share a parser. RUN_SUMMARY.window bounds the run |
extra_charges.conditional[] and conditionalTotal removed | empty everywhere: 0 of 4,934 room-list lines, 0 of 405 search lines. A line's own inclusion still reads conditional, and such a line now groups with excluded, never unknown. depositTotal stays — a refundable deposit there would overstate the bill |
bookerCountry removed | read by nothing since the day it was added — never in src/, never on a request. The point of sale follows the proxy exit country, now reported per row as provenance.exitCountry |
Older waves — the 1.0.94–1.0.141 changes, the two no-op flags that now work and
are billed, the rejected-instead-of-defaulted location, and the retired
country — are in the upgrade guide, with the reasoning for each.
Renames are aliased, not breaking: city and region still work beside
cityId and regionId, searchMode defaults to auto, and an empty
outputFields gives the full record.
Output reference
Every column, with its type and what it means, is on the Output tab — 120 of them, generated from the dataset schema, so it describes what the deployed build returns. The same tables by row kind, each with a real row and the nested objects member by member, are on keyspace.dev:
| row kind | modes | columns | reference |
|---|---|---|---|
| Accommodations | search, rooms, details, surroundings | 35 | property-rows |
| Reviews & scores | reviews, reviewScores | 12 | reviews |
| Watched rates | rates | 27 | rates |
| Attractions | attractions | 30 | attractions |
| Attraction details | attractionDetails | 56 | attraction-details |
| Attraction availability | attractionAvailability | 19 | attraction-availability |
Numeric ids — accommodation type, room type, bed type, charge type, payment card — are tabled in the integer ID reference. Annotated screenshots joining each number on screen to the field behind it are on the screenshots page.
What is inside the amount the guest pays, what Booking adds on top, and what sits outside it entirely, is drawn in the price stack. Read that before reconciling a total against anything.
The five columns read wrong most often
Everything else is in the references above; these five cost money to get wrong.
- Compare against
price.total_with_charges, notprice.total.totalis the display price: right on a room-list rate, low wherever Booking excludes tax from it. One column is correct on both paths. starRatingdoes not say whether the stars are official. Booking awards some and lets others self-estimate; the surface returnssymbol: "STARS"for both, so ranking by it mixes them.amountDueNowisnull, not0, when a rate states no payment stage. Treating null as zero turns every such rate into a false "pays nothing now".- A meal price is in the property's own currency, not the row's. Read
breakfastPricewithbreakfastCurrency: a Cape Town property quotes 120 ZAR on a run priced in EUR. - Do not rebuild
url— read it. Booking's slugs are not derivable from the name, and unavailable ones come backnullrather than guessed.
Known limitations
What this surface cannot give you — each entry with the measurement that says
so, and tagged by who could fix it — is on
known limitations. The one worth knowing
before you start: roomList returns the room types bookable for your dates,
not the property's catalogue, and the shortfall is not detectable from the
payload. So "this property has N room types" is not a claim this data supports.
Attractions: tickets, tours and activities
Booking sells more than places to stay. Three modes read its bookable-product catalogue — museum admissions, guided tours, day trips, transfers. A different vertical from the six property modes, sharing nothing with them but the location primitive: no stay window, no guests, no room blocks.
| mode | gives you |
|---|---|
attractions | the catalogue for a place, with price, rating, duration and cancellation terms |
attractionDetails | one product in full — the long description, what is included, where it starts |
attractionAvailability | which dates are bookable, and optionally the timeslots |
How to point them at a destination, what each input does and how to read the
availability status are in the input reference;
the columns are on attractions,
attraction details and
attraction availability.
Frequently asked questions
Can I get Booking Genius / loyalty-tier prices?
No — and that is a proven absence, not a gap in this Actor. Genius prices
need a member login, which this Actor deliberately never does. The evidence —
genius_discount_percentage present and exactly 0 on 1,385 of 1,385 blocks,
0 of 180 search rows badged, and no request parameter that sets a level — is on
known limitations, which collects everything
this surface cannot give.
Is there a Booking.com API, and do I need partner approval to use it?
Booking.com's official Demand API is partner-gated — contract and affiliate ID.
This actor needs neither: it reads the public mobile app API
(mobile-apps.booking.com) and returns structured JSON, so you can get hotel
prices without an API key.
How do I scrape Booking.com hotel prices?
Give it a destination and dates and it returns every property that can host the
stay, with live total price, currency and booking link. Add includeRooms for the
per-room rate table and includeExtraCharges for itemised taxes and fees.
How do I get room rates and availability for a specific hotel?
Pass its numeric id in hotel_ids with your dates and set includeRooms. You get
every bookable rate — occupancy, bed layout, board, cancellation policy, rooms
left — and, where there is no price, an availability status saying why.
Can I search hotels near a set of coordinates?
Yes. Set latitude, longitude and radius to search by GPS radius, or
neLat/neLon/swLat/swLon for a bounding box — "hotels within 2 km of this
venue" rather than "hotels in this city".
Which Booking.com scraper should I use for rate shopping or revenue management? One returning rates per room rather than one price per hotel, because a blended nightly total cannot be compared across boards, cancellation policies or occupancy. This actor returns the full per-room rate table with taxes itemised. See why this scraper.
Do I need a Booking.com account or API key? No. Just configure the input and run it.
Does it price children correctly?
Yes — pass the ages in childrenAges (or guests.children) and every request
carries them; see Children / family stays. Without
ages Booking prices the party as adults only.
Can I get prices in my own currency and language?
Yes — set currency and language.
How many reviews can I collect?
Reviews are paginated, so you can pull well beyond the first page — set
maxReviews per property.
Can I scrape a specific hotel?
Yes — pass its numeric Booking id (not the URL slug or name) in hotel_ids. To
find it, open the hotel's Booking.com page, View source, and search for
hotelId. An unknown id yields availability.status: "not_found" rather than
silently vanishing.
Can I just put a hotel's name in search instead of finding its id?
Often, yes — the actor tries to resolve a full hotel name to the right property,
with checks against confidently returning the wrong one. But matching is not
guaranteed, and ambiguous names may fail with "Could not resolve destination".
The numeric id in hotel_ids is the reliable way to target one hotel.
Why is a hotel missing from my search results?
Booking's search only returns properties that can host the stay. To learn why
one is missing (sold out, minimum stay, closed), query it through hotel_ids and
read availability.
Does it return phone numbers or email addresses? No. Booking.com hides property contact details until after a booking, so they are not in public data.
What formats can I export? Results land in a dataset, downloadable as JSON, CSV, Excel or HTML, or available through the API and integrations.
Is this an official Booking.com product? Does it use the Demand API? No to both — it is an independent scraper reading the public mobile app API, and needs no partner approval.
This is one entry in a longer inventory: known limitations collects everything this surface cannot give, each with the measurement behind it and tagged by whether it is Booking's limit, ours, or a line we chose not to cross.
Did you find this useful?
Rate this actor on Apify! Your feedback helps other users find it and helps us keep improving it.