# Airbnb Occupancy & Pricing Scraper (`khadinakbar/airbnb-occupancy-pricing-scraper`) Actor

Scrape Airbnb occupancy and pricing from public listing calendars. Use for STR comps, occupancy upper bound, ADR, and RevPAR. Do not use for host iCal exports — see airbnb-calendar-availability-scraper. Returns one occupancy report per listing. Charged $0.006 per listing report.

- **URL**: https://apify.com/khadinakbar/airbnb-occupancy-pricing-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Travel, Real estate, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 occupancy reports

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?

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

## Airbnb occupancy and pricing reports

Turn a public Airbnb listing URL, listing ID, or city search into **one occupancy report per listing**. The Actor reads Airbnb's public forward calendar and returns occupancy upper bound, available vs unavailable nights, typical minimum stay, next available date, and ADR / RevPAR when Airbnb exposes a price. No Airbnb login and no host iCal token.

This Actor is for STR investors, property managers, and agents that need a listing-level occupancy snapshot before they model revenue.

### Best fit for this Actor

- Analyze a known listing URL or ID for the next 30/60/90/365 days of public availability.
- Sample a city search (for example Austin, TX) when listing IDs still need discovery.
- Keep `includeDailyCalendar` off unless you need the raw day array for a downstream model.

For listing cards without a calendar (name, snapshot nightly, ratings, host), use [Airbnb Scraper](https://apify.com/khadinakbar/airbnb-scraper). For authorized host iCal exports you manage, use [Airbnb Calendar Availability Scraper](https://apify.com/khadinakbar/airbnb-calendar-availability-scraper). For review text, use [Airbnb Reviews Scraper](https://apify.com/khadinakbar/airbnb-reviews-scraper).

### Practical scenario

An analyst pastes `https://www.airbnb.com/rooms/12937`, sets `calendarMonths` to `3`, and leaves `maxListings` at `1`. The run returns one row: occupancy upper bound over the forward calendar, available/unavailable night counts, typical min nights, next available date, and listing profile fields (title, rating, capacity). If Airbnb omits anonymous calendar prices, `adr` is null and occupancy is still billed. A later city-search run on Austin can fill `adr` from the search stay quote.

### Quick start input

```json
{
  "listingIds": ["12937"],
  "startUrls": [{ "url": "https://www.airbnb.com/rooms/12937" }],
  "calendarMonths": 3,
  "maxListings": 1,
  "includeProfile": true
}
```

Listing IDs and room URLs are merged and deduped before any HTTP call. When both listing IDs and `location` are set, listing IDs win.

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `listingIds` | array | Numeric Airbnb IDs such as `12937`. |
| `startUrls` | array | Public `/rooms/{id}` URLs. |
| `location` | string | City search used only when no listings are provided. Example: `Austin, TX`. |
| `calendarMonths` | integer | Forward months 1–12. Prefill 3, default 12. |
| `maxListings` | integer | Cap after dedupe. Default 50, prefill 1. |
| `includeProfile` | boolean | Attach title, rating, capacity, bedrooms. Default true. |
| `includeDailyCalendar` | boolean | Attach day-level `days[]`. Default false. |
| `currency` / `locale` | string | Price label currency and GraphQL locale. Occupancy does not depend on them. |
| `proxyConfiguration` | object | Apify Residential recommended. |

### What data you receive

One dataset item is one listing occupancy report.

```json
{
  "listingId": "12937",
  "listingUrl": "https://www.airbnb.com/rooms/12937",
  "title": "1 Stop to Midtown! Private Bedroom, Landmark House",
  "roomType": "Private room",
  "city": "New York",
  "occupancyPctUpperBound": 72.5,
  "occupancy30dPct": 80.0,
  "availableNights": 24,
  "unavailableNights": 66,
  "typicalMinNights": 3,
  "nextAvailableDate": "YYYY-MM-DD",
  "adr": null,
  "adrSource": null,
  "revpar": null,
  "occupancyMethodology": "Unavailable nights on the public forward calendar include both guest bookings and host blocks. occupancyPctUpperBound is therefore an occupancy upper bound, not confirmed booked nights.",
  "source": "listing",
  "scrapedAt": "ISO-8601 timestamp"
}
```

| Field | Meaning |
|---|---|
| `occupancyPctUpperBound` | Unavailable nights / counted nights. Host blocks count as unavailable. |
| `occupancy30dPct` / `90d` / `365d` | Same upper bound on rolling windows. |
| `adr` | Mean calendar nightly price, or search stay total / nights. Often null on anonymous listing-ID runs. |
| `revpar` | ADR × occupancy upper bound. |
| `estimatedRevenue30d` / `365d` | ADR × unavailable nights in that window. Estimate only. |
| `source` | `listing` or `search`. |

Download JSON, CSV, or Excel from the run dataset.

### Use through the API

```bash
curl "https://api.apify.com/v2/acts/khadinakbar~airbnb-occupancy-pricing-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"listingIds":["12937"],"calendarMonths":3,"maxListings":1}'
```

Read rows from the run `defaultDatasetId` after status is SUCCEEDED. Check key-value `OUTPUT.outcome` for `COMPLETE`, `PARTIAL`, `VALID_EMPTY`, or `INVALID_INPUT`.

### Use with AI agents through Apify MCP

> Analyze occupancy for Airbnb listing 12937 over the next 3 months. Return occupancy upper bound, available nights, next available date, and ADR only if Airbnb exposed a price. Treat unavailable nights as an occupancy upper bound that includes host blocks.

Inspect `OUTPUT.outcome`, keep `listingUrl` as provenance, and keep `maxListings` small. Client setup: <https://mcp.apify.com>.

### Connect the workflow

- Listing discovery → occupancy: start with [Airbnb Scraper](https://apify.com/khadinakbar/airbnb-scraper) `listing_id`, then pass it here.
- Host-owned calendars: [Airbnb Calendar Availability Scraper](https://apify.com/khadinakbar/airbnb-calendar-availability-scraper) accepts authorized iCal URLs this Actor rejects.
- After occupancy, continue to guest-review text with [Airbnb Reviews Scraper](https://apify.com/khadinakbar/airbnb-reviews-scraper) when you need comment-level sentiment.

### Pricing

This Actor uses Pay per event plus Apify platform usage. Open the live Pricing tab for current event details, and use Apify's run cost controls to keep the workflow aligned with your budget.

- `apify-actor-start`: $0.00005 per run
- `occupancy-report`: $0.006 per saved listing report

Invalid input, empty city searches, and calendars Airbnb does not return are not charged the listing event. Typical one-listing quality run: one start event + one occupancy report.

### Best results

- Use a real `/rooms/{id}` URL or numeric ID. iCal export URLs are rejected on purpose.
- Start with `calendarMonths: 3` and `maxListings: 1` before a city sample.
- Enable Apify Residential if Airbnb returns HTTP 403/429.
- Treat occupancy as an **upper bound**. Hosts block dates that are not guest bookings.

### Design note

In my testing, Airbnb's anonymous calendar GraphQL returned availability, min nights, and check-in flags, while `localPriceFormatted` was usually empty. I found search-mode stay quotes (`$1,832 for 5 nights`) were the reliable ADR path. That is why listing-ID runs can have occupancy without ADR, and why the field is named `occupancyPctUpperBound` instead of booked occupancy.

### Responsible use

Use public listing data for research, comps, and your own market analysis. Respect Airbnb's terms of service and applicable law. This Actor is for public calendar reports only. Airbnb is a trademark of Airbnb, Inc. This independent Actor is not affiliated with, associated with, or endorsed by Airbnb, Inc.

# Actor input Schema

## `listingIds` (type: `array`):

Airbnb listing IDs to analyze, one per row. Example: 12937 (the number in /rooms/12937). Defaults to none when you only pass startUrls or a location. NOT an iCal export URL — use airbnb-calendar-availability-scraper for authorized calendars.

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

Public Airbnb listing URLs to analyze. Example: https://www.airbnb.com/rooms/12937. Combined with listingIds and deduped. Max 100 unique listings per run. NOT search URLs and NOT iCal links.

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

City or area used only when no listing IDs/URLs are provided. Example: Austin, TX or Bali--Indonesia. Finds public stays then analyzes each calendar. Explicit listing IDs skip this field. NOT a lat/lng pair.

## `calendarMonths` (type: `integer`):

How many forward months of public availability to request, from 1 to 12. Airbnb caps the horizon around 12 months. Prefill 3 keeps quality tests fast; default 12 for full-year occupancy. Not a historical archive.

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

Hard cap on unique listings processed after dedupe. Defaults to 50, maximum 100. Prefill 1 keeps the quality test cheap. Each saved occupancy report costs $0.006.

## `includeProfile` (type: `boolean`):

When true, attach title, room type, location, rating, capacity, and bedroom counts from the public listing page. Defaults to true. Set false for calendar metrics only. Does not add a second billed event.

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

When true, attach the day-level available/minNights/price array on each listing report. Defaults to false to keep agent output small. Turn on only when you need the raw forward calendar.

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

ISO 4217 currency for price labels Airbnb returns (e.g. USD, EUR, GBP). Defaults to USD. Anonymous calendars often omit nightly prices; occupancy does not depend on this field.

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

Locale passed to Airbnb GraphQL (e.g. en, en-US). Defaults to en. Does not change occupancy math.

## `concurrency` (type: `integer`):

How many listings to analyze in parallel, from 1 to 4. Defaults to 2. Keep low to stay under Airbnb rate limits. Not a monthly request quota.

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

Apify proxy used for Airbnb HTTP. Residential is recommended because datacenter ASNs are often fingerprinted. This is not an Airbnb login cookie.

## Actor input object example

```json
{
  "listingIds": [
    "12937"
  ],
  "startUrls": [
    {
      "url": "https://www.airbnb.com/rooms/12937"
    }
  ],
  "calendarMonths": 3,
  "maxListings": 1,
  "includeProfile": true,
  "includeDailyCalendar": false,
  "currency": "USD",
  "locale": "en",
  "concurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One occupancy and pricing report per listing.

## `output` (type: `string`):

Final outcome, itemsPushed, and chargedEventCounts.

## `runSummary` (type: `string`):

Per-listing outcomes, calendar horizon, and billing counters.

# 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 = {
    "listingIds": [
        "12937"
    ],
    "startUrls": [
        {
            "url": "https://www.airbnb.com/rooms/12937"
        }
    ],
    "calendarMonths": 3,
    "maxListings": 1,
    "includeProfile": true,
    "includeDailyCalendar": false,
    "currency": "USD",
    "locale": "en",
    "concurrency": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/airbnb-occupancy-pricing-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 = {
    "listingIds": ["12937"],
    "startUrls": [{ "url": "https://www.airbnb.com/rooms/12937" }],
    "calendarMonths": 3,
    "maxListings": 1,
    "includeProfile": True,
    "includeDailyCalendar": False,
    "currency": "USD",
    "locale": "en",
    "concurrency": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/airbnb-occupancy-pricing-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 '{
  "listingIds": [
    "12937"
  ],
  "startUrls": [
    {
      "url": "https://www.airbnb.com/rooms/12937"
    }
  ],
  "calendarMonths": 3,
  "maxListings": 1,
  "includeProfile": true,
  "includeDailyCalendar": false,
  "currency": "USD",
  "locale": "en",
  "concurrency": 2
}' |
apify call khadinakbar/airbnb-occupancy-pricing-scraper --silent --output-dataset

```

## MCP server setup

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