# Booking Hotels Scraper - Most Comprehensive (`kaix/booking-hotel-scraper`) Actor

🔥 ~$0.1/1K hotels 🔥 Get hotel profiles, guest reviews, facilities, photos, nearby places, and stay availability by hotel or destination.

- **URL**: https://apify.com/kaix/booking-hotel-scraper.md
- **Developed by:** [Kai](https://apify.com/kaix) (community)
- **Categories:** E-commerce, Travel, Real estate
- **Stats:** 3 total users, 2 monthly users, 99.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.08 / 1,000 hotels

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## Booking Hotels

Search a destination or collect details for specific Booking.com hotels. Results
can include property information, photos, facilities, nearby places, guest
reviews, stay availability, rooms, and rates.

### Search hotels

For a normal availability search, enter `destination` and `stay` dates. A
destination without complete dates is accepted and completes without results.
If both a destination and hotel references are present, the destination takes
precedence, matching the original flat-input behavior.

If you already know the exact hotels, use `hotels` instead of `destination`.
Add one or more Booking.com hotel URLs or numeric IDs. Dates are optional unless
you need current availability.

For the recommended nested input, use either `destination` or `hotels`, not
both. Existing integrations that use the original flat API remain supported.
Set `results.includeReviews` to `true` to collect individual guest reviews.

### Quick start

Search a destination with stay details and optional filters. Relative dates are
resolved when the run starts, so this example remains ready to use:

```json
{
  "destination": "Tokyo, Japan",
  "stay": {
    "checkIn": "60 days",
    "checkOut": "63 days",
    "adults": 2,
    "childrenAges": [],
    "rooms": 1,
    "currency": "JPY"
  },
  "results": {
    "maxHotels": 10,
    "sortBy": "guest_rating",
    "includeReviews": false
  },
  "filters": {
    "starRatings": ["4", "5"],
    "minimumReviewScore": 8,
    "propertyTypes": ["Hotels"],
    "hotelAmenities": ["Free WiFi"]
  }
}
```

Get specific hotels by URL or numeric ID:

```json
{
  "hotels": [
    "https://www.booking.com/hotel/jp/park-tokyo.html"
  ],
  "results": {
    "includeReviews": false
  }
}
```

Existing API clients can keep sending the original flat input without a
migration. For example:

```json
{
  "urls": ["179801", "not-a-booking-url"],
  "includeReviews": false,
  "maxReviews": 20,
  "language": "en-us"
}
```

Each value in `urls` is resolved independently. A malformed or unavailable
reference does not reject valid siblings.

### Input

The examples in this section are input fragments. Start with `destination` and
`stay` for a search, or use `hotels` for exact properties. Dates can be exact,
such as `2027-12-15`, or relative to the UTC run date, such as `60 days`.
The Store form prefills a Tokyo example; API calls do not add a destination or
stay unless you provide them.

#### Main fields

| Field | Type | API default | How to use it | Example |
| --- | --- | --- | --- | --- |
| `destination` | string | none | Enter a city, area, landmark, airport, or hotel name to search. Add `stay` and leave `hotels` empty. | `"Tokyo, Japan"` |
| `stay` | object | none | Set dates and guests for availability. Partial or omitted dates are accepted; without both dates, no availability search is run. | `{"checkIn":"60 days","checkOut":"63 days"}` |
| `hotels` | string\[] | none | Add Booking.com hotel URLs or numeric IDs when you already know the exact hotels. Leave `destination` empty; duplicate references are saved once. | `["179801"]` |
| `results` | object | shown below | Set search result limits, ordering, and review collection. With hotel URLs or IDs, change only the review settings. | `{"maxHotels":10,"includeReviews":true}` |
| `filters` | object | none | Narrow a text search. Filters are ignored when destination search is not selected. | `{"starRatings":["4","5"]}` |
| `language` | string | `en-us` | Choose the locale for returned text and localized filter labels. Common values are `en-us`, `en-gb`, `fr`, `de`, `es`, `it`, and `ja`. | `"fr"` |
| `proxyConfiguration` | object | Apify Proxy enabled | Leave the default for normal runs. Change it only when your Apify account requires different connection settings. | `{"useApifyProxy":true}` |

For the recommended nested form, provide one of `destination` or `hotels`.
Filters, `results.maxHotels`, and `results.sortBy` apply only to destination
searches. Unknown fields are retained for forward compatibility. In the legacy
flat form, `search` takes precedence when both `search` and `urls` are present,
matching the original Actor behavior.

#### `stay`

| Field | Type | API default | How to use it | Example |
| --- | --- | --- | --- | --- |
| `checkIn` | string | none | Choose an exact date as `YYYY-MM-DD`, or a date relative to the UTC run date using days, weeks, months, or years. | `"60 days"` |
| `checkOut` | string | none | Choose an exact or relative date. Relative dates are resolved when the run starts; date ordering remains upstream-owned for compatibility. | `"63 days"` |
| `adults` | integer | `2` | Set the number of adult guests for the whole stay, from 1 to 30. | `2` |
| `childrenAges` | integer\[] | `[]` | Add one age, from 0 to 17, for each child. The Actor derives the child count from this list; do not add a separate `children` field. | `[4,9]` |
| `rooms` | integer | `1` | Set the total number of rooms, from 1 to 30. Guests apply to the stay as a whole, not to individual rooms. | `2` |
| `currency` | string | none | Request stay prices in a three-letter uppercase currency. A price filter without currency is ignored. | `"JPY"` |

#### `results`

| Field | Type | API default | How to use it | Example |
| --- | --- | --- | --- | --- |
| `maxHotels` | integer | `20` | Limit a text search to at most `250` hotels. Use `0` to continue until the available result pages end. Do not change it for hotel URLs or IDs. | `10` |
| `sortBy` | string | `recommended` | Order text-search results by `recommended`, `guest_rating`, `price_lowest`, `distance`, `stars_highest`, or `stars_lowest`. Do not change it for hotel URLs or IDs. | `"guest_rating"` |
| `includeReviews` | boolean | `false` | Set `true` to add individual reviews to each hotel record. This works for searches and specific hotels. | `true` |
| `maxReviewsPerHotel` | integer | `20` | When reviews are enabled, stop after this many reviews per hotel, up to `1000`. Use `0` to collect all available reviews. | `50` |

#### `filters`

All filters apply only to `destination` searches.

| Field | Type | API default | How to use it | Example |
| --- | --- | --- | --- | --- |
| `starRatings` | string\[] | `[]` | Keep hotels with any selected star rating. Values are strings from `1` through `5`; you can select more than one. | `["4","5"]` |
| `minimumTotalPrice` | number | none | Set the lowest total price for the full stay. The price filter is used only when both bounds and `stay.currency` are present. | `30000` |
| `maximumTotalPrice` | number | none | Set the highest total price for the full stay. Incomplete price filters are ignored. | `60000` |
| `minimumReviewScore` | integer | none | Keep hotels with a guest score at or above this whole number, from 5 through 9. | `8` |
| `propertyTypes` | string\[] | `[]` | Keep selected property types. Use full labels available for the same destination, stay, and language. | `["Hotels"]` |
| `neighborhoods` | string\[] | `[]` | Limit results to named neighborhoods or districts available for the destination. | `["Shinjuku Ward"]` |
| `mealPlans` | string\[] | `[]` | Keep stays offering the selected meal plans. Use the full localized labels. | `["Breakfast included"]` |
| `hotelAmenities` | string\[] | `[]` | Keep properties with the selected hotel-wide amenities. | `["Free WiFi"]` |
| `roomAmenities` | string\[] | `[]` | Keep stays with the selected in-room amenities. | `["Balcony"]` |
| `bedPreferences` | string\[] | `[]` | Request the selected bed option when it is available for the destination and stay. | `["Double bed"]` |
| `brands` | string\[] | `[]` | Keep hotels from the selected chains or brands available at the destination. | `["Mitsui Garden"]` |
| `freeCancellation` | boolean | `false` | Set `true` to keep stays that offer a free-cancellation option. | `true` |
| `distanceFromCenter` | string | none | Set the maximum distance from the destination center: `1km`, `3km`, or `5km`. | `"3km"` |
| `sustainabilityCertified` | boolean | `false` | Set `true` to keep properties marked with a sustainability certification. | `true` |
| `minimumBedrooms` | integer | none | For apartments or homes, require at least this many bedrooms, from 1 to 10. | `2` |
| `minimumBeds` | integer | none | Require at least this many beds, from 1 to 10. | `2` |
| `travelPurpose` | string | none | Set the stay context to `business` or `leisure`. | `"business"` |

Property, neighborhood, meal, amenity, bed, and brand names depend on the
destination, stay, and selected `language`. Copy the full localized label shown
in Booking.com's filters for the same search, such as `Hotels` or `Free WiFi`.
Matching ignores capitalization and extra spaces. Names that are unavailable
or ambiguous are ignored without failing the run.

#### Legacy flat API compatibility

The following original top-level fields remain accepted for existing API and
CLI integrations. New integrations may use the nested fields above.

| Legacy field | Maps to | Legacy default |
| --- | --- | --- |
| `urls` | Specific hotel references | none |
| `search` | Destination text | none |
| `maxResults` | Maximum search results; `0` means all available | `20` |
| `checkIn`, `checkOut` | Stay dates passed through to Booking.com | none |
| `adults` | Adult guests | `2` |
| `children` | Child count | `0` |
| `childrenAges` | Child ages | none |
| `rooms` | Room count | `1` |
| `sortBy` | `relevance`, `rating_highest`, `rating_lowest`, `newest_first`, or `oldest_first` | `relevance` |
| `includeReviews` | Include individual reviews | `true` |
| `maxReviews` | Reviews per hotel; `0` means all available | `20` |
| `filterStarRating` | Star-rating filter | none |
| `filterPrice` | `{min,max,currency}` total-price filter | none |
| `filterReviewScore` | Review-score bucket such as `8+` | none |
| `filterPropertyType` | Property type IDs or values | none |
| `filterNeighborhood` | District IDs or values | none |
| `filterMeals` | Meal-plan IDs or values | none |
| `filterFacilities` | Hotel facility IDs or values | none |
| `filterRoomFacilities` | Room facility IDs or values | none |
| `filterBedPreference` | Bed preference IDs or values | none |
| `filterBrands` | Brand IDs or values | none |
| `filterFreeCancellation` | Require free cancellation | `false` |
| `filterDistanceFromCenter` | Maximum distance from center | none |
| `filterSustainability` | Require sustainability marker | `false` |
| `filterNumberOfBedrooms` | Minimum bedrooms | none |
| `filterNumberOfBeds` | Minimum beds | none |
| `filterTravelPurpose` | `business` or `leisure` | none |
| `language` | Result locale | `en-us` |
| `proxyConfiguration` | Apify connection settings | Apify Proxy enabled |

### What you get

The Actor saves one record for each returned hotel.

#### Identity and completeness

| Field | Meaning |
| --- | --- |
| `recordType` | Always `hotel`. |
| `status` | `complete` when all requested sections were collected; otherwise `partial`. |
| `failedSections` | Sections that could not be collected safely. Empty on a complete record. |
| `inputSource` | The URL, hotel ID, or destination search that produced the record. |
| `hotelId` | Numeric Booking.com hotel ID. |
| `name` | Hotel name. |
| `url` | Booking.com hotel URL when available. |
| `scrapedAt` | UTC timestamp for when the record was created. |

`failedSections` can contain `propertyInfo`, `reviewSummary`, `facilities`,
`photos`, `surroundings`, `food`, `availability`, `location`, or `reviews`.

#### Property

| Field | Meaning |
| --- | --- |
| `accommodationType` | Property type, such as hotel or apartment. |
| `address`, `city`, `countryCode` | Property location. |
| `latitude`, `longitude` | Geographic coordinates when available. |
| `starRating` | Rating value and rating symbol. |
| `preferredLevel` | Preferred-property level when provided. |
| `isTravelProud` | Whether the property is marked Travel Proud. |
| `isSustainable` | Whether the property has a sustainability marker. |
| `description`, `summary` | Property description and short summary. |
| `houseRules` | Check-in and check-out times. |
| `legalInfo` | Trader status and available company contact details. |
| `finePrints` | Important property notices and conditions. |

#### Amenities and guest feedback

| Field | Meaning |
| --- | --- |
| `facilities` | Hotel facilities, including charge information when available. |
| `photos` | Original-size photo URLs, captions, types, and tags. |
| `surroundings` | Nearby places grouped by category, with distance text. |
| `meals` | Available meal information. |
| `restaurants` | On-site restaurants and cuisines. |
| `reviewScore`, `reviewsCount` | Overall guest score and advertised review count. |
| `ratingScores` | Category scores such as cleanliness, comfort, and location. |
| `featuredReviews` | Review highlights included with the hotel profile. |
| `reviews` | Individual reviews when `results.includeReviews` is `true`. |
| `reviewsMetadata` | Advertised, returned, and completion details for review collection. |

Each individual review can include its score, title, positive and negative
text, Unix timestamp in seconds, guest name and country, language, review
URL, and avatar URL. `reviewsMetadata` contains `advertisedCount`,
`returnedCount`, `complete`, and `topicIds`. `returnedCount` always matches the
saved `reviews` array. `complete: true` means the requested limit was reached
or all available reviews were collected; `advertisedCount` can still be larger
when you requested a smaller limit.

#### Stay and availability

| Field | Meaning |
| --- | --- |
| `stayRequest` | The dates, guests, rooms, and requested currency used for the availability check. Dates are always returned as absolute `YYYY-MM-DD` values. |
| `availability` | Whether a matching stay exists, structured prices, cancellation availability, rate components, and the matching room configuration. |
| `pricing` | Display price and average nightly price when provided with the search result. |
| `availableRooms` | Matching room IDs, names, occupancy, bed counts, and bathroom counts when available. |

Structured money objects inside `availability` contain a decimal `amount`,
`currency`, and optional formatted text. The compact `pricing` fields and meal
prices are formatted strings. When dates are supplied, `availability` is
always present. On a `complete` record, `hasAvailability: false` means no
matching stay was found for those dates and guests. If availability cannot be
interpreted safely, the record is `partial` and `failedSections` includes
`availability`.

Optional fields appear only when they apply. For example, `reviews` and
`reviewsMetadata` require `includeReviews: true`; `stayRequest` and
`availability` require `stay`. A `null` value means the field applies but no
reliable value was returned. An empty array means no items were returned for
that section.

#### Verified output

This trimmed selection came from a real Actor run on July 31, 2026:

```json
{
  "recordType": "hotel",
  "status": "complete",
  "failedSections": [],
  "hotelId": 179801,
  "name": "Park Hotel Tokyo",
  "city": "Tokyo",
  "countryCode": "jp",
  "starRating": {
    "value": 4,
    "symbol": "STARS"
  },
  "reviewScore": 9,
  "reviewsCount": 3489,
  "stayRequest": {
    "checkIn": "2026-09-29",
    "checkOut": "2026-10-01",
    "adults": 2,
    "children": 0,
    "childrenAges": [],
    "rooms": 1,
    "currency": "JPY"
  },
  "availability": {
    "hasAvailability": true,
    "price": {
      "total": {
        "amount": "97988.9328",
        "currency": "JPY",
        "formatted": "¥97,989"
      },
      "originalTotal": null,
      "averagePerNight": null,
      "chargesText": [],
      "taxExceptionsText": []
    },
    "freeCancellationAvailable": true,
    "rateComponents": [
      {
        "roomId": "17980101",
        "occupancy": 2,
        "mealPlanId": 2,
        "policyGroupId": "366275015",
        "packageId": "0",
        "finalPrice": {
          "amount": "97988.9328",
          "currency": "JPY",
          "formatted": null
        },
        "originalPrice": {
          "amount": "97988.9328",
          "currency": "JPY",
          "formatted": null
        },
        "freeCancellationUntil": "2026-09-27T15:00:00.000Z"
      }
    ],
    "matchingUnitConfiguration": {
      "name": "Queen Room - Non Smoking - Above 27th floor",
      "area": null,
      "beds": 1,
      "bedrooms": 0,
      "livingRooms": 0,
      "kitchens": 0,
      "bathrooms": 0,
      "unitCount": 1,
      "units": [
        {
          "unitTypeId": 9,
          "name": "Queen Room - Non Smoking - Above 27th floor",
          "unitCount": 1
        }
      ]
    }
  },
  "scrapedAt": "2026-07-31T13:43:40.200Z"
}
```

### Limits and expectations

- `results.maxHotels: 0` and `results.maxReviewsPerHotel: 0` continue until the
  currently available pages end and can make a run much longer.
- Review arrays stay inside their hotel record. Collection stops safely and
  marks the record `partial` before the dataset item becomes too large.
- A valid destination, filter combination, or dated stay can return no matching
  hotel. Dated hotel records report this with
  `availability.hasAvailability: false`.
- Hotel URLs and IDs are handled independently. Malformed or unavailable
  references are skipped while valid siblings are still saved. If none can be
  returned, the run completes with zero records instead of discarding earlier
  work or changing a previously accepted input into a failed run.
- An omitted selection or an empty `hotels`/legacy `urls` list is a successful
  zero-work run that saves no records.
- Guest counts apply to the stay as a whole. Room-by-room guest allocation is
  not supported.
- Results do not include every bookable rate plan, complete payment and
  cancellation terms, every tax schedule, or a checkout flow.
- Booker country, platform-specific rates, and membership deals cannot be
  selected independently.
- Flexible dates and coordinate-radius search are not input options.
- Review sorting, traveler filters, topic filters, and review-text search are
  not input options.
- Optional sections can be temporarily unavailable. Check `status` and
  `failedSections` before treating a hotel record as complete.

# Actor input Schema

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

Enter a city, area, landmark, airport, or hotel name. Then choose check-in and check-out below. You do not need a hotel URL.

## `stay` (type: `object`):

Required for a destination search. Choose exact dates or dates relative to the run date. Dates are optional for specific hotels.

## `hotels` (type: `array`):

Use this instead of Destination when you already know the exact hotels. Add one or more Booking.com hotel URLs or numeric IDs. Dates are optional.

## `results` (type: `object`):

Set search result limits, ordering, and review collection. With hotel URLs or IDs, change only the review settings.

## `filters` (type: `object`):

Use these only for a destination or hotel-name search. Text labels must be available for the same search, stay, and language; capitalization and extra spaces are ignored.

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

Choose the locale for returned text and localized filter labels. You can enter another supported locale instead of the suggested values.

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

Leave the default for normal runs. Change it only when your Apify account requires different connection settings.

## Actor input object example

```json
{
  "destination": "Tokyo, Japan",
  "stay": {
    "checkIn": "60 days",
    "checkOut": "63 days",
    "adults": 2,
    "childrenAges": [],
    "rooms": 1,
    "currency": "EUR"
  },
  "hotels": [
    "https://www.booking.com/hotel/jp/park-tokyo.html"
  ],
  "results": {
    "maxHotels": 10,
    "sortBy": "guest_rating",
    "includeReviews": true,
    "maxReviewsPerHotel": 50
  },
  "filters": {
    "starRatings": [
      "4",
      "5"
    ],
    "minimumReviewScore": 8,
    "freeCancellation": true
  },
  "language": "fr",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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": "Tokyo, Japan",
    "stay": {
        "checkIn": "60 days",
        "checkOut": "63 days",
        "adults": 2,
        "childrenAges": [],
        "rooms": 1
    },
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kaix/booking-hotel-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 = {
    "destination": "Tokyo, Japan",
    "stay": {
        "checkIn": "60 days",
        "checkOut": "63 days",
        "adults": 2,
        "childrenAges": [],
        "rooms": 1,
    },
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("kaix/booking-hotel-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 '{
  "destination": "Tokyo, Japan",
  "stay": {
    "checkIn": "60 days",
    "checkOut": "63 days",
    "adults": 2,
    "childrenAges": [],
    "rooms": 1
  },
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call kaix/booking-hotel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kaix/booking-hotel-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/pW7h7RXiZlmEXax4f/builds/DxJCfjIkVl4jBKpQj/openapi.json
