# Booking.com Hotels Scraper (`dami_studio/booking-hotels-scraper`) Actor

Give Booking.com a destination and dates and get one row per property: live price for that stay, guest review score and count, star rating, exact coordinates, room type and cancellation policy. Plain HTTP, no browser. About 1,000 per search. $1.00 per 1,000.

- **URL**: https://apify.com/dami\_studio/booking-hotels-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Travel, Lead generation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 booking.com property returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Booking.com Hotels Scraper

Give it a destination and a set of dates and you get the matching properties back as rows: the live price for that stay, the guest review score and count, star rating, coordinates, room type, cancellation policy and the Booking.com URL.

Booking.com stops serving new properties at around 1,000 per search, so a whole large city is a deep sample rather than a full inventory. The measured numbers are in Limits below.

It talks to Booking.com's own search API over plain HTTP. No headless browser and no residential bandwidth. A real 300-property run finished in a handful of requests and about 1 MB of traffic, which came to $0.0039 of Apify platform usage end to end.

### What you get

One row per property. The fill rates below come from a single real 300-property run (Paris and Rome, 2-night stay, 2026-08-10) and are the numbers that run actually produced:

| Field | Filled | Notes |
|---|---|---|
| `hotelId`, `name`, `url` | 100% | Booking.com's own property id and canonical page |
| `city`, `countryCode` | 100% | |
| `latitude`, `longitude` | 100% | exact, not geocoded |
| `neighborhood`, `distanceFromCenter` | 100% | `18th arr., Paris`, `3.5 km from downtown` |
| `reviewScore`, `reviewsCount`, `reviewScoreWord` | 100% | `9.6`, `2634`, `Exceptional` |
| `priceTotal`, `pricePerNight`, `priceFormatted`, `currency` | 100% | for the exact stay you asked for |
| `roomType` | 100% | the room the price belongs to |
| `beds`, `bedrooms`, `bathrooms` | 100% | `bedrooms` and `bathrooms` are `0` on plain hotel rooms |
| `freeCancellation`, `noPrepayment`, `isSoldOut` | 100% | |
| `isSustainable`, `isPreferredPartner`, `isNewlyOpened` | 100% | |
| `photoUrl` | 100% | |
| `accommodationType` | 100% | `Hotel`, `Apartment`, `Guesthouse`, `Condo Hotel` and so on |
| `address` | 99.7% | |
| `starRating` | 98.0% | absent on some private homes. A later 250-row sample across a different destination mix came back at 90%, so expect it to move with what you search |
| `availabilityMessage` | 73.0% | `We have 1 left at this price` |
| `unitType` | 41.0% | `Entire apartment`, `Private suite`. Homes only |
| `priceBeforeDiscount` | 35.7% | only when there's a real strike-through price |
| `taxesAndFees` | 33.3% | only where Booking.com breaks it out |
| `freeCancellationUntil` | 31.7% | ISO timestamp |
| `mealPlan` | 15.0% | `Breakfast included` |

Those optional-field percentages track how often Booking.com publishes the detail, not how often the scraper manages to read it, so they move with the destination and the dates. No column came back empty on every row.

Every row also carries the stay it was priced for: `checkIn`, `checkOut`, `nights`, `adults`, `rooms`, `children`, `searchQuery`, `scrapedAt`.

### Input

```json
{
  "destinations": ["Paris", "Manhattan, New York", "Eiffel Tower"],
  "checkIn": "2026-09-01",
  "checkOut": "2026-09-03",
  "adults": 2,
  "rooms": 1,
  "childrenAges": ["7"],
  "maxItemsPerDestination": 200,
  "sortBy": "review_score",
  "currency": "EUR",
  "language": "en-us",
  "minStars": 4,
  "minReviewScore": "80",
  "maxPricePerNight": 250,
  "freeCancellationOnly": true
}
```

`destinations` takes anything Booking.com's own search box takes: a city, a district, a landmark, an airport, a country, or a specific property name. Every filter and sorter in the input form is Booking.com's own, and each was checked to actually change the result set before being exposed.

