Booking.com Hotels Scraper - Dated Prices & Rooms
Under maintenancePricing
from $3.00 / 1,000 hotel records
Booking.com Hotels Scraper - Dated Prices & Rooms
Under maintenanceBooking.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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
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-scraper | this actor | |
|---|---|---|
| Price per property | $0.005 | $0.003 |
| Room offers / surroundings | +$0.005 add-on event | included |
| Complete record | $0.010 | $0.003 |
currency | symbol (US$) | ISO code (USD) |
| Price parsing | from rendered text | Booking's numeric field |
| Taxes & charges, price before discount | — | yes |
freeCancellationUntil timestamp | — | yes |
| Children priced by age | — | childrenAges |
Property description | from the property page | not 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:
currencyis the ISO code instead of the symbol. If you were displaying it verbatim, map it.descriptionisnull. 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
| Field | Type | Default | Description |
|---|---|---|---|
search | string | — | Destination: city, region, landmark or address. |
startUrls | array | [] | Booking URLs: /searchresults.html?ss=…, ?dest_id=…&dest_type=city, /city/nl/amsterdam.html, or a single /hotel/nl/….html. |
maxItems | integer | 25 | Properties per destination or URL. |
checkIn / checkOut | YYYY-MM-DD | 1 night, 30 days out | The stay the price is for. |
flexWindow | "0"–"7" | "0" | Let Booking shift the stay by up to N days. |
rooms / adults / children | integer | 1 / 2 / 0 | Occupancy the price covers. |
childrenAges | array of integers | [] | One age (0–17) per child. Booking prices children by age. |
propertyType | enum | "none" | Hotels, Apartments, Hostels, Resorts, Villas, … |
sortBy | enum | "distance_from_search" | Also popularity, price, price_from_high_to_low, bayesian_review_score, class_asc, class_descending, … |
minScore | string | — | Minimum guest score, "8.4" format (rounded down to Booking's 5.0/6.0/7.0/8.0/9.0 buckets). |
starsCountFilter | enum | "any" | 1–5, or unrated. |
minMaxPrice | string | "0-999999" | Price per night, e.g. "100-150" or "100+". |
currency | ISO 4217 | "USD" | Currency every price is returned in. |
language | string | "en-gb" | Language Booking renders names and labels in. |
proxyConfiguration | object | Apify proxy | See 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
scrapedAtand 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,onlyXLeftMessageandfreeCancellationUntilper 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.