# Airbnb Scraper: Every Listing, Prices With Fees, Calendar (`alexmorain/airbnb-scraper`) Actor

Scrape every Airbnb listing of a city or map area, past the 280-per-search limit: total stay price with fees, rating, beds, coordinates, photos. Add the 12-month availability calendar and the listing page (amenities, host, rules). By place, search URL or listing ID.

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

## Pricing

from $0.40 / 1,000 listings

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

### What does Airbnb Scraper do?

**Airbnb Scraper** returns **every listing Airbnb shows for a city or a map area**, not just the 280 that one search on [airbnb.com](https://www.airbnb.com) lets you page through. Give it a place name, an Airbnb search URL or listing IDs. Each listing comes with its **total stay price, cleaning and service fees included**, its rating, beds and bathrooms, coordinates and photos. Turn on two options to add the **full listing page** (description, amenities, host, house rules, ratings by category) and the **12-month availability calendar**.

It works as an **Airbnb API alternative**: no login, no browser, plain HTTP on the same API the Airbnb website calls.

#### The 280 listings limit, measured

An Airbnb search stops at 7 pages of 40 listings, whatever the area holds ("Over 1,000 homes within map area"). The order also shifts between pages, so those 280 rows hold duplicates. This Actor splits the map into smaller areas until Airbnb serves each one in full, and returns each listing once.

| Area, 27 October 2026 | Airbnb announces | One Airbnb search | This Actor, full coverage | Run time |
|---|---|---|---|---|
| Annecy, France | Over 1,000 homes | 244 unique (280 rows, 36 duplicates) | **3,730 unique** | 37 s |
| Paris, France | Over 1,000 homes | 280 at most | **42,835 unique** | 4 min 22 s |
| A map area in Vincennes | 304 homes | 280 at most | **306 unique** | 31 s |

Every run writes the proof in its `SUMMARY` record: for each input, the count Airbnb announced in each map cell read in full, and how many distinct listings those cells served. In Paris, cells announcing 41,758 listings served 42,724.

### Why scrape Airbnb?

- **Short-term rental investors and analysts**: size the supply of a city, find the neighborhoods with the most listings, compare prices per night and occupancy from the calendar.
- **Property managers and hosts**: watch competitors around your listings, their prices for your dates, their minimum stays and how full their calendar is.
- **Market research and tourism boards**: count listings by type, by Superhost status, by rating, across a whole city.
- **Monitoring**: schedule a daily run in **New listings only** mode and get only the listings that appeared since the last run, with a Slack or Discord message.

What you get on top of a plain search:

- ✅ **Full coverage** past the 280 cap, duplicates removed before they are charged
- ✅ **Total stay price with fees** for your dates, plus the price per night and the discount when there is one
- ✅ **Availability calendar**, 1 to 12 months, day by day, with availability rates for the next 30, 90 and 365 days
- ✅ **Listing page details**: description, every amenity, host, house rules, ratings by category, all photos
- ✅ **Three ways in**: place name, Airbnb search URL with its map and filters, or listing URL / ID for pipelines
- ✅ Filters that Airbnb applies on its side: price per night, type of place, bedrooms, beds, bathrooms, guests, pets, Instant Book, Guest favorites

As an Apify Actor, it runs on a schedule, from the API, or from Make, Zapier, n8n and other integrations, with proxy rotation and run monitoring included.

### What data can Airbnb Scraper extract?

| Field | Type | Description |
|---|---|---|
| `id`, `url` | string | Airbnb listing ID and page |
| `name` | string | Listing name |
| `latitude`, `longitude` | number | Location (Airbnb may offset it, see `isExactLocation`) |
| `totalPrice`, `pricePerNight`, `nights` | number | Total stay price with fees, price per night, number of nights |
| `originalTotalPrice` | number | Price before the discount, when shown |
| `rating`, `reviewsCount` | number | Average rating out of 5 and number of reviews |
| `isSuperhost`, `isGuestFavorite` | boolean | Badges |
| `bedrooms`, `beds`, `bathrooms` | number | Room facts from the search card |
| `freeCancellation`, `promotions` | boolean, array | Free cancellation and other price messages |
| `photos` | array | Photo URLs |
| `propertyType`, `roomType`, `personCapacity` | string, number | From the listing page (details) |
| `description`, `amenities`, `houseRules`, `highlights` | string, array | From the listing page (details) |
| `host` | object | Host first name, ID, Superhost, verified, years hosting, rating, reviews |
| `categoryRatings` | object | Cleanliness, accuracy, check-in, communication, location, value |
| `calendar` | object | Available days and rate in the next 30, 90 and 365 days, minimum stay, first available check-in |
| `calendarDays` | array | Every day: available, minimum and maximum nights, check-in and check-out possible |

### How to scrape Airbnb listings

1. Open the Actor and type a place in **Places**, for example `Lisbon, Portugal`. Or paste an Airbnb search URL from your browser after moving the map to the area you want.
2. Optional: set **Check-in** and **Check-out** to get the total price of that stay, fees included, and add guests or filters.
3. Leave **Full coverage** on. Set **Max listings per search** to the number you need, or 0 for the whole area.
4. Optional: turn on **Add listing details** and **Add availability calendar**.
5. Click **Start**. Download the results as JSON, CSV or Excel, or read them from the API.

For a list of known listings, paste their URLs or IDs in **Listing URLs or IDs**: each one returns its full listing page and, with dates, the stay price.

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

You pay per listing returned, and only once per listing in a run. Prices on the Free plan, lower on paid plans:

| Event | Free plan | Gold plan and above |
|---|---|---|
| Listing from a search | $1.00 per 1,000 | $0.40 per 1,000 |
| Detailed listing (listing page and/or calendar, or a listing read by URL or ID) | $2.00 per 1,000 | $0.80 per 1,000 |
| Run start | $0.002 | $0.002 |

Examples at Free plan prices: 1,000 listings of a city cost $1. The whole of Paris, 42,835 listings, costs about $43 ($17 on Gold). The same 1,000 listings with their calendar and listing page cost $2. A detailed listing is charged instead of a plain one, not on top of it. If the listing page and the calendar both fail to load, the row is charged as a plain listing.

Set **Max listings per search** or a maximum charge per run to stay within a budget: the run stops cleanly when it is reached and keeps what it already returned.

### Input

See the **Input** tab for every option. The points worth knowing:

- **Places** are turned into a map area with OpenStreetMap. A city name gives the city limits. For a precise area, move the Airbnb map and paste the search URL instead.
- **Search URLs** keep their map area, dates, guests and filters. The filters of this form only fill what the URL does not set.
- **Prices** without dates: Airbnb picks a stay on its own, shown in `priceDates`. Set dates to compare listings on the same stay.
- **Currency**: every price of the run is in the currency you choose, as Airbnb converts it.

```json
{
  "locations": ["Lisbon, Portugal"],
  "checkIn": "2026-12-04",
  "checkOut": "2026-12-07",
  "adults": 2,
  "roomTypes": ["entire_home"],
  "maxListingsPerSearch": 0,
  "includeCalendar": true,
  "currency": "EUR"
}
```

### Output

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. A listing from a search with dates:

```json
{
  "input": "Vincennes",
  "id": "775314634015847773",
  "url": "https://www.airbnb.com/rooms/775314634015847773",
  "name": "Bright and comfortable apartment",
  "latitude": 48.85,
  "longitude": 2.4337,
  "rating": 4.89,
  "reviewsCount": 258,
  "isSuperhost": true,
  "beds": 1,
  "bathrooms": 1,
  "roomSummary": ["1 queen bed", "1 bath"],
  "totalPrice": 350,
  "originalTotalPrice": 428,
  "pricePerNight": 116.67,
  "nights": 3,
  "priceLabel": "€350 for 3 nights, originally €428",
  "freeCancellation": true,
  "checkIn": "2026-12-04",
  "checkOut": "2026-12-07",
  "currency": "EUR"
}
```

With details and calendar, the same row also carries:

```json
{
  "propertyType": "Entire rental unit",
  "personCapacity": 2,
  "categoryRatings": {"cleanliness": 4.8, "accuracy": 4.9, "checkin": 5, "communication": 5, "location": 4.9, "value": 4.8},
  "amenities": ["Wifi", "Kitchen", "Free washer – In unit"],
  "host": {"id": "123456789", "name": "Ana", "isSuperhost": true, "yearsHosting": 4, "rating": 4.89, "reviewsCount": 258},
  "calendar": {
    "next30": {"days": 30, "available": 13, "availabilityRate": 0.433},
    "next90": {"days": 90, "available": 73, "availabilityRate": 0.811},
    "minNightsMostCommon": 2,
    "firstAvailableCheckin": "2026-11-08"
  },
  "calendarDays": [{"date": "2026-11-08", "available": true, "minNights": 2, "maxNights": 1125, "availableForCheckin": true, "availableForCheckout": false}]
}
```

The `SUMMARY` record of the run's key-value store gives, for each input, its status, the count Airbnb announced, the listings served by the map cells, the cells and pages read and the duplicates skipped.

### Tips and advanced options

- **Monitoring new listings**: set **Run mode** to **New listings only** and schedule the task daily. The Actor remembers what it returned for this input and charges only new listings. A listing counts as new the first time this input sees it, so one that was hidden from search during earlier runs shows up as new when it comes back.
- **Webhook**: add a Slack or Discord incoming webhook URL to get a message at the end of each run that returned something.
- **Speed**: a city of 4,000 listings takes under a minute, Paris about 5 minutes. The listing page and the calendar each add one request per listing, so a detailed run is slower.
- **Pipelines**: the listing URL / ID mode reads one listing per request, for tools that already know which listings they want.

### FAQ, disclaimers and support

#### Does it return every listing that exists in a city?

It returns every listing Airbnb shows in search for that area and those filters. Listings that are unlisted, snoozed, or not available for your dates (when you set dates) are not in Airbnb's search, so they are not in the results either.

#### Why are some calendar days unavailable?

Airbnb does not say whether an unavailable day is booked or blocked by the host. The availability rate is therefore an upper bound of occupancy, not an occupancy figure. Airbnb does not show nightly prices in the calendar: the price of a stay comes from the search or the listing page for your dates.

#### Why do some listings go past my maximum price?

The price filters are applied by Airbnb, exactly as on the website. Airbnb compares its own base nightly price, so a few listings can show a price per night a little above your maximum once discounts and fees are counted. Filter `pricePerNight` or `totalPrice` in the dataset if you need a hard limit.

#### Why is the location a little off?

Airbnb shifts the coordinates of many listings for privacy until a booking is made. `isExactLocation` (with details) tells which ones are exact.

#### Is it legal to scrape Airbnb?

Our Actors are ethical and do not extract any private user data, such as email addresses, gender, or location. They only extract what the user has chosen to share publicly. We therefore believe that our Actors, when used for ethical purposes by Apify users, are safe. However, you should be aware that your results could contain personal data, such as a host's first name and profile picture. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

#### Something is wrong or missing?

Open an issue in the **Issues** tab: it gets an answer quickly. To call the Actor from your own code, see the **API** tab.

# Actor input Schema

## `locations` (type: `array`):

City, neighborhood or region, as you would type it on Airbnb: <code>Lisbon, Portugal</code>, <code>Brooklyn, New York</code>, <code>Lake Annecy</code>. The place is turned into a map area (OpenStreetMap), then the whole area is read.

## `searchUrls` (type: `array`):

Search pages copied from airbnb.com (any Airbnb domain). The map area (<code>ne_lat</code>, <code>sw_lng</code>…), dates, guests and filters of the URL are used as they are. Move the map before copying to target an exact area.

## `listingUrls` (type: `array`):

Listings to read one by one: <code>https://www.airbnb.com/rooms/41507104</code> or just <code>41507104</code>. Each one returns the full listing page (description, amenities, host, house rules, ratings by category) and, with dates, the total price of the stay.

## `checkIn` (type: `string`):

Optional. With check-in and check-out, every listing carries the total price for the stay, cleaning and service fees included, as Airbnb shows it. Without dates, Airbnb picks a stay of its own (shown in <code>priceDates</code>).

## `checkOut` (type: `string`):

Set with check-in.

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

Only listings that fit this many guests. 0 = no guest filter.

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

Children aged 2 to 17.

## `infants` (type: `integer`):

Under 2.

## `pets` (type: `integer`):

Above 0, only listings that allow pets.

## `minPrice` (type: `integer`):

In the chosen currency, per night.

## `maxPrice` (type: `integer`):

In the chosen currency, per night.

## `roomTypes` (type: `array`):

Leave empty for all types.

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

Only listings with at least this many bedrooms.

## `minBeds` (type: `integer`):

Only listings with at least this many beds.

## `minBathrooms` (type: `integer`):

Only listings with at least this many bathrooms.

## `instantBook` (type: `boolean`):

Only listings you can book without waiting for the host.

## `guestFavorite` (type: `boolean`):

Only listings with the Guest favorite badge.

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

Prices are returned in this currency, as Airbnb converts them.

## `maxListingsPerSearch` (type: `integer`):

Cap for each place or search URL. <b>0 = no cap</b>: with Full coverage, a large city returns tens of thousands of listings. With a cap, the map is read level by level, so a capped run is spread over the whole area rather than one corner.

## `fullCoverage` (type: `boolean`):

Airbnb shows at most 280 listings per search, whatever the area holds ("Over 1,000 homes"). On: the map is split into smaller areas until each one is fully served, then into price ranges where listings are packed in one building. Listings found twice are returned and charged once. Off: one plain search, 280 at most.

## `includeDetails` (type: `boolean`):

Reads each listing page: description, all amenities, host (name, Superhost, verified, years hosting, rating, reviews), house rules, ratings by category, capacity, every photo, and the stay price for your dates. One extra request per listing, charged as a detailed listing.

## `includeCalendar` (type: `boolean`):

Adds the day-by-day calendar (available, minimum and maximum nights, check-in and check-out days) and a summary: available days in the next 30, 90 and 365 days, most common minimum stay, first available check-in. Airbnb does not show nightly prices in the calendar, and an unavailable day can be booked or blocked by the host. Charged as a detailed listing.

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

How many months ahead, from the current month.

## `mode` (type: `string`):

<b>New listings only</b> remembers the listings already returned for this input and returns (and charges) only the new ones. Schedule it daily to watch an area for new listings.

## `registryKey` (type: `string`):

Only used by <b>New listings only</b>. Leave empty and the memory is named after your input (places, URLs, dates, filters), so the same input always finds its own memory. Set it to make several schedules share one memory.

## `webhookUrl` (type: `string`):

If set, the Actor POSTs a short JSON summary to this URL at the end of the run, <b>only when at least one listing was returned</b>. It carries <code>text</code> and <code>content</code> fields, so a Slack or Discord incoming webhook works as is. A redirect is not followed except 307 or 308; an unreachable webhook is logged and never fails the run.

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

Airbnb answers Apify datacenter IPs. The Actor starts some requests without proxy (fastest) and moves to these proxies when an IP is refused or slow.

## Actor input object example

```json
{
  "locations": [
    "Annecy, France"
  ],
  "adults": 0,
  "children": 0,
  "infants": 0,
  "pets": 0,
  "instantBook": false,
  "guestFavorite": false,
  "currency": "USD",
  "maxListingsPerSearch": 50,
  "fullCoverage": true,
  "includeDetails": false,
  "includeCalendar": false,
  "calendarMonths": 12,
  "mode": "all",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All listings in the default dataset.

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

Status of each place, search URL or listing: listings returned, count announced by Airbnb, listings served by the map cells, cells read, duplicates skipped, already seen listings skipped in monitoring mode.

# 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 = {
    "locations": [
        "Annecy, France"
    ],
    "maxListingsPerSearch": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("alexmorain/airbnb-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 = {
    "locations": ["Annecy, France"],
    "maxListingsPerSearch": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("alexmorain/airbnb-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 '{
  "locations": [
    "Annecy, France"
  ],
  "maxListingsPerSearch": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call alexmorain/airbnb-scraper --silent --output-dataset

```

## MCP server setup

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