Booking.com Hotels Scraper - Dated Prices & Rooms avatar

Booking.com Hotels Scraper - Dated Prices & Rooms

Under maintenance

Pricing

from $3.00 / 1,000 hotel records

Go to Apify Store
Booking.com Hotels Scraper - Dated Prices & Rooms

Booking.com Hotels Scraper - Dated Prices & Rooms

Under maintenance

Booking.com hotel search with dated pricing: per-night and total price, taxes and charges, price before discount, and every matched room offer with its meal plan and free-cancellation deadline. Drop-in compatible with voyager/booking-scraper. Filters are verified against Booking's echo.

Pricing

from $3.00 / 1,000 hotel records

Rating

0.0

(0)

Developer

DIOPSIDE AI

DIOPSIDE AI

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Booking.com Hotels Scraper

Drop-in compatible with voyager/booking-scraper — cheaper, and room data is included instead of being an add-on.

Search Booking.com by destination, dates and occupancy and get back every property with the price for that exact stay: per-night price, total, taxes and charges, the struck-through price when there is a discount, and every room offer Booking matched to your party — room type, bed layout, meal plan, and the free-cancellation deadline.

Public search-result data only. No login, no guest names, no personal data.

Why this one

1. Prices are numbers, not parsed strings. Booking renders prices localised (US$1,118), and a scraper that reads the rendered text has to guess whether , is a thousands separator or a decimal point. This actor reads Booking's own numeric field (amountUnformatted), so US$1,118 is 1118.0 and never 1.118. currency is an ISO 4217 code (USD), not a symbol (US$).

2. Room offers are part of the record, at no extra charge. The incumbent puts surroundings and room offerings behind a second additional-hotel-data-scraped event, which doubles the price of a complete record. Here rooms[] ships with every hotel: room type, bed layout, per-offer price in both your currency and the hotel's, meal plan, "only 1 left at this price", and freeCancellationUntil as a real timestamp.

3. &offset= on Booking's search page is a no-op. The server-rendered results page returns the same first ~18 properties for offset=0, offset=25 and offset=50 — with a 200 OK and no hint that anything was ignored. Real pagination only happens through Booking's FullSearch GraphQL operation. This actor paginates there and de-duplicates by property id, so asking for 200 hotels gets you 200 distinct hotels.

4. The bot wall is handled once per run, not once per page. Booking fronts every page with an AWS WAF JavaScript challenge — plain httpx and curl_cffi impersonating Chrome both get the interstitial. A real browser is launched once, clears the challenge, and hands its aws-waf-token to ordinary HTTP requests for the rest of the run. Nothing about Booking's GraphQL query is hard-coded: the browser is made to fire one FullSearch call and that request is captured and replayed, so a front-end release that rewrites the query or rotates the CSRF token does not break the actor.

5. Filters are verified, not assumed. Booking answers 200 OK with a plausible unfiltered feed when a filter id is stale or wrong. After the first page of every destination, this actor re-reads the filter list Booking echoes back and fails that destination loudly if something was dropped — rather than writing unfiltered rows into your dataset. propertyType is resolved against the category list Booking returns for your destination, so a renamed category is an error, not a silent no-op.

vs voyager/booking-scraper

The incumbent is healthy (4.5★, ~0.9% failed runs) — this is not a displacement play, it is a cheaper and more complete record:

voyager/booking-scraperthis actor
Price per property$0.005$0.003
Room offers / surroundings+$0.005 add-on eventincluded
Complete record$0.010$0.003
currencysymbol (US$)ISO code (USD)
Price parsingfrom rendered textBooking's numeric field
Taxes & charges, price before discount—yes
freeCancellationUntil timestamp—yes
Children priced by age—childrenAges
Property descriptionfrom the property pagenot returned (search cards do not carry one)

Switching from voyager/booking-scraper

Change the actor id. Every input field name (search, startUrls, maxItems, propertyType, sortBy, minScore, starsCountFilter, currency, language, checkIn, checkOut, flexWindow, rooms, adults, children, minMaxPrice) and the core output field names (order, url, name, type, id, description, stars, price, currency, rating, ratingLabel, reviews, breakfast, location, address, image, rooms) mean the same thing. Everything this actor adds sits alongside them under new names.

Two differences to know about:

  • currency is the ISO code instead of the symbol. If you were displaying it verbatim, map it.
  • description is null. Booking's search cards do not carry a property description; the incumbent fills it by fetching each property page, which is what its add-on event pays for.

Input

