# Booking.com Hotel Scraper: Search & Stay Prices (`luminar/booking-hotels-scraper-private-v1`) Actor

Export deduplicated Booking.com search-card properties with stay totals, derived nightly prices, ratings, availability, stable IDs, and transparent coverage.

- **URL**: https://apify.com/luminar/booking-hotels-scraper-private-v1.md
- **Developed by:** [Luka](https://apify.com/luminar) (community)
- **Categories:** Business, Marketing, Travel
- **Stats:** 2 total users, 1 monthly users, 81.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.24 / 1,000 fast property results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

**Use this Booking.com hotel scraper to search by destination or public URL and export deduplicated property rows with stay-price context, ratings, availability, and transparent coverage.** The active contract is a fast search-card export: it is designed for market scans and property shortlists without silently charging for a hidden detail-page stage.

Use this **Booking scraper** for hotel and accommodation research, price snapshots, destination supply analysis, lead lists, and repeatable search exports. Each property row preserves the requested stay and the exact price basis exposed by the search result. Search-card prices are not presented as checkout-verified quotes.

### 🚀 Start in 60 seconds

Run a small destination search first:

```json
{
  "destination": "Tivat, Montenegro",
  "checkIn": "2026-09-15",
  "checkOut": "2026-09-17",
  "adults": 2,
  "rooms": 1,
  "maxProperties": 20,
  "maxPagesPerTarget": 1,
  "maxRequests": 5,
  "currency": "EUR",
  "language": "en-gb"
}
```

After the run finishes, open:

- **Property overview** for the core property, rating, availability, and stay-price fields.
- **Full export** for every schema-backed search-card field and provenance value.
- **Coverage & run summary** for the free run-level and per-target evidence.
- `OUTPUT` in the default key-value store for the machine-readable run report.

The first target can take longer than later pages in the same run. Keep the first test to one destination and one page before increasing pages, flexible dates, or result limits.

### 🎯 Choose the right workflow

#### Destination search

Use `destination` for a city, region, country, landmark, or property-name query. Add both `checkIn` and `checkOut` when the result must include stay-specific price and availability context.

This is the simplest workflow for market research and destination supply scans.

#### Search-result or property URLs

Use `startUrls` when you already have a public Booking.com search-result or property URL. URLs take precedence over `destination`. Explicit date and guest fields in the Actor input take precedence over matching query parameters already present in a URL.

Duplicate properties found across localized URLs or date windows collapse to stable canonical property rows before delivery and charging. `stablePropertyKey` is derived from the locale-neutral canonical Booking path, with a numeric-ID fallback only when no canonical URL is exposed.

#### Flexible stay comparison

Set `flexibleDays` to create shifted stay searches around the exact dates. Each output row keeps the date window that produced it. The workload grows quickly: `±7 days` can create up to 15 targets for one base search, before pagination.

Raise `maxRequests` only after calculating enough room for URLs or destinations × date targets × pages, plus bounded retries.

#### One active extraction mode

The current product contract exposes only `fast`, which returns proven search-card property data. The compatibility key remains accepted by the runtime but is hidden in the UI because there is no buyer-facing mode choice. **Full export** is a dataset view with all available columns; it is not a separate detail-page scraping mode.

### 📦 What you get

The default dataset contains two record types.

| Record | What it contains | Charge |
| --- | --- | ---: |
| `property` | Stable identity, canonical URL, name, location, stars, guest score, availability, displayed stay price, price basis, selected room text when exposed, search context, and warnings | `fast_property_result` |
| `coverage` | Per-target outcome, source observations, duplicates, pages, caps, requests, retries, runtime, warnings, and PPE counts | Free |

A representative property row:

```json
{
  "recordType": "property",
  "propertyId": "10001",
  "stablePropertyKey": "booking_path_0123456789abcdef01234567",
  "canonicalUrl": "https://www.booking.com/hotel/me/alpha.html",
  "name": "Hotel Alpha",
  "locationText": "Tivat, Montenegro",
  "stars": 4,
  "guestReviewScore": 8.7,
  "guestReviewCount": 1234,
  "availabilityStatus": "available_search_card",
  "stayTotalAmount": 312,
  "stayNights": 2,
  "nightlyAmount": 156,
  "priceCurrency": "EUR",
  "priceBasis": "search_card_total_for_stay",
  "checkoutVerified": false
}
```

Missing source values remain `null`. The Actor does not invent coordinates, amenities, taxes, policies, room details, or checkout verification when they are absent from the active search-card source.

![Booking Hotels scraper output showing Tivat property rows, ratings, and coverage](https://api.apify.com/v2/key-value-stores/DJgPR6wwS2tIaLDQW/records/booking_hotels--bdc1385c88065b84-booking-hotels-results.png)

The screenshot shows real rows from a successful verified run. It is an output example, not a completeness or live-price guarantee.

### 🎛️ Input guide

#### Search target and delivery

| Input | Use it when | Work and result impact |
| --- | --- | --- |
| `destination` | You need a text search | Ignored when `startUrls` is not empty |
| `startUrls` | You have up to ten public Booking.com URLs | Each URL can add date targets and pages |
| `maxProperties` | You want a bounded export size | Global limit across all targets and flexible windows |

#### Dates and guests

`checkIn` and `checkOut` are optional but must be supplied together. `adults` and `rooms` default to two and one. When `children` is greater than zero, `childrenAges` must contain exactly one age from 0 to 17 for each child, for example `[4, 9]` for two children.

`flexibleDays` multiplies the number of explicit stay searches. Keep exact dates when you want the lowest-work run.

#### Property filters

Property type, official stars, guest score, displayed price, and sort order are sent as supported Booking.com search filters. Empty optional filters keep the wider result set. The selected currency and locale control source display context, while each row preserves the values actually returned.

#### Limits and coverage

- `maxPagesPerTarget` caps search depth for every destination, URL, and date target.
- `maxRequests` includes search pages and bounded retries.
- `maxRuntimeSecs` stops new work at the wall-clock ceiling.
- `requestTimeoutSecs` bounds one request; a timeout becomes partial or failed coverage.
- `maxRetries` applies only to retryable temporary source and network failures. Every retry counts against `maxRequests`.

### 💰 Pricing

This Actor uses pay per event. The prices below are the current configured Store prices.

```text
total = $0.02 actor start
      + delivered unique property rows × your Apify-tier property price
```

`actor_start` is a flat **$0.02 once per run**, charged only after the source produced a usable collection or positively verified an exhausted empty search. A blocked, failed, or unverified empty source attempt does not charge the start event.

| Apify tier | Per delivered property | Per 1,000 properties | Total for 1,000 properties |
| --- | ---: | ---: | ---: |
| Free | $0.00139 | $1.390 | $1.410 |
| Bronze | $0.00134 | $1.340 | $1.360 |
| Silver | $0.00129 | $1.290 | $1.310 |
| Gold | $0.00124 | $1.240 | $1.260 |
| Platinum | $0.00119 | $1.190 | $1.210 |
| Diamond | $0.00114 | $1.140 | $1.160 |

Example on the Free tier: 20 delivered property rows cost `$0.02 + (20 × $0.00139) = $0.0478`.

At 1,000 delivered properties, Gold lowers the property-row price by **10.8%** and the complete event charge from $1.410 to $1.260, a **10.6% total reduction** after the flat start. Diamond lowers the row price by 18.0% and the complete event charge by 17.7% at the same volume.

Coverage rows, duplicate observations, filtered rows, failed targets, and suppressed rows are free. A property is charged only when the unique useful row is accepted and written to the default dataset. Raising page or request limits can increase platform work even when deduplication keeps the delivered row count unchanged.

### ✅ Coverage you can trust

The free coverage row separates delivery size from source completeness.

- `COMPLETE` requires positive evidence that every advertised page for the requested target was exhausted.
- `CAPPED` means a delivery, page, request, runtime, cost, or buyer-charge ceiling stopped the requested work.
- `PARTIAL` means some target work succeeded but at least one requested part could not be verified.
- `BLOCKED` or `FAILED` remains explicit and is never relabeled as a successful empty search.
- `EMPTY_CONFIRMED` requires positive evidence that the requested source search returned no matching property.

Per-target evidence reports target precedence, pages fetched, raw observations, unique properties, duplicates removed, requests, retries, delivery cap, warnings, and billing counts. A search-card result can report availability and a displayed price without claiming checkout verification.

### 🔌 API and automation

Use the same input through the Apify API. Keep the Actor ID and token in environment variables:

```bash
curl -X POST "https://api.apify.com/v2/acts/$ACTOR_ID/runs?token=$APIFY_TOKEN&waitForFinish=300" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": "Tivat, Montenegro",
    "checkIn": "2026-09-15",
    "checkOut": "2026-09-17",
    "adults": 2,
    "rooms": 1,
    "maxProperties": 20,
    "maxPagesPerTarget": 1
  }'
```

After completion, read `defaultDatasetId` from the run and fetch:

```text
GET https://api.apify.com/v2/datasets/{defaultDatasetId}/items?clean=true&format=json
```

For recurring market snapshots, keep destination or URLs, stay dates, guests, filters, currency, and locale stable. The Actor does not create schedules itself.

### ⚠️ Not yet supported

- Checkout-verified final prices
- Full property-detail pages and policy/room extraction
- CAPTCHA solving or login-only data

### ❓ FAQ and support

#### Does Full export scrape full property detail pages?

No. Full export means all columns available from the active fast search-card contract. It does not activate a hidden detail-page mode.

#### Are prices checkout-verified?

No. The row keeps `priceBasis`, stay dates, nights, displayed total, derived nightly amount, currency, and `checkoutVerified` so downstream users can interpret the snapshot correctly.

#### Why did I receive fewer properties than `maxProperties`?

Open **Coverage & run summary**. The source may have ended, filters or deduplication may have reduced rows, a page or safety ceiling may have stopped work, or a target may have failed.

#### What should I include in a support report?

Include the Apify run ID, input with tokens or secrets removed, the expected and observed result, and the relevant coverage row or `OUTPUT` report. Never send an API token in a support message.

# Actor input Schema

## `destination` (type: `string`):

Enter a city, region, country, landmark or property name. This field is ignored when one or more Booking.com URLs are supplied below.

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

Optional public Booking.com search-result or property URLs. URLs take precedence over Destination. Explicit date and guest fields below take precedence over matching URL query parameters.

## `maxProperties` (type: `integer`):

Maximum unique property rows delivered across every URL and flexible date window. Higher values can require more pages and runtime; reaching the limit is reported as capped, not source-complete.

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

Compatibility field for the active fast search-card contract. It is hidden because there is currently no buyer-facing mode choice.

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

Optional stay check-in date. Supply it together with Check-out to attach live price and availability context to the requested stay.

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

Optional stay check-out date. It must be after Check-in.

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

Number of adult guests used for availability and displayed-price context.

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

Number of rooms requested for the stay query.

## `children` (type: `integer`):

Number of child guests. When greater than zero, provide exactly one matching age below for every child.

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

Enter one age from 0 to 17 for each child, for example \[4, 9] for two children. The number of ages must match Children.

## `flexibleDays` (type: `string`):

Runs explicit shifted stays and keeps every row tied to exact dates. ±7 days creates up to 15 search targets and can materially increase requests, runtime and transfer.

## `propertyTypes` (type: `array`):

Optional native Booking.com property-type filters. Leave empty to keep every property type.

## `starCategories` (type: `array`):

Optional native official star-category filters. Quality ratings used by some apartments are not converted into official stars.

## `minGuestScore` (type: `number`):

Minimum Booking.com guest-review score from 0 to 10. The source may apply its own score buckets.

## `minPrice` (type: `number`):

Optional native price filter in the selected currency. Booking.com may interpret the filter as nightly while output preserves the displayed stay total and price basis.

## `maxPrice` (type: `number`):

Optional native upper price filter in the selected currency.

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

Native Booking.com result order applied to every search target.

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

Requested Booking.com display currency. Each row still preserves the currency actually exposed by the source.

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

Booking.com display locale used for localized source labels and address text.

## `maxPagesPerTarget` (type: `integer`):

Page ceiling for each destination, URL and date target. A visible next page at this ceiling is reported as capped rather than source-complete.

## `maxRequests` (type: `integer`):

Hard request total across search pages, retries and automatic routing. The run stops before the next request would exceed it.

## `maxRuntimeSecs` (type: `integer`):

Wall-clock source-work ceiling. New source work stops before another request after this time.

## `maxCostUsd` (type: `number`):

Internal conservative compute-and-transfer guard; this is not the buyer PPE charge.

## `maxBuyerChargeUsd` (type: `number`):

Optional second PPE ceiling enforced inside this Actor for integrations that cannot forward Apify's run-level maxTotalChargeUsd. It can only lower, never raise, the platform allowance.

## `requestTimeoutSecs` (type: `integer`):

Maximum wait for one live Booking.com request before that target is reported partial or failed.

## `maxRetries` (type: `integer`):

Challenge, transient HTTP and network failures can retry up to this many times. Every retry counts against Maximum live source requests.

## Actor input object example

```json
{
  "destination": "Tivat, Montenegro",
  "maxProperties": 20,
  "mode": "fast",
  "adults": 2,
  "rooms": 1,
  "children": 0,
  "childrenAges": [],
  "flexibleDays": "0",
  "sortBy": "popularity",
  "currency": "EUR",
  "language": "en-gb",
  "maxPagesPerTarget": 2,
  "maxRequests": 20,
  "maxRuntimeSecs": 240,
  "maxCostUsd": 0.25,
  "requestTimeoutSecs": 35,
  "maxRetries": 1
}
```

# Actor output Schema

## `dataset` (type: `string`):

The default dataset containing unique fast search-card property rows plus a free coverage record. Use Overview for core fields, Full export for every available column, or Coverage for run evidence.

## `runReport` (type: `string`):

The OUTPUT record with per-target outcomes, pages, duplicates, caps, requests, retries, warnings, and accepted billing-event totals.

# 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 = {
    "destination": "Tivat, Montenegro",
    "maxProperties": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("luminar/booking-hotels-scraper-private-v1").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 = {
    "destination": "Tivat, Montenegro",
    "maxProperties": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("luminar/booking-hotels-scraper-private-v1").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 '{
  "destination": "Tivat, Montenegro",
  "maxProperties": 20
}' |
apify call luminar/booking-hotels-scraper-private-v1 --silent --output-dataset

```

## MCP server setup

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

```

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/wn5AtoyZUWCZMivbx/builds/JNT8yI3uCNxNo4yfg/openapi.json
