kestrel actors docs

kestrel actorsGuides

How to scrape Booking.com reviews (the page is walled, the review call is not)

Every guide to scraping Booking.com starts with a headless browser, because the hotel page really is walled. That wall is on the page route only. The call the review list itself makes answers a plain HTTP client, which is why review rows cost a fraction of a cent and price rows are harder.

The short version

  1. Stop loading the hotel page. A first navigation to a Booking hotel page returns 202 and an AWS WAF challenge, and the real page took 54-77 seconds through a residential proxy in a measured Playwright run.
  2. Call the review endpoint instead. Booking's own review list is fetched by the page from a JSON endpoint that answers without the WAF token. Twenty-five reviews per call, no login, no API key.
  3. Push your filters into the request. Language, traveller type and keyword are Booking's own server-side filters, so the reviews you do not want are never fetched.
  4. Read lowest-score-first for complaints. Sorting by score ascending and stopping at the first review above your threshold turns a 36-page walk into one page.

The wall is on the page, not on the data

Booking.com property pages are behind AWS WAF. A measured headless-Chromium run through a residential proxy got a 202 and the WAF interstitial on first navigation; the real page was ready 54-77 seconds later and weighed 1.56 MB. A second hotel in the same browser context skipped the challenge and still took 61 seconds and 2.1 MB, because the cost is the page weight, not the challenge.

That is a fine way to spend a dollar reading one hotel. It is a terrible way to build a review pipeline.

The interesting finding is what the WAF actually guards. Booking's page routes fingerprint the TLS stack; the JSON endpoint its own review list calls does not. Point a plain HTTP client at the review call and you get 25 structured reviews per request, in a few hundred milliseconds, with no cookie, no token and no browser. In testing Booking answered ten calls a second from a single IP — one call per second per session is the conservative default, not a limit.

What that changes about cost

A review row is a few hundred bytes of JSON instead of a share of a two-megabyte residential page load. That is the whole reason Booking review rows are $0.005 while a browser-based approach cannot be priced there honestly.

It also changes what a complaints feed costs. Booking's endpoint takes a sort order, so asking for reviews lowest score first and stopping at the first one above your threshold reads one page rather than thirty-six:

from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/booking-reviews-scraper").call(run_input={
    "hotelNames": ["Memmo Alfama Lisbon"],
    "maxReviewsPerHotel": 0,      # every review that matches
    "maxRating": 6,               # Booking's 1-10 scale: keep 6 and below
    "requireText": True,          # drop score-only reviews before billing
})
rows = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items() if r["type"] == "review"]
print(len(rows), "complaints;", sum(1 for r in rows if r["response"]), "answered by the hotel")

A property with 892 reviews and 24 of them at 6 or below costs one page and 24 rows — about twelve cents — not the whole corpus.

Three ways to name a property, one billing

A URL, its name and its id are the same property: they are resolved, deduplicated and harvested once, so mixing inputs never double-bills. One caveat worth knowing — an id resolves to reviews and scores but not to a name, because Booking's lookup runs from name to id and not back. If you need hotel_name filled, give a URL or a name.

What one row carries

{
  "type": "review",
  "review_id": "c90f61915f999a43",
  "rating": 10.0,
  "rating_5": 5.0,
  "title": "Best place to stay in Lisbon!",
  "positives": "Great location and amazing terrace.",
  "negatives": null,
  "language": "en",
  "review_date": "2026-08-28",
  "check_in": "2026-08-24",
  "check_out": "2026-08-27",
  "nights": 3,
  "room_type": "Premium Double or Twin Room",
  "traveler_type": "Couple",
  "reviewer_country": "Canada",
  "response": null,
  "hotel_id": "536251",
  "hotel_name": "Memmo Alfama - Design Hotels"
}

Two fields there do work most review datasets cannot. positives and negatives are kept apart rather than glued into one blob, which is exactly the split a topic model wants. And room_type on every row means "which room do the complaints cluster in" is a GROUP BY, not a project.

The honest limitations

Run it

Booking.com Reviews Scraper — $0.005 per delivered review, with hotel rows, status rows, filtered reviews, empty properties and failed lookups all free. If you are comparing sources first, the review scraper comparison shows what each OTA row carries, and monitoring hotel reputation across OTAs shows how to run this daily across a portfolio.

FAQ

Do I need a Booking.com API key?

No, and there is no public one for reviews. Booking's Demand API is a partner product tied to a commercial agreement. The review endpoint this uses is the same one the public review list on the property page calls, with no login and no account.

Why do my Booking prices differ between two runs minutes apart?

Because Booking prices a stay per session. Two runs seconds apart can differ by a few per cent to a quarter — the site rotates promotional rates, mobile-only deals and currency handling per session. It is the site's behaviour, not a parsing artefact, and every scraper of it sees the same thing. Compare like with like, give alerts a threshold, and take a median for anything that must be defensible.

How many reviews does one property return?

All of them, 25 per call. One Lisbon boutique hotel returned 892 reviews across 24 languages. Roughly four in ten Booking reviews are a score with no words — a text filter drops those before you are billed.

Can I get the property's reply and its category scores?

Yes. Each review row carries the property's public reply, and the free hotel row carries Booking's seven category scores (staff, facilities, cleanliness, comfort, value, location, wifi) plus the language and traveller-type breakdowns with counts.

Run Booking.com Reviews Scraper on Apify →

Other actors used on this page: apify.com/kestrel/booking-prices-scraper.

More guides