# Booking.com Scraper — Hotels, Prices & Reviews (`tortuga/booking-scraper`) Actor

Scrape Booking.com: hotels from destination searches with prices for your dates (per stay, per night, taxes), review scores, stars and distance; hotel details with facilities and policies; guest reviews. Monitor prices and new reviews.

- **URL**: https://apify.com/tortuga/booking-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 hotel from search results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Booking.com Scraper — Hotels, Prices & Reviews

Scrape **Booking.com search results with prices for your dates**, **hotel details** (address, coordinates, description, facilities, house rules, check-in/out times, photos, score breakdown) and **guest reviews** for any hotel or destination. Export to JSON, CSV or Excel, or call it from your app through the Apify API.

- Prices for your exact dates, guests, rooms and currency: price per stay and per night, taxes and fees, struck-through price and deal name, free cancellation, breakfast, rooms left.
- Search any city, region, district or landmark; filter by stars, review score, free cancellation or breakfast; sort by price, rating or distance.
- Reviews: score, title, liked / disliked text, review date, stay dates and nights, room type, traveller type, reviewer name and country as shown, hotel response, language. Every review of a hotel is reachable (thousands for big hotels).
- Monitoring: schedule it and get only changed prices or new reviews each run; repeats are not charged.
- Fast: plain HTTP through Apify datacenter proxies, no browser. 100 hotels per request in search mode.

### What data does it extract?

#### Search results (`mode: search`, default)

| Field | Example |
|---|---|
| `hotelId`, `name`, `url` | `59015`, `Ibis Styles Paris Batignolles`, `https://www.booking.com/hotel/fr/mirific.html` |
| `accommodationType`, `starRating` | `Hotel`, `3` |
| `address`, `area`, `city`, `countryCode`, `latitude`, `longitude` | `119 Avenue De Clichy`, `17th arr., Paris`, `Paris`, `FR`, `48.8899`, `2.3212` |
| `distanceFromCentre`, `distanceFromCentreKm`, `publicTransport` | `2.7 miles from downtown`, `4.35`, `Brochant station is within 150 yards` |
| `reviewScore`, `reviewScoreText`, `reviewCount` | `8.3`, `Very Good`, `3225` |
| `price`, `pricePerNight` | Price for the whole stay as Booking.com shows it, and per night |
| `taxesAndCharges`, `priceWithTaxes`, `pricePerNightWithTaxes` | Taxes and fees not included in `price` (city tax etc.), and the total with them |
| `originalPrice`, `originalPricePerNight`, `deal` | Struck-through price and the deal name (`Late Escape Deal`) |
| `currency`, `checkIn`, `checkOut`, `nights`, `adults`, `rooms` | `EUR`, `2026-11-10`, `2026-11-12`, `2`, `2`, `1` |
| `available`, `soldOut`, `roomsLeft` | `true`, `false`, `1` ("Only 1 left at this price") |
| `roomType`, `freeCancellation`, `freeCancellationUntil`, `noPrepayment` | `Standard Twin Room`, `false`, … |
| `mealPlan`, `breakfastIncluded` | `Breakfast included`, `true` |
| `photo`, `position`, `searchQuery`, `destination` | Main photo URL, rank in the results, your input, the resolved destination |
| `changeType`, `previousValues` | Only with monitoring |

#### Hotel details (`mode: hotels`)

`hotelId`, `name`, `url`, `accommodationType`, `starRating`, `address`, `city`, `countryCode`, `latitude`, `longitude`, `reviewScore`, `reviewCount`, `scoreBreakdown` (Staff, Facilities, Cleanliness, Comfort, Value for money, Location, WiFi, Breakfast, …), `scoreByTravellerType` (Couples, Solo travelers, Groups of friends), `description`, `checkInFrom`, `checkInUntil`, `checkOutFrom`, `checkOutUntil` (24-hour times), `amenities`, `amenityGroups` (facilities by group: General, Bathroom, Food & Drink, Parking, …), `languagesSpoken`, `policies` (pets, children, cribs, extra beds, minimum check-in age, accepted cards, cash), `roomTypes`, `images` (up to 45 photo URLs), `hotelCurrency`, and with a check-in date: `checkIn`, `checkOut`, `nights`, `available`, `price`, `pricePerNight`, `taxesAndCharges`, `priceWithTaxes`, `originalPrice`, `currency`, `cheapestRoom`, `freeCancellation`, `breakfastIncluded`, `reviewScoreText`.

#### Reviews (`mode: reviews`)

