# Airbnb Calendar Scraper - Day-by-Day Availability (`dami_studio/airbnb-calendar-scraper`) Actor

Airbnb calendar scraper: paste listing links or numbers and get one row per listing with every night for up to 12 months marked free or taken, minimum stays, check-in and check-out days, and month-by-month totals. No login. $0.75 per 1,000 calendars.

- **URL**: https://apify.com/dami_studio/airbnb-calendar-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Travel, Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.72 / 1,000 calendars

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?

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

**Paste Airbnb listing links or listing numbers and get one row per listing with its day-by-day availability calendar**: every night for up to 12 months marked free or taken, the minimum stay, the days a stay can start or end, and a month-by-month summary.

One thing to know before you build on it: a taken night can be a booking or a night the host blocked, and Airbnb's calendar does not say which.

| | |
|---|---|
| **Input** | Airbnb listing links from any country site, or listing numbers |
| **Output** | One row per listing: totals, a month-by-month summary and every day |
| **Ceiling** | 2,000 listings per run, up to 12 months each |
| **Speed** | About 115 to 120 calendars a minute, so 2,000 listings take about 17 minutes |
| **Account needed** | None from you |
| **Price** | $0.75 per 1,000 listing calendars, flat on every plan. The free plan's $5 a month covers about 6,500 |

### 🔍 What Airbnb Calendar Scraper does

It reads the same calendar a guest sees on a listing page, for each listing you give it, and turns it into a row you can sort and count. Each night says whether it is free, whether a stay can begin or end there, and the minimum and maximum stay the host set for it. On top of that every row carries the totals: free nights, taken nights, the share taken, the first and last free night, and the same counts month by month.

It reads the calendar as it is at the moment of the run. Run it on a schedule and you get a history.

The calendar never starts before today, by the UTC date, even if you pick the current month: a night that has already passed says nothing about bookings. A `startMonth` already in the past is moved to the current month and the run tells you so in a free note row.

### 📋 What data you get from each Airbnb calendar

| What you get | Field |
|---|---|
| Airbnb's listing number, the link, and the line you gave for it | `listingId`, `listingUrl`, `input` |
| First and last night read, and how many nights that is | `startDate`, `endDate`, `totalDays` |
| Free nights and taken nights | `availableDays`, `unavailableDays` |
| Taken nights divided by nights read, 0 to 1 | `unavailableShare` |
| Nights a stay could start on, by Airbnb's own rules | `bookableDays` |
| First and last free night, `null` when nothing is free | `firstAvailableDate`, `lastAvailableDate` |
| Per month: nights, free, taken, bookable and the share taken | `months` |
| Per night: free or taken, bookable, check-in and check-out allowed, minimum and maximum stay | `days` |
| When the calendar was read | `scrapedAt` |

### ▶️ How to scrape Airbnb calendars