### Prices need dates

Booking.com won't quote a price without a stay attached. Leave `checkIn` and `checkOut` empty and the actor searches a 1-night stay 30 days out, then writes those dates onto every row, so a price is never ambiguous about what it's a price for.

### Billing

$1.00 per 1,000 properties, plus a $0.001 actor start fee. It's a flat rate, the same $1.00 on the free plan as on any paid one.

You're charged once per property row and for nothing else. Diagnostics, the empty-input sample, retries and duplicate properties dropped during pagination are all free.

### Limits, and what this doesn't do

- **About 1,000 properties per search, maximum.** Booking.com's API stops serving new properties around that depth and starts repeating itself. The actor de-duplicates, notices the repeats and stops. A measured Tokyo run reached 1,075 unique properties and Paris reached 870. Split by neighbourhood or price band if you need a full city inventory.
- **One price per property**, the one Booking.com puts on the search card for your dates and occupancy. It doesn't enumerate every room type or rate plan for a hotel.
- **No guest review text.** Only the score and the count. Review text is a different scraper.
- **No hotel description, facility list or photo gallery.** One main photo URL per property.
- **Availability moves.** Prices and result order change through the day, so two runs minutes apart won't be byte-identical.
- **`minReviewScore` is Booking.com's own filter, and it lets unreviewed properties through.** In a 260-row run filtered to "Very Good: 8+", 4 rows came back with `reviewScore: 0` and `reviewsCount: 0`. Those are brand-new listings Booking.com hasn't scored yet. Drop them client-side if you only want rated properties.
- Results follow Booking.com's own ranking, sponsored placements included, exactly as the site serves them.

### FAQ

**Does this need a Booking.com account or an affiliate key?**
No. There's nothing to log into and no key to supply.

**Does it use a browser or residential proxies?**
No, it's a plain HTTP client against Booking.com's search API, which is why it's cheap. The free Apify datacenter proxy is used by default. Residential gets stripped from the proxy configuration even if you select it, because it costs $8/GB and buys you nothing here.

**Can I get prices for one specific hotel?**
Yes. Put the property name in `destinations`, like `"Hilton Paris Opera"`. Booking.com resolves it and the property comes back first, with the surrounding results behind it.

**Can I scrape apartments and vacation rentals rather than hotels?**
Yes. Set `propertyType` to `apartments`, `villas`, `vacation_homes` or `entire_homes_and_apartments`. The `accommodationType` field tells you what each row is.

**What currency are the prices in?**
Whatever you set in `currency`. Booking.com does the conversion server-side, and all 39 offered currencies were checked against the live API.

**Why do I get fewer rows than `maxItemsPerDestination`?**
Either the destination genuinely has fewer properties available for those dates and filters, or you hit the ~1,000 pagination ceiling. The run log prints how many properties Booking.com reports for your search, so you can tell which.

**What if a search returns nothing?**
You get one uncharged diagnostic row saying so. An empty result is never billed.

### Output sample

```json
{
  "hotelId": 50908,
  "name": "Bradford Elysées - Astotel",
  "url": "https://www.booking.com/hotel/fr/bradfordelysees.html",
  "accommodationType": "Hotel",
  "address": "10 rue Saint-Philippe-du-Roule",
  "city": "Paris",
  "countryCode": "fr",
  "latitude": 48.8729132030409,
  "longitude": 2.30819128448275,
  "neighborhood": "Champs Elysées, Paris",
  "distanceFromCenter": "1.4 miles from downtown",
  "reviewScore": 9.1,
  "reviewsCount": 1113,
  "reviewScoreWord": "Wonderful",
  "starRating": 4,
  "priceTotal": 517.08,
  "pricePerNight": 258.54,
  "priceFormatted": "$517.08",
  "currency": "USD",
  "taxesAndFees": "+$27 taxes and fees",
  "roomType": "Superior Twin Room",
  "beds": 2,
  "freeCancellation": true,
  "noPrepayment": false,
  "isPreferredPartner": true,
  "photoUrl": "https://cf.bstatic.com/xdata/images/hotel/square600/346706084.jpg",
  "checkIn": "2026-09-01",
  "checkOut": "2026-09-03",
  "nights": 2,
  "adults": 2,
  "rooms": 1,
  "children": 0,
  "searchQuery": "Paris",
  "scrapedAt": "2026-08-09T22:41:00.000Z"
}
```

