# Booking.com Availability & Rate Calendar Scraper (`integrative_tangent/booking-calendar-scraper`) Actor

Track Booking.com availability and nightly rates date by date. Per-property calendar with occupancy rate, min/max/median prices, weekend vs weekday rates, minimum-stay detection and remaining offers per night. No account or API key needed.

- **URL**: https://apify.com/integrative\_tangent/booking-calendar-scraper.md
- **Developed by:** [Luis Segura](https://apify.com/integrative_tangent) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 property calendars

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Availability & Rate Calendar Scraper

Track **availability and nightly rates on Booking.com date by date**. Give it a property, a search, or a whole destination, and get back a calendar: which nights are open, which are gone, and what each one costs.

A Booking.com scraper built for availability tracking, hotel price monitoring, occupancy analysis and rate shopping — hotels, apartments, villas, hostels and B\&Bs, anywhere Booking.com lists them.

**No Booking.com account required. No API key needed.**

***

### Why a calendar, not a listing dump

Most Booking.com scrapers answer *"what does this property cost today?"*. That is one number, and it is stale tomorrow.

This actor answers the questions that number can't:

- **How full is this property?** 30 nights checked, 11 with no rooms left → 37% booked.
- **How does it price the calendar?** Weekend average vs weekday average, min, max, median.
- **When does it get expensive?** A rate per date, so you can see the season, the holiday spike, the soft mid-week.
- **Is it sold out, or is it a minimum-stay rule?** Turn on `detectMinStay` and the actor probes longer stays before calling a night sold out — a distinction that quietly wrecks occupancy estimates when nobody makes it.

That is the difference between a price check and a revenue-management input.

***

### What You Get

One dataset item per property.

#### Property

| Field | Description |
|---|---|
| `propertyId` | Booking.com internal hotel ID |
| `propertyName` | Property name |
| `url` | Canonical property URL |
| `city`, `country` | Location |
| `starRating` | Official star rating |
| `reviewScore` | Guest review score (0–10) |
| `latitude`, `longitude` | Coordinates |
| `currency` | Currency of every rate in the item |

#### Window summary

| Field | Description |
|---|---|
| `startDate`, `endDate` | First and last check-in date probed |
| `lengthOfStay` | Nights priced per date |
| `daysChecked` | Dates successfully checked |
| `daysAvailable` | Dates with at least one bookable room |
| `daysSoldOut` | Dates with nothing bookable |
| `daysFailed` | Dates that could not be checked (see `calendar[].error`) |
| `occupancyRate` | `daysSoldOut / daysChecked` — the occupancy proxy |
| `minRate`, `maxRate`, `avgRate`, `medianRate` | Nightly rate statistics |
| `weekendAvgRate`, `weekdayAvgRate` | Friday/Saturday vs Sunday–Thursday check-ins |

#### Calendar (one entry per date)

| Field | Description |
|---|---|
| `date`, `checkout`, `nights` | The stay window probed |
| `available` | Whether anything was bookable |
| `minStayNights` | Detected minimum stay, when longer than requested |
| `cheapestTotal`, `cheapestPerNight` | Cheapest offer for that stay |
| `priceFormatted` | Price as Booking.com displays it |
| `roomsOffered` | How many rate offers came back |
| `cheapestRoomName` | Room type of the cheapest offer |
| `mealPlan` | e.g. `all_inclusive`, `breakfast_included` |
| `cancellationType` | e.g. `free_cancellation`, `non_refundable` |
| `isGeniusRate` | Whether the cheapest offer is a Genius rate |
| `isWeekend` | Friday or Saturday check-in |
| `error` | Why a date could not be checked (`null` when fine) |

***

### How to Use It

#### Mode A — specific properties

```json
{
  "propertyUrls": [
    "https://www.booking.com/hotel/do/barcelo-bavaro-palace-deluxe.html",
    "https://www.booking.com/hotel/do/hyatt-zilara-cap-cana.html"
  ],
  "daysAhead": 30,
  "lengthOfStay": 2,
  "currency": "USD"
}
```

Any Booking.com URL works — any language, any tracking parameters. They are normalised for you.

#### Mode B — a search you already filtered

```json
{
  "searchUrl": "https://www.booking.com/searchresults.html?ss=Punta+Cana&nflt=class%3D5",
  "maxProperties": 25,
  "daysAhead": 60
}
```

#### Mode C — a whole destination

```json
{
  "location": "Las Terrenas",
  "maxProperties": 20,
  "daysAhead": 90,
  "detectMinStay": true
}
```

***

### Sample Output

```json
{
  "propertyId": "24708",
  "propertyName": "Barceló Bávaro Palace All Inclusive",
  "city": "Punta Cana",
  "country": "Dominican Republic",
  "starRating": 5,
  "reviewScore": 8.1,
  "currency": "USD",
  "startDate": "2026-11-20",
  "endDate": "2026-12-19",
  "lengthOfStay": 2,
  "daysChecked": 30,
  "daysAvailable": 24,
  "daysSoldOut": 6,
  "occupancyRate": 0.2,
  "minRate": 742.11,
  "maxRate": 1204.5,
  "avgRate": 889.34,
  "medianRate": 861.2,
  "weekendAvgRate": 976.4,
  "weekdayAvgRate": 852.7,
  "calendar": [
    {
      "date": "2026-11-20",
      "checkout": "2026-11-22",
      "nights": 2,
      "available": true,
      "minStayNights": null,
      "cheapestTotal": 1600.0,
      "cheapestPerNight": 800.0,
      "priceFormatted": "US$1,600",
      "roomsOffered": 12,
      "cheapestRoomName": "Family Double Room",
      "mealPlan": "all_inclusive",
      "cancellationType": "free_cancellation",
      "isGeniusRate": true,
      "isWeekend": true,
      "error": null
    }
  ],
  "scrapedAt": "2026-09-09T14:12:03.918Z"
}
```

***

### Use Cases

- **Revenue management** — watch your competitive set's rate and availability curve, and reprice against it.
- **Hotel and STR market research** — occupancy and ADR proxies for a destination, without buying a data subscription.
- **Investment and acquisition analysis** — seasonality and pricing power of a property before you buy it.
- **Travel deal monitoring** — schedule a daily run and catch the night a rate drops.
- **OTA parity checks** — compare Booking.com rates against your direct channel.

Schedule it daily and each run is a snapshot; stacked over weeks, the dataset becomes a booking-pace history nobody sells off the shelf.

***

### Pricing & Cost

**$0.008 per property calendar**, whatever the window length — one item, one charge.

| Run | Properties | Result items | Actor cost |
|---|---|---|---|
| One property, 30 nights | 1 | 1 | $0.008 |
| Competitive set, 30 nights | 10 | 10 | $0.08 |
| Destination scan, 60 nights | 50 | 50 | $0.40 |

Platform usage (proxy and compute) is billed to your own Apify account on top, as it is by every availability monitor on the Store. That matters here because **each check-in date is one request per property**: a 30-day window moves about 10 MB per property, a 90-day window about 30 MB. Choose your proxy tier with that in mind, and start with a small `daysAhead` and `maxProperties` before scaling up.

***

### Proxy Recommendations

| Volume | Recommended setting |
|---|---|
| Under ~50 requests | Apify Proxy, datacenter, is usually fine |
| Anything larger | Apify Proxy with the **RESIDENTIAL** group |
| Large destination scans | RESIDENTIAL plus `requestsPerSecond: 1` |

The actor adapts its own rate: it backs off and refreshes its session when Booking.com pushes back, then speeds up again while requests succeed.

***

### Limitations

- **Rates are the cheapest bookable offer** for the requested guest count and stay length. Change `adults`, `rooms` or `lengthOfStay` and the numbers change — that is Booking.com's behaviour, not a bug.
- **`occupancyRate` is a proxy, not booked occupancy.** It measures what the public site refuses to sell you. A property closed for renovation and a property fully booked look the same from outside.
- **One-night probes are unreliable** on properties with a two-night minimum. That is why `lengthOfStay` defaults to 2 and why `detectMinStay` exists.
- **Long horizons return less data.** Booking.com stops selling beyond roughly 16 months out, and many properties load far less than that.
- **Dates that could not be checked are reported, not hidden** — they appear in `daysFailed` with a reason in `calendar[].error`, so a partial run is visible instead of masquerading as a sold-out property.

***

### Frequently Asked Questions

**Do I need a Booking.com account or an API key?**
No. Everything comes from the public property pages, exactly what any visitor sees without logging in.

**How is the occupancy rate calculated?**
`daysSoldOut / daysChecked` — the share of probed check-in dates where Booking.com offered no bookable room for your guest count and stay length. It is a proxy for occupancy, not booked occupancy: a property closed for renovation looks the same from outside.

**Can I run it every day to build price history?**
Yes, and that is the point. Put it on an Apify schedule and each run is a snapshot; stacked over weeks you get booking pace and rate history for your competitive set. Pin `apifyProxyCountry` so every run measures from the same country — Booking.com prices vary by visitor location.

**What kinds of property does it handle?**
Anything with a Booking.com property page: hotels, resorts, apartments, aparthotels, villas, hostels, guesthouses and B\&Bs, worldwide.

**Can I export the data to Excel, CSV or Google Sheets?**
Yes. Every Apify dataset exports to CSV, Excel, JSON, XML or HTML, and can be pushed to Google Sheets through Apify integrations. Use the `unwind=calendar` export option to get one row per date instead of one row per property.

**How is this different from a Booking.com price scraper?**
A price scraper answers "what does this cost today". This one returns the whole curve: a rate and an availability flag for every check-in date in the window, plus how many offers are left on each night — which is what tells you whether a rate is about to move.

**Does it detect minimum-stay restrictions?**
Yes, with `detectMinStay` on. When a date returns nothing, it retries with longer stays before calling the night sold out, and writes the answer to `minStayNights`. Without that step, every property with a two-night minimum looks fully booked.

**Is scraping Booking.com legal?**
This Actor reads only public, non-personal information. You are responsible for how you use it, including Booking.com's Terms of Service and the data-protection rules where you operate.

***

### Legal

This actor collects only publicly available information from Booking.com property pages — the same rates and availability any visitor sees without logging in. It collects no personal data and no guest information.

You are responsible for how you use the output, including compliance with Booking.com's Terms of Service and with the data-protection rules that apply where you operate.

# Actor input Schema

## `propertyUrls` (type: `array`):

Booking.com hotel page URLs. Any locale and any query string works — they are normalised automatically. Example: \["https://www.booking.com/hotel/do/barcelo-bavaro-palace-deluxe.html"]

## `searchUrl` (type: `string`):

A Booking.com search results URL. Every property on it is discovered and scraped. Example: "https://www.booking.com/searchresults.html?ss=Punta+Cana"

## `location` (type: `string`):

A city, region or area name, resolved through Booking.com's own search. Example: "Punta Cana"

## `startDate` (type: `string`):

First check-in date to probe, as YYYY-MM-DD. Defaults to tomorrow. Past dates are moved forward automatically.

## `daysAhead` (type: `integer`):

How many consecutive check-in dates to probe, starting from the start date (1–365).

This is the main cost driver: each date is one request per property. 30 days is a good default; 90 covers a full booking season.

## `lengthOfStay` (type: `integer`):

Stay length used to price each check-in date.

Keep this at 2 unless you have a reason not to. A large share of properties enforce a two-night minimum, and a one-night probe against those returns no rooms — which looks exactly like sold out.

## `detectMinStay` (type: `boolean`):

When a date comes back with no rooms, retry it with longer stays (+1, +2, +3 nights) to find out whether the property is actually sold out or just enforcing a minimum stay. The answer is written to `minStayNights`.

Costs up to 3 extra requests per unavailable date — accurate, but slower.

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

Number of adults in the search. Changes which rooms are offered and at what price.

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

Number of children in the search.

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

Number of rooms requested.

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

Currency for all rates in the output.

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

Cap on how many properties to scrape. 0 means no limit. Always set this when using a destination or search URL.

## `requestsPerSecond` (type: `number`):

Starting request rate. The actor adapts on its own — it backs off when Booking.com pushes back and speeds up while requests succeed. Range 0.25–5.

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

Strongly recommended: Apify Proxy with the RESIDENTIAL group. Datacenter IPs get challenged quickly on Booking.com once a run goes beyond a handful of requests.

## Actor input object example

```json
{
  "propertyUrls": [
    "https://www.booking.com/hotel/do/barcelo-bavaro-palace-deluxe.html"
  ],
  "daysAhead": 7,
  "lengthOfStay": 2,
  "detectMinStay": false,
  "adults": 2,
  "children": 0,
  "rooms": 1,
  "currency": "USD",
  "maxProperties": 3,
  "requestsPerSecond": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One item per property: summary statistics plus the day-by-day calendar.

## `summary` (type: `string`):

Totals for the run: properties processed, nights open, nights sold out, failed requests.

# 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 = {
    "propertyUrls": [
        "https://www.booking.com/hotel/do/barcelo-bavaro-palace-deluxe.html"
    ],
    "daysAhead": 7,
    "maxProperties": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("integrative_tangent/booking-calendar-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 = {
    "propertyUrls": ["https://www.booking.com/hotel/do/barcelo-bavaro-palace-deluxe.html"],
    "daysAhead": 7,
    "maxProperties": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("integrative_tangent/booking-calendar-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 '{
  "propertyUrls": [
    "https://www.booking.com/hotel/do/barcelo-bavaro-palace-deluxe.html"
  ],
  "daysAhead": 7,
  "maxProperties": 3
}' |
apify call integrative_tangent/booking-calendar-scraper --silent --output-dataset

```

## MCP server setup

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