1. Open [Airbnb Calendar Scraper](https://apify.com/dami_studio/airbnb-calendar-scraper) and click **Try for free**.
2. Paste your links into **Listings**, one per line.
3. Set **Number of months**, and **First month** if you want a later start.
4. Click **Start**.
5. When it finishes, open the **Output** tab and download JSON, CSV or Excel.

Start it with no listings at all and you get one free sample row, a note rather than data.

### 💰 How much does it cost to scrape Airbnb calendars?

**$0.75 per 1,000 listing calendars.** Flat on every Apify plan, no volume tiers. On the free plan, the $5 Apify gives you each month covers about 6,500 calendars. A calendar costs the same whether you read 1 month or 12, with or without the day list.

You are not charged for the sample row, for any diagnostic row, for listings that were not found, for duplicates in your list, or for listings a run never reached. To cap a run's spend, set a maximum cost when you start it: the run stops at that limit and lists the listings it did not read.

### 📥 What you give it

```json
{
  "listings": [
    "https://www.airbnb.com/rooms/26939959",
    "https://www.airbnb.co.uk/rooms/4054044?adults=2",
    "913978009878067640"
  ],
  "months": 3
}
```

| Field | Default | What it is |
|---|---|---|
| `listings` | none | Listing links (airbnb.com, airbnb.co.uk, airbnb.fr and the rest) or bare listing numbers. One per line, up to 2,000. Duplicates are read once. |
| `months` | 12 when empty | How many months to read for each listing, 1 to 12. |
| `startMonth` | today when empty | The first month, written like `2027-06`. At most 11 months ahead. |
| `includeDays` | on | Keep the full day list on each row. Turn it off for totals and months only. |

### 📤 What you get back

A real row from run `rISM4I1QygW0LpATI`, three months read, with the day list cut to three days:

```json
{
  "recordType": "calendar",
  "listingId": "26939959",
  "listingUrl": "https://www.airbnb.com/rooms/26939959",
  "input": "https://www.airbnb.com/rooms/26939959",
  "startDate": "2026-10-03",
  "endDate": "2026-12-31",
  "totalDays": 90,
  "availableDays": 39,
  "unavailableDays": 51,
  "unavailableShare": 0.567,
  "bookableDays": 39,
  "firstAvailableDate": "2026-10-04",
  "lastAvailableDate": "2026-12-28",
  "months": [
    { "month": "2026-10", "totalDays": 29, "availableDays": 10, "unavailableDays": 19, "bookableDays": 10, "unavailableShare": 0.655 },
    { "month": "2026-11", "totalDays": 30, "availableDays": 13, "unavailableDays": 17, "bookableDays": 13, "unavailableShare": 0.567 },
    { "month": "2026-12", "totalDays": 31, "availableDays": 16, "unavailableDays": 15, "bookableDays": 16, "unavailableShare": 0.484 }
  ],
  "scrapedAt": "2026-10-03T05:13:32.955Z",
  "days": [
    { "date": "2026-10-03", "available": false, "bookable": false, "availableForCheckIn": false, "availableForCheckOut": false, "minNights": 1, "maxNights": 30 },
    { "date": "2026-10-04", "available": true, "bookable": true, "availableForCheckIn": true, "availableForCheckOut": false, "minNights": 1, "maxNights": 30 },
    { "date": "2026-10-05", "available": true, "bookable": true, "availableForCheckIn": true, "availableForCheckOut": true, "minNights": 1, "maxNights": 30 }
  ]
}
```

| Field | How to read it |
|---|---|
| `unavailableShare` | An upper bound on occupancy, since a taken night may be one the host blocked. |
| `bookableDays` | Nights a stay could start on, by Airbnb's own rules, which need not match the free nights one for one. |
| `days[].availableForCheckIn`, `days[].availableForCheckOut` | Whether a stay may begin or end that day. A host can allow one and not the other, as on 4 October above. |
| `days[].minNights`, `days[].maxNights` | The minimum and maximum stay the host set for that night. |

A flag Airbnb leaves out comes back as `null` rather than a guess.

### 🧾 Reading the output

Every row has a `recordType`. To keep only the calendars you paid for, filter on `recordType` equal to `calendar`.

- `calendar`: a listing's calendar. These are the only billed rows.
- `sample`: one free row with `_sample: true`, written when the input has no listings at all. It is a note, not data.
- `diagnostic`: a free row with `_diagnostic: true`, `charged: false`, an `errorCode` and a `message`.

| `errorCode` | What happened |
|---|---|
| `BAD_LISTING` | That line is not an Airbnb listing link or number. |
| `LISTING_NOT_FOUND` | Airbnb shows no calendar for that listing: removed, unlisted, or a wrong number. |
| `LISTING_UNREADABLE` | Airbnb answered with an error for that listing. |
| `UNEXPECTED_ANSWER` | Airbnb answered, but not with that listing's calendar. |
| `FETCH_FAILED` | No answer for that listing after three tries. |
| `WINDOW_ADJUSTED` | Your `startMonth` was in the past and moved to this month. |
| `BAD_INPUT` | `months` or `startMonth` out of range; nothing was read. |
| `TOO_MANY_LISTINGS` | Over 2,000 listings; the rest are listed in the row. |
| `STOPPED_EARLY` | The run ended early: Airbnb stopped answering, or 100 listings in a row had no calendar. The unread listings are in `unreadListingIds`. |
| `DEADLINE_REACHED` | The run hit its time limit; the unread listings are in `unreadListingIds`. |
| `COST_LIMIT_REACHED` | The run hit the maximum cost you set; the unread listings are in `unreadListingIds`. |
| `CALENDAR_UNAVAILABLE` | Airbnb returned no calendars at all on this run. |
| `UNEXPECTED_ERROR` | The run hit an unexpected error; the unread listings are in `unreadListingIds`. |

`unreadListingIds` is there so you can paste those numbers straight into the next run.

Want one row per night in a spreadsheet? Download the dataset through the Apify API with `unwind=days` added, for example `GET /v2/datasets/<dataset id>/items?format=csv&unwind=days`. Each night then becomes its own line with the listing's fields beside it. The **Month by month** view in the Output tab does the same for months.

### 💡 What people use it for

- A host deciding what to charge for a holiday weekend, who wants to see how fast the neighbours' calendars are filling.
- Someone weighing up a flat as a short-let investment, checking how full the listings in the same building look across a year.
- A property manager who keeps a daily snapshot of every listing they run in one sheet, instead of clicking through calendars.
- Anyone studying a market's seasonality: which months open up late, and where minimum stays jump around holidays.

From a place to how full it is, in three steps:

1. Run [Airbnb Listings Scraper](https://apify.com/dami_studio/airbnb-listings-scraper) on the place and copy the `listingId` column.
2. Paste those numbers into **Listings** here and run it with **Number of months** at 3.
3. Sort on `unavailableShare`, and read `months` to see which weeks are filling first.

### 🚧 What it does not do

- **No nightly prices.** Airbnb's calendar does not carry them.
- **It cannot tell a booking from a night the host blocked.** Both read as taken.
- **Nights past the host's booking window read as taken too.** A calendar that is taken from some date to the end often just means the host has not opened those dates.
- **At most 12 months per listing**, starting no more than 11 months ahead.
- **It does not search Airbnb by place.** You need the listing links or numbers.
- **No listing details** such as title, photos or rating.
- **Short links and `/h/` links can't be read.** Open the listing and copy the link with `/rooms/` in it.

### 🧭 Which travel scraper do you need?

| If you want | Use |
|---|---|
| A listing's day-by-day calendar for up to 12 months | This one |
| Airbnb listings with prices, ratings and coordinates for a place | [Airbnb Listings Scraper](https://apify.com/dami_studio/airbnb-listings-scraper) |
| Hotels on Booking.com with live prices and scores for your dates | [Booking.com Hotels Scraper](https://apify.com/dami_studio/booking-hotels-scraper) |
| Guest reviews with stay dates for a Booking.com property | [Booking.com Reviews Scraper](https://apify.com/dami_studio/booking-reviews-scraper) |
| Live hotel prices for your dates, as Google Hotels shows them | [Google Hotels Scraper](https://apify.com/dami_studio/google-hotels-scraper) |
| Reviews of a hotel, restaurant or attraction on Tripadvisor | [Tripadvisor Reviews Scraper](https://apify.com/dami_studio/tripadvisor-reviews-scraper) |
| Flight fares for your routes and dates | [Skyscanner Flights Scraper](https://apify.com/dami_studio/skyscanner-flights-scraper) |

### ❓ Questions people ask

#### Does a taken night mean it was booked?

Not always. Airbnb shows booked nights and nights the host blocked the same way. Treat `unavailableShare` as an upper bound on occupancy.

#### Where do I get listing numbers for a place?

Run the Airbnb Listings Scraper for it, up to 20 listings per place in a run, and paste its `listingId` values here. Or copy the links from Airbnb's own search page.

#### How do I see changes over time?

Schedule the run daily or weekly. Each row has `scrapedAt`, so you can compare the same listing between runs.

#### Do I need an Airbnb account?

No. It reads what any visitor sees on a listing page, without logging in.

#### Can I call it from code or connect it to an AI assistant?

Yes. The [API tab](https://apify.com/dami_studio/airbnb-calendar-scraper/api/python) has ready-made code for Python, JavaScript and the command line. For Claude, ChatGPT or another MCP client, connect `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/airbnb-calendar-scraper`. Either way the run happens on your Apify account at the same price.

#### Is it legal to scrape Airbnb calendars?

This reads public calendar information that anyone can see without logging in. Apify explains the general picture in its write-up on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/), and we are not lawyers.

### 🆘 If something breaks

Open an issue on the actor's **Issues** tab with the run link and two or three of the listing numbers involved. Diagnostic rows carry an `errorCode`, so including it helps.

# Actor input Schema

## `listings` (type: `array`):

Airbnb listing links (any country site, for example https://www.airbnb.com/rooms/26939959 or https://www.airbnb.fr/rooms/26939959) or just the listing number. One per line, up to 2,000 per run. Duplicates are read once. Short links and /h/ links can't be read: open the listing and copy the link with /rooms/ in it.

## `months` (type: `integer`):

How many calendar months to read for each listing, from 1 to 12. Leave it empty for 12. The price per listing is the same whatever you pick.

## `startMonth` (type: `string`):

The month the calendar starts, written like 2027-06. Leave it empty to start today. It can be at most 11 months ahead. A month already in the past is moved to the current month. Days before today are never included.

## `includeDays` (type: `boolean`):

On: each row carries the full day-by-day list. Off: each row carries only the totals and the month-by-month summary, which keeps the rows small for market-wide work. The price is the same either way.

## Actor input object example

```json
{
  "listings": [
    "https://www.airbnb.com/rooms/26939959",
    "https://www.airbnb.com/rooms/4054044"
  ],
  "months": 12,
  "includeDays": true
}
```

# Actor output Schema

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

One row per listing: the first and last date read, how many nights are free and how many are taken, the share taken, the first and last free night, a month-by-month summary and, unless switched off, every day with its free or taken flag, whether a stay can start or end there, and the minimum and maximum stay. Empty input writes one free sample row instead, and a listing that can't be read writes a free row saying why.

# 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 = {
    "listings": [
        "https://www.airbnb.com/rooms/26939959",
        "https://www.airbnb.com/rooms/4054044"
    ],
    "months": 12
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/airbnb-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 = {
    "listings": [
        "https://www.airbnb.com/rooms/26939959",
        "https://www.airbnb.com/rooms/4054044",
    ],
    "months": 12,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/airbnb-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 '{
  "listings": [
    "https://www.airbnb.com/rooms/26939959",
    "https://www.airbnb.com/rooms/4054044"
  ],
  "months": 12
}' |
apify call dami_studio/airbnb-calendar-scraper --silent --output-dataset

```

## MCP server setup

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