# Actor input Schema

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

What to search. Anything Booking.com's own search box accepts works: a city ("Paris"), a district ("Manhattan, New York"), a landmark ("Eiffel Tower"), an airport ("JFK Airport"), a country ("Japan") or a specific property name ("Hilton Paris Opera"). Up to 20 per run.

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

Start of the stay. Booking.com only quotes prices for a concrete stay, so if you leave this empty the actor uses a 1-night stay 30 days from now and writes the dates it used onto every row.

## `checkOut` (type: `string`):

End of the stay. Must be after check-in. Defaults to one night after check-in.

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

Adults per room. Changes which rooms are matched and therefore the quoted price.

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

Number of rooms requested.

## `childrenAges` (type: `array`):

One entry per child, each the child's age in years (0-17). Leave empty for an adults-only search.

## `maxItemsPerDestination` (type: `integer`):

How many properties to return for each destination. Booking.com stops serving new properties at roughly 1,000 per search, which is the hard cap here.

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

Sort order, using Booking.com's own sorters. "popularity" is the site default (Our top picks).

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

Three-letter currency code. Booking.com converts the quoted price into it.

## `language` (type: `string`):

Language for the human-readable text fields (neighborhood, distance, review word, room type).

## `minPricePerNight` (type: `integer`):

Booking.com's own budget filter, in the selected currency, per night. Leave empty for no minimum.

## `maxPricePerNight` (type: `integer`):

Booking.com's own budget filter, in the selected currency, per night. Leave empty for no maximum.

## `minStars` (type: `integer`):

Only return properties with at least this star rating (1-5). Leave empty for any.

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

Booking.com only offers four review-score buckets. Pick one, or leave empty for any score. Note that Booking.com's own filter still lets brand-new properties with zero reviews through - those rows come back with reviewScore 0 and reviewsCount 0.

## `propertyType` (type: `string`):

Restrict to one Booking.com property type. Leave empty for all types.

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

Only return properties whose matched room offers free cancellation.

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

Only return properties whose matched room includes breakfast.

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

Optional. Booking.com's search API is not IP-gated, so the free Apify datacenter pool is used by default. Do NOT select RESIDENTIAL - it is billed at $8/GB and buys nothing here; the actor strips it if selected.

## Actor input object example

```json
{
  "destinations": [
    "Paris"
  ],
  "adults": 2,
  "rooms": 1,
  "maxItemsPerDestination": 100,
  "sortBy": "popularity",
  "currency": "USD",
  "language": "en-us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One dataset row per property Booking.com returned for the search, with its price, guest score, star rating and room details. Empty input and failed searches write an uncharged sample or diagnostic row instead.

# 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"
    ],
    "adults": 2,
    "rooms": 1,
    "maxItemsPerDestination": 100,
    "sortBy": "popularity",
    "currency": "USD",
    "language": "en-us",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/booking-hotels-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"],
    "adults": 2,
    "rooms": 1,
    "maxItemsPerDestination": 100,
    "sortBy": "popularity",
    "currency": "USD",
    "language": "en-us",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/booking-hotels-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"
  ],
  "adults": 2,
  "rooms": 1,
  "maxItemsPerDestination": 100,
  "sortBy": "popularity",
  "currency": "USD",
  "language": "en-us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call dami_studio/booking-hotels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/booking-hotels-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/baobMehb6IXXR8qQQ/builds/8ZllrLBdV7GJoGN1m/openapi.json
