# Airbnb Market Occupancy Scraper (`cirkit/airbnb-market-occupancy-scraper`) Actor

Occupancy for every listing in an Airbnb market. Walks each 12-month calendar and corrects for the host's booking horizon and owner blocks, giving real occupancy per listing, per month, and market-wide — not the naive blocked-day count, which can overstate occupancy by 15x.

- **URL**: https://apify.com/cirkit/airbnb-market-occupancy-scraper.md
- **Developed by:** [Crikit](https://apify.com/cirkit) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 listing analyzeds

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

## Airbnb Market Occupancy Scraper

**Scrape Airbnb occupancy** for every listing in a market. This **Airbnb occupancy scraper** walks each listing's 12-month calendar and then corrects for the host's booking horizon and long owner blocks, producing real occupancy per listing, per month, and market-wide — not the naive blocked-day count that overstates occupancy on almost every listing.

No Airbnb API key, no login, and no headless browser. Results land in a structured dataset you can export to JSON, CSV, Excel, or XML, or pull straight from the Apify API.

### What is Airbnb Market Occupancy Scraper?

Airbnb Market Occupancy Scraper is an [Apify Actor](https://apify.com/actors) that extracts market-wide occupancy rates from [Airbnb](https://www.airbnb.com) and returns them as clean, structured records. The correction matters: a host who has only opened three months of calendar looks 75% booked to a naive counter. This Actor detects the booking horizon and the long blocks that signal owner use, and reports `occupancyStatusReason` so you can see exactly how each number was derived.

### What data can this Airbnb occupancy scraper extract?

Every run writes one row per listing. The full schema carries **57 fields**; the most-used ones are below.

| Field | Type | Description |
| --- | --- | --- |
| `recordType` | string | recordType |
| `listingId` | string | listingId |
| `url` | string | url |
| `name` | string | name |
| `subtitle` | string | subtitle |
| `latitude` | number | latitude |
| `longitude` | number | longitude |
| `rating` | number | rating |
| `badges` | array | badges |
| `isSuperhost` | boolean | isSuperhost |
| `searchPriceLabel` | string | searchPriceLabel |
| `occupancyStatus` | string | occupancyStatus |
| `occupancyStatusReason` | string | occupancyStatusReason |
| `occupancyPct` | number | occupancyPct |
| `occupancyWindowDays` | integer | occupancyWindowDays |
| `occupancyByWindow` | object | occupancyByWindow |
| `monthlyOccupancy` | array | monthlyOccupancy |
| `bookingHorizonDays` | integer | bookingHorizonDays |
| `bookingHorizonDate` | string | bookingHorizonDate |
| `calendarFirstDate` | string | calendarFirstDate |
| `calendarLastDate` | string | calendarLastDate |
| `calendarDaysScanned` | integer | calendarDaysScanned |

See the **Output** tab in Apify Console for all 57 fields.

### How to scrape Airbnb market occupancy

1. Open [Airbnb Market Occupancy Scraper](https://apify.com/cirkit/airbnb-market-occupancy-scraper) and click **Try for free**.
2. Give a `location` name, or a `boundingBox` / `radiusKm` if you want to draw the market yourself.
3. Set `monthsAhead` and `occupancyWindows` to define the periods you want measured.
4. Filter supply with `minBedrooms`, `priceMin`, and `priceMax` so you measure a comparable set.
5. Leave `includeMarketSummary` on for a market-level roll-up row alongside the per-listing rows.
6. Click **Start** and watch rows appear live in the **Output** tab.
7. Export the dataset as JSON, CSV, Excel, or XML — or fetch it from the [Apify API](https://docs.apify.com/api/v2) once the run finishes.

### Airbnb Market Occupancy Scraper input options

| Input | Type | What it does | Default |
| --- | --- | --- | --- |
| `location` | string | The market to analyse, for example "Joshua Tree, CA" or "Gatlinburg, Tennessee". Resolved to a centre point, then... | `"Joshua Tree, CA"` |
| `boundingBox` | object | Exact search area as neLat, neLng, swLat, swLng. Use this for irregular markets or to split a large region across... |  |
| `radiusKm` | integer | Half-width of the search box around the resolved location. 12 km suits a town; raise it for a metro area or a... | `12` |
| `maxListings` | integer | Hard cap on listings analysed, and therefore on run cost. Set 0 for no cap — a dense metro can hold many thousands... | `300` |
| `occupancyWindows` | array | Forward-looking windows to score, in days. 90 days is the most reliable read: far enough out to capture demand, near... | `[30, 60, 90]` |
| `monthsAhead` | integer | How far ahead to pull each calendar, 1 to 12. Airbnb silently caps this at 12. | `12` |
| `longBlockDays` | integer | An unbroken run of unavailable days at least this long is treated as an owner block (renovation, personal use)... | `21` |
| `includeDailyCalendar` | boolean | Adds a `days` array to every listing row. Useful for your own modelling; it makes the dataset far larger. | `false` |
| `includeMarketSummary` | boolean | Appends one roll-up row with market-wide occupancy percentiles and a month-by-month curve. | `true` |
| `minBedrooms` | integer | Restrict the market to listings with at least this many bedrooms. |  |
| `priceMin` | integer | Lower bound on nightly price, in `currency`. |  |
| `priceMax` | integer | Upper bound on nightly price, in `currency`. |  |
| `maxSubdivisionDepth` | integer | Airbnb truncates any single search at 270 results, so cells that come back full are split into quadrants and... | `3` |
| `maxConcurrency` | integer | Parallel calendar requests. Airbnb rate-limits per IP, so raise this only alongside a rotating proxy. | `8` |
| `maxRunSecs` | integer | The Actor stops cleanly at this point and still writes everything it has gathered, rather than being killed by the... | `3300` |
| `currency` | string | Three-letter ISO currency code for price fields. | `"USD"` |

Proxy is configured through the standard `proxyConfiguration` object; the defaults shipped with this Actor are already tuned for the target site.

### Example output

```json
{
  "location": "Joshua Tree, CA",
  "radiusKm": 12,
  "maxListings": 300,
  "occupancyWindows": [30, 60, 90],
  "monthsAhead": 12
}
```

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

This Actor runs on **pay per result** pricing: **$0.004 per listing** ($4 per 1,000).

| Results | Approximate cost |
| --- | --- |
| 1,000 | $4.00 |
| 10,000 | $40.00 |
| 100,000 | $400.00 |

You pay for rows you actually receive, so a search that returns nothing costs nothing beyond the start fee. Every Apify account includes free monthly usage credit, so you can trial Airbnb Market Occupancy Scraper at no cost.

### What can you build with Airbnb data?

#### Underwrite a short-term-rental purchase

Real occupancy by bedroom tier, plus median available price, is the pair of numbers a revenue projection actually rests on. Both come out of one run.

#### Benchmark your listing against its true comp set

Compare your occupancy to the market median for the same bedroom count and price band rather than to a city-wide average that mixes studios with five-bedroom houses.

#### Measure seasonality before committing to a market

`monthlyOccupancy` per listing rolls up into a seasonality curve, so you can see whether a market is a year-round performer or carried by three peak months.

#### Spot markets where supply is outrunning demand

Track `listingsDiscovered` and market occupancy together over successive runs to catch saturation early.

#### Validate a third-party data provider

Occupancy estimates from commercial STR data vendors vary widely. This gives you an independent, auditable read with the derivation exposed.

### Airbnb scraping tips and limits

- Naive occupancy counts every blocked day as booked and typically overstates the real figure badly. `naiveUnavailablePct` is returned alongside the corrected number so you can see the size of that gap yourself.
- `longBlockDays` controls how many consecutive blocked days count as owner use rather than a booking. Tune it for markets with long-stay patterns.
- Big markets are expensive — every listing means a full calendar walk. Use `maxListings` and the price and bedroom filters to measure a representative sample instead of everything.
- `maxRunSecs` is a hard stop. For a very large metro, split by neighborhood rather than raising it indefinitely.

### Integrations and automation

Run Airbnb Market Occupancy Scraper on a [schedule](https://docs.apify.com/platform/schedules) to keep a Airbnb dataset fresh, or trigger it from your own stack through the [Apify API](https://docs.apify.com/api/v2) and the official [JavaScript](https://docs.apify.com/sdk/js) and [Python](https://docs.apify.com/sdk/python) clients. Native [integrations](https://docs.apify.com/platform/integrations) push results into Google Sheets, Slack, Airtable, Zapier, Make, GitHub, or any webhook endpoint. The Actor also works as an [MCP](https://docs.apify.com/platform/integrations/mcp) tool, so an AI agent can call it directly.

### Related scrapers

| Actor | What it does |
| --- | --- |
| [Airbnb Search Scraper](https://apify.com/cirkit/airbnb-search-scraper) | Cheap supply-side census of a market. |
| [Airbnb Full-Year Price Scraper](https://apify.com/cirkit/airbnb-full-year-price-scraper) | Real nightly prices to pair with occupancy for revenue estimates. |
| [Airbnb Market Reviews Scraper](https://apify.com/cirkit/airbnb-market-reviews-scraper) | Every review in the market, a demand-side signal. |
| [Airbnb Availability Calendar Scraper](https://apify.com/cirkit/airbnb-availability-scraper) | Raw per-listing calendars without the occupancy correction. |

### Frequently asked questions

#### How is occupancy calculated?

The Actor reads each listing's calendar, establishes how far ahead the host has actually opened bookings, and excludes long consecutive blocks that indicate owner use. Occupancy is then booked days over genuinely bookable days inside that horizon. `occupancyStatusReason` records which rule applied to each listing.

#### Why is this more accurate than counting blocked days?

Airbnb does not publicly distinguish a guest booking from an owner block or an unopened calendar. Counting every unavailable day as booked inflates occupancy for any host who has not opened a full year — which is most of them.

#### Do I need a Airbnb API key?

No. Airbnb Market Occupancy Scraper reads publicly available Airbnb pages and endpoints directly, so there is no key to obtain, no OAuth app to register, and no account to connect.

#### Can I export Airbnb data to CSV, Excel, or Google Sheets?

Yes. Every run stores results in an Apify dataset that exports to JSON, JSONL, CSV, Excel, XML, or HTML with one click, and the Google Sheets integration writes rows straight into a spreadsheet.

#### Can I run Airbnb Market Occupancy Scraper on a schedule?

Yes. Apify schedules run the Actor hourly, daily, weekly, or on any cron expression, and webhooks can notify your systems the moment a run finishes.

#### Is it legal to scrape Airbnb?

Scraping publicly available data is broadly lawful in the US and EU, but the answer depends on what you collect and how you use it. This Actor is built to gather public calendar and listing information Airbnb publishes on public pages. Personal data carries extra obligations under GDPR and CCPA. Read Apify's guide on [whether web scraping is legal](https://blog.apify.com/is-web-scraping-legal/) and take your own legal advice for your use case.

### Support and feedback

Found a bug, a missing field, or a Airbnb page shape this Actor does not handle yet? Open an issue from the **Issues** tab on the Actor page. Feature requests and custom-scraper enquiries are welcome there too.

# Actor input Schema

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

The market to analyse, for example "Joshua Tree, CA" or "Gatlinburg, Tennessee". Resolved to a centre point, then searched within `radiusKm`. Ignored when `boundingBox` is set.

## `boundingBox` (type: `object`):

Exact search area as neLat, neLng, swLat, swLng. Use this for irregular markets or to split a large region across several runs.

## `radiusKm` (type: `integer`):

Half-width of the search box around the resolved location. 12 km suits a town; raise it for a metro area or a spread-out region.

## `maxListings` (type: `integer`):

Hard cap on listings analysed, and therefore on run cost. Set 0 for no cap — a dense metro can hold many thousands of listings.

## `occupancyWindows` (type: `array`):

Forward-looking windows to score, in days. 90 days is the most reliable read: far enough out to capture demand, near enough that most hosts have their calendar open.

## `monthsAhead` (type: `integer`):

How far ahead to pull each calendar, 1 to 12. Airbnb silently caps this at 12.

## `longBlockDays` (type: `integer`):

An unbroken run of unavailable days at least this long is treated as an owner block (renovation, personal use) rather than a booking, and is removed from the occupancy denominator. Lower it in markets dominated by long stays.

## `includeDailyCalendar` (type: `boolean`):

Adds a `days` array to every listing row. Useful for your own modelling; it makes the dataset far larger.

## `includeMarketSummary` (type: `boolean`):

Appends one roll-up row with market-wide occupancy percentiles and a month-by-month curve.

## `minBedrooms` (type: `integer`):

Restrict the market to listings with at least this many bedrooms.

## `priceMin` (type: `integer`):

Lower bound on nightly price, in `currency`.

## `priceMax` (type: `integer`):

Upper bound on nightly price, in `currency`.

## `maxSubdivisionDepth` (type: `integer`):

Airbnb truncates any single search at 270 results, so cells that come back full are split into quadrants and re-searched. Each extra level can quadruple coverage in dense markets, at proportionally more search requests.

## `maxConcurrency` (type: `integer`):

Parallel calendar requests. Airbnb rate-limits per IP, so raise this only alongside a rotating proxy.

## `maxRunSecs` (type: `integer`):

The Actor stops cleanly at this point and still writes everything it has gathered, rather than being killed by the platform timeout.

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

Three-letter ISO currency code for price fields.

## `locale` (type: `string`):

Locale tag passed to Airbnb, for example en-US or en-GB.

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

Apify Proxy is enabled by default and pinned to US exit IPs. Airbnb's edge serves a different response shape and overrides currency based on exit country, so changing the country degrades the output. Rotation is what keeps throughput up, since Airbnb rate-limits per IP.

## Actor input object example

```json
{
  "location": "Joshua Tree, CA",
  "radiusKm": 12,
  "maxListings": 300,
  "occupancyWindows": [
    30,
    60,
    90
  ],
  "monthsAhead": 12,
  "longBlockDays": 21,
  "includeDailyCalendar": false,
  "includeMarketSummary": true,
  "maxSubdivisionDepth": 3,
  "maxConcurrency": 8,
  "maxRunSecs": 3300,
  "currency": "USD",
  "locale": "en-US",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `items` (type: `string`):

Every analysed listing with its corrected occupancy.

## `itemsCsv` (type: `string`):

No description

## `itemsJson` (type: `string`):

Complete output including per-window and per-month occupancy.

## `consoleRun` (type: `string`):

No description

# 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 = {
    "location": "Joshua Tree, CA",
    "radiusKm": 12,
    "maxListings": 300,
    "occupancyWindows": [
        30,
        60,
        90
    ],
    "monthsAhead": 12,
    "longBlockDays": 21,
    "maxSubdivisionDepth": 3,
    "maxConcurrency": 8,
    "maxRunSecs": 3300,
    "currency": "USD",
    "locale": "en-US",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cirkit/airbnb-market-occupancy-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 = {
    "location": "Joshua Tree, CA",
    "radiusKm": 12,
    "maxListings": 300,
    "occupancyWindows": [
        30,
        60,
        90,
    ],
    "monthsAhead": 12,
    "longBlockDays": 21,
    "maxSubdivisionDepth": 3,
    "maxConcurrency": 8,
    "maxRunSecs": 3300,
    "currency": "USD",
    "locale": "en-US",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("cirkit/airbnb-market-occupancy-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 '{
  "location": "Joshua Tree, CA",
  "radiusKm": 12,
  "maxListings": 300,
  "occupancyWindows": [
    30,
    60,
    90
  ],
  "monthsAhead": 12,
  "longBlockDays": 21,
  "maxSubdivisionDepth": 3,
  "maxConcurrency": 8,
  "maxRunSecs": 3300,
  "currency": "USD",
  "locale": "en-US",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}' |
apify call cirkit/airbnb-market-occupancy-scraper --silent --output-dataset

```

## MCP server setup

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