FieldTypeDefaultDescription
searchstring—Destination: city, region, landmark or address.
startUrlsarray[]Booking URLs: /searchresults.html?ss=…, ?dest_id=…&dest_type=city, /city/nl/amsterdam.html, or a single /hotel/nl/….html.
maxItemsinteger25Properties per destination or URL.
checkIn / checkOutYYYY-MM-DD1 night, 30 days outThe stay the price is for.
flexWindow"0"–"7""0"Let Booking shift the stay by up to N days.
rooms / adults / childreninteger1 / 2 / 0Occupancy the price covers.
childrenAgesarray of integers[]One age (0–17) per child. Booking prices children by age.
propertyTypeenum"none"Hotels, Apartments, Hostels, Resorts, Villas, …
sortByenum"distance_from_search"Also popularity, price, price_from_high_to_low, bayesian_review_score, class_asc, class_descending, …
minScorestring—Minimum guest score, "8.4" format (rounded down to Booking's 5.0/6.0/7.0/8.0/9.0 buckets).
starsCountFilterenum"any"1–5, or unrated.
minMaxPricestring"0-999999"Price per night, e.g. "100-150" or "100+".
currencyISO 4217"USD"Currency every price is returned in.
languagestring"en-gb"Language Booking renders names and labels in.
proxyConfigurationobjectApify proxySee Proxy below.

Output

One record per property. Example (trimmed):

{
"order": 0,
"url": "https://www.booking.com/hotel/nl/twentyseven-amsterdam.en-gb.html?checkin=2026-11-10&checkout=2026-11-12&group_adults=2&no_rooms=1&group_children=0&selected_currency=USD",
"name": "Hotel TwentySeven - Small Luxury Hotels of the World",
"type": "hotel",
"id": 2785940,
"stars": 5.0,
"price": 1637.61,
"currency": "USD",
"rating": 9.4,
"ratingLabel": "Superb",
"reviews": 213,
"breakfast": null,
"location": { "lat": 52.3724914, "lng": 4.89332549999995 },
"address": {
"full": "27 Dam, Amsterdam",
"street": "27 Dam",
"country": "NL",
"region": "Amsterdam City Centre, Amsterdam",
"postalCode": null
},
"image": "https://cf.bstatic.com/xdata/images/hotel/square600/559981856.webp",
"rooms": [
{
"available": true,
"roomType": "Junior Suite",
"bedType": "1 large double bed",
"persons": 2,
"price": 1637.61,
"currency": "USD",
"priceInHotelCurrency": 1428.73,
"hotelCurrency": "EUR",
"freeCancellationUntil": null,
"onlyXLeftMessage": null
}
],
"checkInDate": "2026-11-10",
"checkOutDate": "2026-11-12",
"nights": 2,
"adults": 2,
"children": 0,
"roomsRequested": 1,
"pricePerNight": 818.81,
"priceTotal": 1637.61,
"priceBeforeDiscount": null,
"taxesAndCharges": 472.33,
"city": "Amsterdam",
"countryCode": "nl",
"latitude": 52.3724914,
"longitude": 4.89332549999995,
"distanceFromCentre": "150 feet from centre",
"publicTransport": "Rokin station is within 250 yards",
"isCentrallyLocated": true,
"freeCancellation": false,
"noPrepayment": false,
"isSoldOut": false,
"isSustainable": false,
"sustainabilityCertifications": [],
"isPreferred": true,
"isSponsored": false,
"searchLabel": "Amsterdam",
"searchUrl": "https://www.booking.com/searchresults.html?ss=Amsterdam&checkin=2026-11-10&checkout=2026-11-12&group_adults=2&no_rooms=1&group_children=0&selected_currency=USD&lang=en-gb",
"scrapedAt": "2026-09-21T06:50:40.678556+00:00"
}

Missing upstream values are null, never a crash. bedType names the five bed types Booking's own cards label; an id Booking has not shown us reads as a plain "bed" rather than a guess.

Sizes and limits

  • Per destination: Booking serves roughly 1,000 results for one search before it stops returning new properties. When that ceiling — and not maxItems — is what stopped a destination, the run says so in its status message. To go deeper, split the search (by star rating, price band or district) into several start URLs.
  • Speed: one browser launch (~15–25 s) plus ~1 s per page of 25 properties.
  • Prices move. Every record carries scrapedAt and the stay it was priced for, so a series of runs is directly comparable.

Use cases

  • Rate shopping / revenue management — track a compset's nightly rate for a rolling date window.
  • Travel meta-search and price alerts — per-night and total price with taxes broken out.
  • Market analysis — supply, star mix, review scores and sustainability certification per city.
  • Availability monitoring — isSoldOut, onlyXLeftMessage and freeCancellationUntil per offer.

Proxy

A proxy is required. Booking's AWS WAF challenge never clears from Apify's own datacenter IPs — a run with proxyConfiguration: { "useApifyProxy": false } fails with "search results never rendered behind the challenge" after three attempts. The default Apify proxy clears it. Escalate to RESIDENTIAL if the challenge starts failing there too.

The token is minted through one exit IP and bound to it, so the browser and every later request share a single sticky session. When the challenge fails, the actor retries on a fresh exit IP up to three times before giving up, because the challenge refuses individual IPs intermittently.