| Field | Example |
|---|---|
| `reviewId` | `7cc232726d221a9b` |
| `hotelId`, `hotelName`, `hotelUrl` | `280149`, `The Savoy`, `https://www.booking.com/hotel/gb/the-savoy.html` |
| `rating` | `10` (Booking.com's 1-10 scale) |
| `title`, `positives`, `negatives` | Review title, what the guest liked, what they disliked |
| `reviewDate` | `2026-10-06T20:13:48Z` |
| `checkInDate`, `checkOutDate`, `lengthOfStay` | `2026-10-02`, `2026-10-04`, `2` |
| `roomType`, `travellerType` | `Deluxe Room with 2 Twin Beds`, `Family` |
| `reviewerName`, `reviewerCountry`, `reviewerCountryCode`, `isAnonymous` | `Katie`, `United Kingdom`, `GB`, `false` (as Booking.com shows them) |
| `language` | `en`, `fr`, `en-us`, … |
| `helpfulVotes`, `photos` | Votes and guest photo URLs |
| `hotelResponse` | The property's reply |

### How to scrape Booking.com hotel prices for my dates

Use the default **Search results with prices** mode:

```json
{
  "mode": "search",
  "destinations": ["Paris"],
  "checkIn": "2026-11-10",
  "nights": 2,
  "adults": 2,
  "rooms": 1,
  "currency": "EUR",
  "starRatings": ["4", "5"],
  "maxItems": 200
}
```

Output example (shortened):

```json
{
  "type": "searchResult",
  "hotelId": 50013,
  "name": "Hôtel Jardin de Cluny",
  "url": "https://www.booking.com/hotel/fr/lejardindecluny.html",
  "accommodationType": "Hotel",
  "starRating": 4,
  "area": "5th arr., Paris",
  "distanceFromCentre": "0.5 miles from downtown",
  "distanceFromCentreKm": 0.8,
  "reviewScore": 9.1,
  "reviewScoreText": "Wonderful",
  "reviewCount": 1763,
  "available": true,
  "price": 437.21,
  "pricePerNight": 218.6,
  "taxesAndCharges": 77.52,
  "priceWithTaxes": 514.73,
  "originalPrice": 514.36,
  "deal": "Late Escape Deal",
  "currency": "EUR",
  "roomType": "Classic Double Room",
  "roomsLeft": 1,
  "freeCancellation": false,
  "breakfastIncluded": false,
  "checkIn": "2026-11-10",
  "checkOut": "2026-11-12",
  "nights": 2
}
```

Prices depend on the dates, the number of guests and rooms, the currency and the visitor's country, so all of them are inputs. Without a check-in date the search uses the dates in your search URL, else 7 days from today. You can also paste a search URL copied from Booking.com (`https://www.booking.com/searchresults.html?ss=Rome&checkin=...&checkout=...&nflt=class%3D4`); its dates, guests and filters are used.

### How to scrape Booking.com reviews

1. Choose **Reviews** mode.
2. Paste hotel URLs (`https://www.booking.com/hotel/gb/the-savoy.html`; language suffixes and tracking parameters are fine), or give a destination to read the reviews of its top hotels (**Max hotels per destination**).
3. Set **Max reviews per hotel**, optionally **Review languages** (`en`, `fr`, …), **Sort reviews by** or **Only reviews newer than**.

```json
{
  "mode": "reviews",
  "startUrls": [{ "url": "https://www.booking.com/hotel/gb/the-savoy.html" }],
  "maxReviewsPerHotel": 1000,
  "maxItems": 1000
}
```

```json
{
  "type": "review",
  "reviewId": "7cc232726d221a9b",
  "hotelId": 280149,
  "hotelName": "The Savoy",
  "rating": 10,
  "title": "The staff were so polite, kind and helpful. They went above and beyond to make us feel welcome.",
  "positives": "Great decor and amazing attention to detail.",
  "negatives": "It was all amazing, can’t fault a thing.",
  "language": "en",
  "reviewDate": "2026-10-06T20:13:48Z",
  "checkInDate": "2026-10-02",
  "checkOutDate": "2026-10-04",
  "lengthOfStay": 2,
  "travellerType": "Family",
  "roomType": "Deluxe Room with 2 Twin Beds",
  "reviewerName": "Katie",
  "reviewerCountry": "United Kingdom",
  "reviewerCountryCode": "GB",
  "hotelResponse": "Dear Katie, Thank you for staying with us at The Savoy ..."
}
```

### How to get Booking.com hotel details, facilities and policies

Use **Hotel details** mode with hotel URLs or destinations. Add a **Check-in date** to also get the price and availability for your stay (one extra request per hotel URL; hotels taken from a destination search reuse the search prices).

### Monitor changes on a schedule

Turn on **Only new or changed items**, save the input as a task and add a Schedule in Apify (for example daily). In search mode the first **Max items** hotels of each search are watched and a hotel is saved again only when its price, struck-through price, availability or review score changed; use fixed dates for this. Hotel details are compared on price, availability, review score and stars. In reviews mode each run checks the newest **Max reviews per hotel** reviews of every hotel and saves only reviews it has not saved before; **Only reviews newer than** (`1 day`, `7 days` or a date) narrows it further. Unchanged items are not output and not charged. New items carry `changeType: "new"`, changed ones `changeType: "changed"` plus `previousValues` with the old values of the fields that changed. Booking.com shows different rates to different anonymous visitors (A/B pricing), so each watch keeps one visitor identity between runs to compare like with like. Use **Monitoring key** to keep several schedules on the same input apart.

### Does it work without login or a browser?

Yes. It reads the same public data the Booking.com website loads for any visitor, with no login, no cookies from you and no browser. The default Apify datacenter proxy is enough.

### How much does it cost?

Pay per result: $0.0035 per search result, $0.006 per hotel details item, $0.0015 per review. Platform usage is small (plain HTTP, 512 MB), and you can cap spend with **Max items**.

### Proxy note

The default `{"useApifyProxy": true}` (datacenter) works; no residential or Unblocker proxy is needed. Booking.com may show different prices to visitors from different countries; set a proxy country (for example US) to keep prices comparable between runs, especially when monitoring.

### Is it legal to scrape Booking.com?

The actor collects publicly visible data: hotel information, prices and guest reviews with the reviewer's display name and country as Booking.com shows them. It does not log in or access anything private. Reviews contain personal data (names, countries); make sure your use complies with GDPR and similar laws and with Booking.com's terms, and do not use the data to contact or profile individuals.

# Actor input Schema

## `mode` (type: `string`):

search: one item per hotel in a destination's search results with the price for your dates. hotels: one details item per hotel (address, coordinates, facilities, policies, check-in/out, photos, score breakdown; with a check-in date also price and availability). reviews: one item per guest review.

## `destinations` (type: `array`):

Cities, regions, districts or landmarks to search, e.g. "Paris" or "Manhattan, New York". Resolved with Booking.com's own destination search. In hotels and reviews mode a destination stands for its top hotels (see Max hotels per destination).

## `startUrls` (type: `array`):

Hotel pages (https://www.booking.com/hotel/fr/mirific.html, any language suffix and query works) or search pages (https://www.booking.com/searchresults.html?ss=Paris\&checkin=2026-12-01\&checkout=2026-12-03). Dates, guests and filters (nflt) in a URL apply to that URL when the input does not set them.

## `maxItems` (type: `integer`):

Stop after this many items in total (hotels or reviews across all inputs). With 'Only new or changed items' in search mode, the first Max items hotels of each search are watched.

## `checkIn` (type: `string`):

Check-in date (YYYY-MM-DD) for prices. Search mode without a date uses the dates in the search URL, else 7 days from today. Hotel details without a date have no prices. Use fixed dates when monitoring prices.

## `nights` (type: `integer`):

Length of stay. Prices are given per stay and per night.

## `adults` (type: `integer`):

Guests for prices and availability.

## `rooms` (type: `integer`):

Rooms for prices and availability.

## `currency` (type: `string`):

Currency of all prices (ISO code such as USD, EUR, GBP, JPY). Booking.com converts at its own rate.

## `sortBy` (type: `string`):

Search order. Empty = Booking.com's default (Our top picks) or the order in the search URL.

## `starRatings` (type: `array`):

Only hotels with these star ratings. Empty = all.

## `minReviewScore` (type: `string`):

Only properties with at least this guest review score.

## `freeCancellationOnly` (type: `boolean`):

Only offers with free cancellation.

## `breakfastIncluded` (type: `boolean`):

Only offers that include breakfast.

## `maxHotelsPerSearch` (type: `integer`):

Hotels and reviews mode: how many top hotels of each destination or search URL are processed.

## `maxReviewsPerHotel` (type: `integer`):

Reviews mode: cap per hotel. Booking.com serves every review of a hotel (thousands for big hotels).

## `sortReviewsBy` (type: `string`):

Review order. 'Only reviews newer than' and monitoring always use newest first.

## `reviewLanguages` (type: `array`):

Only reviews written in these languages (codes such as en, fr, de, es, it, nl, pt, ru, zh, ja). Empty = all languages.

## `reviewsNewerThan` (type: `string`):

Only reviews newer than: a date (YYYY-MM-DD) or a period such as '7 days' or '24 hours'. Reviews are read newest first and paging stops at the first page that is entirely older.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this search returned and output only new items or items whose price/status changed. Use with a Schedule; unchanged items are not charged. Search: price, struck-through price, availability, review score. Hotels: price, availability, review score, stars. Reviews: only reviews not seen before among the newest 'Max reviews per hotel'.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input.

## `proxyConfiguration` (type: `object`):

Apify datacenter proxy (default) works; no residential or Unblocker proxy is needed. Booking.com can show different prices to visitors from different countries: pick a proxy country to pin it (important when monitoring prices).

## Actor input object example

```json
{
  "mode": "search",
  "destinations": [
    "Paris"
  ],
  "startUrls": [],
  "maxItems": 100,
  "nights": 1,
  "adults": 2,
  "rooms": 1,
  "currency": "USD",
  "starRatings": [],
  "maxHotelsPerSearch": 10,
  "maxReviewsPerHotel": 100,
  "sortReviewsBy": "newest",
  "reviewLanguages": [],
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "destinations": [
        "Paris"
    ],
    "startUrls": [],
    "starRatings": [],
    "reviewLanguages": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/booking-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "destinations": ["Paris"],
    "startUrls": [],
    "starRatings": [],
    "reviewLanguages": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/booking-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "destinations": [
    "Paris"
  ],
  "startUrls": [],
  "starRatings": [],
  "reviewLanguages": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call tortuga/booking-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tortuga/booking-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/aJ6tFmRbl0GcILKyA/builds/8TanU3zOIOPLZ0C07/openapi.json
