# Airbnb Listings Scraper & Price Tracker (`myth84/airbnb-listings-scraper`) Actor

Airbnb listings scraper and price tracker: scrape Airbnb search results for any location and dates — nightly and total price with taxes, rating, number of reviews, property type, coordinates and photos. Track price changes between runs. One flat row per listing, no personal data.

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

## Pricing

from $0.99 / 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

### Airbnb Listings Scraper & Price Tracker – prices and ratings, no personal data

Airbnb listings scraper and Airbnb price tracker: get nightly and total prices, ratings, review counts, room types and locations of Airbnb rentals for any place and dates, as a clean table or through an API — no login. Use it as an Airbnb scraper for vacation rental and short-term rental data: market research, pricing your own listing, price monitoring and deal alerts.

**No login, no cookies, no personal data:** nothing to sign in to, no cookies to paste, nothing about hosts or guests in the output.

- **What you get:** one flat row per Airbnb listing for any place and dates: nightly and total price, strike-through price, taxes on European sites, rating, review count, type, bedrooms / beds / bathrooms, badges, coordinates, photos and link; a price-monitoring mode that returns only what changed since your previous run. No personal data.
- **For whom:** hosts and property managers (pricing), short-term-rental analysts and investors, deal finders and price-alert tools, AI agents through MCP.
- **How much:** from $1.50 per 1,000 listings on the Free plan, lower on paid plans; Apify's free monthly credit of $5 covers about 3,333 listings. A fixed price per listing saved and nothing else (compute and proxies included, see the Pricing tab); unchanged prices in monitoring mode are never charged; `maxResults` caps every run (every destination when you list several).

**Sample output**: one real row from a Lisbon search, key columns only (the full row is under *Output example*):

```json
{
  "platformListingId": "29990558",
  "title": "Tram 28 - Marvelous Balcony & Tagus River Views 3D",
  "propertyType": "Apartment",
  "city": "Lisbon",
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "currency": "USD",
  "pricePerNight": 135.45,
  "priceTotal": 407,
  "taxesIncluded": false,
  "rating": 4.92,
  "reviewCount": 726
}
```

Scrape Airbnb search results for any location and dates and get **one flat row per listing**: nightly price and total price (with taxes on European sites), strike-through price, rating and number of reviews, property and room type, coordinates, bedrooms / beds / bathrooms, badges such as Superhost or Guest favorite, photos, free-cancellation flag and the dates the price applies to. **Monitor prices**: in price-monitoring mode a scheduled run compares every listing with your previous run of the same search and can return only the new listings and the price changes. Search **up to 50 destinations in one run** with `locations`. This Airbnb scraper reads the public search pages without logging in, keeps one browsing session per search for a consistent ranking, removes duplicates and stops exactly at the number of listings you ask for.

**No personal data, by design.** Nothing about hosts or guests is collected: no names, no profile photos, no ids, no review texts. The output only contains listing-level facts, the average rating, the number of reviews and the professional / individual status that Airbnb itself displays on the listing (EU trader status). The dataset is safe to store, share and feed to AI agents.

### What can you do with Airbnb data?

- **Track prices and availability** for a destination and dates: revenue management for hosts and property managers, deal finders, seasonal price studies. With `monitorPrices` and `onlyChanges`, a daily schedule gives you only the price drops, rises and new listings, and you pay only for those rows.
- **Build short-term rental datasets** for a city, a region or a map area, with coordinates ready for mapping and clustering.
- **Compare neighbourhoods and segments**: entire homes vs private rooms, professional vs individual listers, ratings, badges, price per night.
- **Feed AI agents, spreadsheets and BI tools** with a stable, documented schema shared by the other scrapers of this travel studio, so listings from several platforms have the same columns.

### What data does Airbnb Scraper extract?

Every row has every column: a value Airbnb does not show in search results is `null` (or `[]` for lists), so CSV and Excel exports keep the same columns from run to run.

| Group | Columns |
|---|---|
| Identity | `schemaVersion`, `platform`, `platformListingId`, `url`, `title`, `propertyType`, `roomType` |
| Location | `city`, `region`, `country`, `countryCode`, `latitude`, `longitude` |
| Stay | `checkIn`, `checkOut`, `nights`, `guests`, `isAvailable` |
| Prices | `currency`, `pricePerNight`, `priceTotal`, `priceBeforeDiscount`, `taxesAndFees`, `taxesIncluded`, `priceTotalAllIn`, `cleaningFee`, `serviceFee` |
| Quality | `rating`, `ratingMax`, `reviewCount`, `starRating`, `badges`, `isSuperhost`, `isNew`, `listedBy`, `cancellationPolicy`, `instantBook` |
| Size | `bedrooms`, `beds`, `bathrooms`, `maxGuests`, `amenities` |
| Photos | `imageUrls`, `imageCount` |
| Search context | `searchLocation`, `searchRank`, `searchPage`, `viewCountryCode`, `scrapedAt` |
| Price monitoring | `changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent` (null unless `monitorPrices` is on, see below) |

Columns that Airbnb never shows on search result cards stay empty for this Actor: `amenities` (always `[]`), `maxGuests`, `instantBook`, `starRating` (always `null`), and `cleaningFee` / `serviceFee` unless Airbnb itemises them in the price breakdown. `bedrooms`, `beds` and `bathrooms` are shown by Airbnb for searches with dates (fixed or relative) only, and `listedBy` on sites where Airbnb displays the professional / individual status (European sites such as airbnb.fr, not airbnb.com).

**Which price to compare.** On Airbnb, `priceTotal` is the total Airbnb displays for the stay after discounts, and what it holds depends on the site: European sites (airbnb.fr, airbnb.de, airbnb.es…) display a regulated total **with** the taxes (`taxesIncluded` is `true`, the taxes line is in `taxesAndFees`), while airbnb.com, the default `domain`, displays a total **before** taxes (`taxesIncluded` is `false`, as in the sample above; inferred from Airbnb's price style, since most airbnb.com cards do not repeat "before taxes") and does not show the taxes in search results. `priceTotalAllIn` is the total with taxes: `priceTotal` when `taxesIncluded` is `true`, `null` otherwise, never estimated (`taxesAndFees` is a part of `priceTotal`, not an amount to add). It is the column to compare stays across platforms, filled with the same rule by every travel scraper of the studio: set `domain` to a European site (`www.airbnb.fr`, with the `currency` you want) to get it filled.

### How to scrape Airbnb listings

1. **Enter a location** as you would type it on Airbnb ("Lisbon, Portugal", "Brooklyn, New York"), **a list of destinations** in `locations`, or paste one or more **Airbnb search URLs** copied from your browser, filters included.
2. **Choose the stay and the guests**: fixed dates (`checkIn` / `checkOut`) for a precise quote, or a relative stay (check-in in *N* days for *M* nights: `checkInDaysFromNow`, `nights`) for schedules that never expire. Without any date, Airbnb proposes its own stay for each listing: the prices are then for different stays and not comparable from one listing to another.
3. **Set the maximum number of listings** (per destination when you list several) and, if needed, the currency, the language, the Airbnb site and the price / room type / bedroom filters.
4. **Run** and open the **Output** tab: the *Overview*, *Prices*, *Location* and *Changes* views show the main columns; export everything as JSON, CSV, Excel or through the API.
5. **To follow prices**, schedule the run with fixed dates or a relative stay, tick `monitorPrices` and, to get only what changed, `onlyChanges`: every run after the first returns only the new listings and the price changes (see *Price monitoring* below).

### Input

Give a list of `locations` (one or more destinations) or at least one URL in `startUrls`; `location` (a single destination) is still accepted. `checkIn` and `checkOut` go together, otherwise `checkInDaysFromNow` and `nights` give a relative stay, and with none of the four the search is flexible. Field names and defaults are those of the input form.

The search of our validation run, written with `locations`: 60 listings in Lisbon for 20–23 November 2026, 2 adults, prices in EUR on airbnb.com (every other field keeps its default):

```json
{
  "locations": ["Lisbon, Portugal"],
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "adults": 2,
  "currency": "EUR",
  "locale": "en",
  "domain": "www.airbnb.com",
  "maxResults": 60
}
```

Every field, with its type and default:

| Field | Type | Default | Description |
|---|---|---|---|
| `locations` | string\[] | `[]` | One or more destinations as typed on Airbnb (city, region, address or landmark), one per line, searched one after the other with the same dates, guests, filters and currency (up to 50, with `location`). When set, `maxResults` applies to each destination and each search URL. Prefilled with `["Lisbon, Portugal"]` in the form; clear it when you only use search URLs. |
| `location` | string | — | Single destination (kept for compatibility; prefer `locations`), as typed on Airbnb, searched before the destinations of `locations`. Leave empty when you use search URLs. |
| `startUrls` | string\[] | `[]` | Airbnb search result URLs copied from your browser, e.g. `https://www.airbnb.com/s/Lisbon--Portugal/homes`. Filters that have no field here (amenities, neighbourhoods, map area, categories) are kept; guests, currency, language, price and size fields below replace the URL's own, dates and room types too when set. |
| `checkIn` | string | — | Check-in date, YYYY-MM-DD. Leave both dates empty for the relative stay below; with the relative fields empty too, the search is flexible (Airbnb proposes a stay per listing, prices not comparable). |
| `checkOut` | string | — | Check-out date, YYYY-MM-DD, after check-in. Required when `checkIn` is set. |
| `checkInDaysFromNow` | integer | — | Without fixed dates: check-in this many days from today (UTC), recomputed at every run. Alone, the stay lasts 3 nights. |
| `nights` | integer | — | Without fixed dates: length of the relative stay (1–90). Alone, check-in is in 30 days. |
| `adults` | integer | `2` | Number of adults (prices depend on the number of guests). |
| `children` | integer | `0` | Children aged 2 to 12. |
| `infants` | integer | `0` | Infants under 2 (not counted in `guests`). |
| `pets` | integer | `0` | Pets travelling with you (pet-friendly listings only when set). |
| `maxResults` | integer | `100` | Stop after this many unique listings: for the whole run, or for each destination and each search URL when `locations` is set. You are never charged for more. Prefilled with 20 in the form. |
| `priceMin` | integer | — | Minimum nightly price, a whole number in the selected currency (Airbnb's own price filter). |
| `priceMax` | integer | — | Maximum nightly price, a whole number in the selected currency, at least `priceMin`. |
| `roomTypes` | string\[] | `[]` | Any of `entire_home`, `private_room`, `shared_room`, `hotel_room`. Empty = all types. |
| `minBedrooms` | integer | — | Minimum number of bedrooms. |
| `minBeds` | integer | — | Minimum number of beds. |
| `minBathrooms` | integer | — | Minimum number of bathrooms. |
| `currency` | string | `"USD"` | ISO 4217 code of the currency for every price (USD, EUR, GBP, CAD, AUD, CHF, JPY, BRL, MXN, INR or any code Airbnb supports). |
| `locale` | string | `"en"` | Airbnb language code for titles, property types and badges (en, fr, es, de, it, pt, or en-GB style). |
| `domain` | string | `"www.airbnb.com"` | Airbnb site to query, e.g. www.airbnb.fr or www.airbnb.co.uk. Listing URLs use this domain. |
| `monitorPrices` | boolean | `false` | Compare the total price of every listing with the one recorded by your previous run of the same search (`changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent`). When on, `maxResults` is the number of listings compared. |
| `monitorStoreName` | string | `"airbnb-price-monitor"` | Key-value store of your account that keeps the previous prices (one record per search). Letters, digits and dashes. |
| `onlyChanges` | boolean | `false` | With `monitorPrices`: save and charge only the new listings and the price changes; a run without any change succeeds with an empty dataset. |
| `proxyConfiguration` | object | `{"useApifyProxy":true}` | Apify datacenter proxies by default, included in the price; keep them, or bring your own proxy URLs. A proxy country is recorded in `viewCountryCode`. |
| `splitSearchArea` | boolean | `true` | When a search is saturated (15 pages), split its map area into quadrants until `maxResults` is reached. |
| `includeMapPins` | boolean | `true` | Also keep the listings shown only as map pins next to the result grid. |
| `maxRequests` | integer | `500` | Safety cap on result pages fetched in one run (about 18 listings per page). |
| `delayMs` | integer | `800` | Pause between two page requests of the same search, in milliseconds. |
| `debugStoreHtml` | boolean | `false` | Debugging only: store every fetched page in the key-value store. |

**Several destinations in one run.** List them in `locations` (with or without `location`): each destination is searched on its own with the same dates, guests, filters and currency, and `maxResults` applies to each of them. This input returns up to 50 listings for each of the three cities, 150 at most:

```json
{
  "locations": ["Paris, France", "Rome, Italy", "Barcelona, Spain"],
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "adults": 2,
  "currency": "EUR",
  "maxResults": 50
}
```

The `searchLocation` column tells which destination found each listing. A listing found by two destinations (neighbouring areas, a city and one of its districts) is saved and charged once, under the first destination of the list. Without `locations`, `maxResults` keeps capping the whole run, `location` and search URLs together.

### Output example

One real row, produced by the Actor from a Lisbon search on airbnb.com (USD, English, 20–23 November 2026, 2 adults). All columns are present, in this order, for every listing:

```json
{
  "schemaVersion": "travel-listing/1.5",
  "platform": "airbnb",
  "platformListingId": "29990558",
  "url": "https://www.airbnb.com/rooms/29990558?check_in=2026-11-20&check_out=2026-11-23&adults=2",
  "title": "Tram 28 - Marvelous Balcony & Tagus River Views 3D",
  "propertyType": "Apartment",
  "roomType": "entire_place",
  "city": "Lisbon",
  "region": "Lisbon",
  "country": "Portugal",
  "countryCode": "PT",
  "latitude": 38.71785,
  "longitude": -9.12793,
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "nights": 3,
  "guests": 2,
  "currency": "USD",
  "pricePerNight": 135.45,
  "priceTotal": 407,
  "priceBeforeDiscount": null,
  "cleaningFee": null,
  "serviceFee": null,
  "taxesAndFees": null,
  "taxesIncluded": false,
  "priceTotalAllIn": null,
  "rating": 4.92,
  "ratingMax": 5,
  "reviewCount": 726,
  "starRating": null,
  "bedrooms": 1,
  "beds": 2,
  "bathrooms": 1,
  "maxGuests": null,
  "amenities": [],
  "badges": [
    "Guest favorite"
  ],
  "isSuperhost": false,
  "listedBy": null,
  "instantBook": null,
  "isNew": false,
  "isAvailable": true,
  "cancellationPolicy": "free_cancellation",
  "imageUrls": [
    "https://a0.muscache.com/im/pictures/47608a2a-ea37-4d3d-ab8b-9272427a6b25.jpg",
    "https://a0.muscache.com/im/pictures/46db520d-3ffe-47eb-abf9-6f3ba4c7aa19.jpg",
    "https://a0.muscache.com/im/pictures/6903ddaf-5883-4132-93b7-652c73375676.jpg",
    "https://a0.muscache.com/im/pictures/f8e6338c-a125-4fc6-8d90-56c26a8d1c43.jpg",
    "https://a0.muscache.com/im/pictures/52d5a186-a310-46af-a98b-cc592c1f72f8.jpg",
    "https://a0.muscache.com/im/pictures/cfa2acf4-b883-45e5-989e-f45b5f23d658.jpg"
  ],
  "imageCount": 6,
  "searchLocation": "Lisbon, Portugal",
  "searchRank": 1,
  "searchPage": 1,
  "viewCountryCode": null,
  "scrapedAt": "2026-10-19T08:00:00.000Z",
  "changeType": null,
  "previousPrice": null,
  "priceDelta": null,
  "priceDeltaPercent": null
}
```

On European Airbnb sites the same row also carries `taxesAndFees` (the regulated total includes taxes), `listedBy` ("professional" or "individual") and, when discounted, `priceBeforeDiscount`.

The last four columns are filled in price-monitoring runs only (see below).

The **Output** tab offers four views of the same rows: **Overview** (title, type, city, dates, prices, rating, reviews, Superhost, link, photos), **Prices** (nights, nightly price, total, strike-through price, taxes, currency, cancellation), **Location** (city, region, country, coordinates, rank) and **Changes** (price-monitoring runs: change type, dates, total price, previous total, difference, search, link).

### Price monitoring

Tick `monitorPrices`, then **schedule the run** (daily is typical). The scraper keeps the last total price of every listing it compared in a key-value store of your account named `monitorStoreName` (`airbnb-price-monitor` by default), with one record per search (same location or URL, site, currency, language, dates or relative stay, guests and filters; the quadrants of a split area belong to their search). The first run of a search is the reference: every listing comes back with `changeType` = `"new"` (and `RUN_STATS.monitorBaseline` is true). From the second run on, every listing is compared with the recorded one: `price_changed` rows carry `previousPrice` (the previous total), `priceDelta` and `priceDeltaPercent`, the others are `unchanged`. With `onlyChanges` the dataset holds **only the new listings and the price changes**, and **only those rows are charged**; a run where nothing changed succeeds with an empty dataset. In both cases `maxResults` is the number of listings compared by the run.

A scheduled input that watches the 100 first Lisbon listings for a fixed stay:

```json
{
  "location": "Lisbon, Portugal",
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "adults": 2,
  "currency": "EUR",
  "maxResults": 100,
  "monitorPrices": true,
  "onlyChanges": true
}
```

A `price_changed` row, as the Actor produces it from the row of the output example when the previous run had recorded a total of 447 USD (the other columns are the same as above):

```json
{
  "platformListingId": "29990558",
  "title": "Tram 28 - Marvelous Balcony & Tagus River Views 3D",
  "checkIn": "2026-11-20",
  "checkOut": "2026-11-23",
  "currency": "USD",
  "priceTotal": 407,
  "changeType": "price_changed",
  "previousPrice": 447,
  "priceDelta": -40,
  "priceDeltaPercent": -8.9
}
```

What to know before relying on it:

- **Use fixed dates or a relative stay.** The compared price is `priceTotal`, the total of the stay, and a price is compared only with a previous total for the **same dates and currency**. With a relative stay (`checkInDaysFromNow`, `nights`) the record follows the definition of the stay instead of its dates: each run compares the total of the stay that starts *N* days later with the previous run's, for the same number of nights, so a daily schedule tracks the price at a constant lead time and never expires. In a flexible search Airbnb proposes its own week per listing: a listing whose proposed dates moved is reported `unchanged` and the new dates become its reference, so only listings that keep the same proposed week are really compared.
- **The record is written only after a clean run.** A blocked or failed search, a charge limit reached in the middle of a run or an interrupted run never overwrites the reference, so a bad day cannot erase your prices; the log says when a record was kept unchanged.
- **Rankings move.** Airbnb reshuffles results between sessions, so the set of compared listings (the first `maxResults` of the run) drifts a little from run to run; a listing that drops out of the results and comes back later is not reported as new (its last price is kept for 90 days).
- **One record per search.** Runs sharing a `monitorStoreName` share the records; changing the location, site, currency, language, dates (or relative stay), guests or filters of a search starts a new reference for it. Each destination of `locations` has its own record; keep the same list, in the same order, from run to run, since a listing found by two destinations is compared under the first one. Delete the key-value store in Apify Console to start over.

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

This Actor uses **pay-per-event pricing: you pay a fixed price per listing** saved to your dataset, and nothing else. On the Free plan, listings cost $1.50 per 1,000, so Apify's free monthly credit of $5 covers about 3,333 listings; paid plans pay less per listing, and the price of every plan tier is on the **Pricing tab** of this page.

| What you pay for | How it is counted | Price |
|---|---|---|
| `result` event: one listing saved to your dataset | once per unique listing of the run, capped by `maxResults` (per destination with `locations`) | Pricing tab, per plan tier |
| Run start | a symbolic amount, once per run | Pricing tab |
| Compute, proxies, storage and data transfer | included: no platform usage is passed on to you | — |

The cost of a run is known before you start: 1,000 listings cost 1,000 times the `result` price of your tier, whatever the number of pages fetched. In price-monitoring mode with `onlyChanges`, only the new listings and the price changes are charged. A page of 18 listings takes a few seconds; a run of a few hundred listings takes a couple of minutes.

### Limits and behaviour worth knowing

- **270 listings per search.** Airbnb returns at most 15 pages of 18 listings for one search. With `splitSearchArea` on, the scraper divides a saturated map area into quadrants (up to three levels) until it reaches `maxResults`; whole cities therefore need many more requests than small areas.
- **Rankings move.** Airbnb recomputes its ranking per session. The scraper keeps one session per search (same proxy session, same cookies, same search session id) and removes duplicates by listing id, so a listing appears once per run even when Airbnb shows it on two pages. The count of listings per page can be slightly below 18 for that reason.
- **What the prices mean.** `priceTotal` is the total of the stay shown on the card, after every discount (weekly, monthly, early-bird, special offers); `pricePerNight` is the nightly price after the same discounts, before taxes and itemised fees; `priceBeforeDiscount` is the total before discounts (the strike-through price, or the total plus the discount lines of Airbnb's breakdown), null without a discount. So `priceTotal` = `pricePerNight` × `nights` + `taxesAndFees` + `cleaningFee` (when itemised), to the rounding of the displayed prices (airbnb.com rounds totals to the unit). `priceTotal` / `nights` is the nightly cost, taxes included when `taxesIncluded` is `true`.
- **Prices depend on the site and the dates.** European sites (airbnb.fr, airbnb.de, airbnb.es…) display a regulated total including taxes and fees, itemised in `taxesAndFees`; airbnb.com shows a total before taxes. `taxesIncluded` says which, and `priceTotalAllIn` holds the total with taxes when Airbnb displays it. Always compare rows obtained with the same `domain`, `currency` and dates.
- **Search cards show a subset of the listing.** Amenities, maximum guests, instant booking, hotel stars and detailed fees are not on search result cards and are therefore `null` or empty; bedrooms, beds and bathrooms appear for searches with dates (fixed or relative), not in flexible searches. A card that lists beds without a bedroom line is counted as 1 bedroom for a private room and 0 for an entire studio.
- **Blocking.** Airbnb search pages are served to Apify datacenter proxies without challenge in our tests. If a page is unusable, the scraper opens a new session and retries once, then gives up on that search and reports it in the log. If a whole run is blocked, raise `delayMs` and try again later, or open an issue with your run id: blocking changes are fixed as a priority.
- **No empty successes.** A run that finds no listing ends as failed with an explicit message (location misspelt, filters too strict, or blocked), so that your schedules and integrations notice. The one exception is a price-monitoring run that compared listings and found no change: it succeeds with an empty dataset.

### Integrations and API

Start the Actor from Apify Console, schedule it, or call it from the [Apify API](https://docs.apify.com/api/v2) with any language (the **API** tab shows ready-made code for Node.js, Python and curl). Webhooks notify your systems when a run finishes; the dataset can be pulled as JSON, CSV, Excel, XML or RSS, or pushed to Google Sheets, Make, Zapier, n8n, Slack, Airtable, Google Drive and more through the Apify integrations.

### Use it from your AI assistant

Add this Actor as a tool to Claude, ChatGPT, Cursor, VS Code or any MCP client with the Apify MCP server URL below (sign in with your Apify account when asked):

`https://mcp.apify.com?tools=myth84/airbnb-listings-scraper`

Then ask in plain words. Copy this prompt and change the details:

```text
Use myth84/airbnb-listings-scraper to find 20 Airbnb stays in Porto, Portugal for 2 adults with prices in EUR, best rated first. Input: {"location": "Porto, Portugal", "adults": 2, "currency": "EUR", "maxResults": 20}. Add checkIn and checkOut (YYYY-MM-DD), or checkInDaysFromNow and nights, so that every price is for the same stay. Show the title, price per night, total price, rating, number of reviews and link of each stay.
```

### For AI agents (MCP)

Through the [Apify MCP server](https://docs.apify.com/mcp) this Actor is a tool that answers questions such as *"what do apartments in Lisbon cost for two adults from 20 to 23 November?"* or *"list the best-rated entire homes under 150 USD a night in Porto"*.

**Minimal input** (every other field keeps its default; raise `maxResults` only when the question needs more rows):

```json
{
  "location": "Porto, Portugal",
  "adults": 2,
  "currency": "EUR",
  "maxResults": 20
}
```

- **Key output fields**: `platformListingId`, `title`, `url`, `pricePerNight`, `priceTotal`, `currency`, `checkIn` / `checkOut`, `rating`, `reviewCount`, `roomType`, `bedrooms`, `latitude` / `longitude`, `badges`; in monitoring mode `changeType`, `previousPrice`, `priceDelta`. Every column is always present, in a fixed order; `null` means Airbnb did not show the value. The full list is in *What data does Airbnb Scraper extract?* above.
- **Use it when** you need current Airbnb listings for a place and (optionally) dates: prices, ratings, review counts, type, location, photos. Pass `location` (or `locations` to compare several places in one call, or `startUrls`), `checkIn` / `checkOut` (or `checkInDaysFromNow` / `nights` for a stay relative to today), `adults`, `currency`, filters and a small `maxResults` (10–50 is enough for an answer). Without any date each listing is priced for its own stay: do not compare those prices. To report price changes between two calls, pass `monitorPrices: true`, `onlyChanges: true` and the same dates (or relative stay) and `monitorStoreName`.
- **Do not use it for** host profiles, guest reviews, availability calendars, booking or anything behind a login: it does not collect personal data and only reads public search results.
- **Cost per 1,000 results**: 1,000 `result` events, $1.50 per 1,000 on the Free plan and less on paid plans (Pricing tab); nothing else is charged (in monitoring mode with `onlyChanges`, unchanged prices are free).
- **Typical time**: a few seconds per page of 18 listings: 20 listings in about ten seconds, a few hundred in a couple of minutes.

### Alternatives and switching

- **Official API.** Airbnb does not offer an open API for search results: its API is reserved to selected partners. This Actor reads what any visitor sees in Airbnb search results (public prices, ratings, review counts, locations) with no account, no keys and no approval.
- **Your own scraper.** Nothing to build or maintain: sessions, retries, pagination, deduplication and Airbnb page changes are handled for you. The columns stay the same from run to run, a run that finds nothing fails with an explicit message instead of succeeding empty, and a scheduled test run catches Airbnb changes early.
- **Another scraper from the Store.** What you can count on here: `maxResults` is a hard cap (per destination with `locations`), the run stops at that number of listings and you are never charged for more; with `monitorPrices` and `onlyChanges`, a scheduled run returns and charges only the new listings and the price changes; `taxesIncluded` says what each total holds, and `priceTotalAllIn` gives the total with taxes to compare with the other platforms. The input names differ from one Actor to the next; the usual ones map to this Actor as follows:

| Usual input | Here | Note |
|---|---|---|
| `search`, `query`, `location` | `location`, or `locations` for up to 50 places | Place as typed on Airbnb |
| `startUrls` | `startUrls` | Airbnb search URLs, with their filters |
| `maxItems`, `limit` | `maxResults` | Unique listings per destination, never charged beyond |
| `checkin`, `checkout` | `checkIn`, `checkOut` | YYYY-MM-DD |
| `adults`, `children`, `infants`, `pets` | `adults`, `children`, `infants`, `pets` | Prices depend on them |
| `minPrice`, `maxPrice` | `priceMin`, `priceMax` | Per night, in `currency` |
| `currency`, `language` | `currency`, `locale` | ISO 4217 code, Airbnb language code |
| `proxy` | `proxyConfiguration` | Apify proxies, included in the price |

### FAQ

#### Does Airbnb Scraper collect personal data?

No. The scraper reads search result cards only and keeps nothing about hosts or guests: no names, photos, profile ids, contact details or review texts. It outputs listing facts, the average rating, the number of reviews, the Superhost badge of the listing and the professional / individual status Airbnb shows for EU listings. Every row goes through a privacy filter before it is saved.

#### Is it legal to scrape Airbnb?

The Actor only reads pages that Airbnb serves to any visitor without an account, and it stores no personal data. Scraping public data is generally lawful, but you are responsible for how you use the results and for complying with Airbnb's terms of service and the laws that apply to you (including copyright on listing texts and photos). Use the data for analysis, not to republish listings.

#### What if my runs get blocked? Do I need proxies?

Apify datacenter proxies (the default) work for Airbnb search pages and are included in the price. If a run reports blocked pages, increase `delayMs` and try again later, or bring your own proxy URLs; persistent blocking is fixed as a priority, open an issue with your run id.

#### Why do I get fewer listings than Airbnb announces, or some listings twice?

Airbnb says "over 1,000 homes" but serves at most 270 per search; the scraper splits the map area to find more. Rankings change between sessions, so the same listing can appear on two pages: the scraper removes those duplicates by listing id, which is why a page may yield fewer than 18 new listings. Across separate runs, use `platformListingId` to deduplicate.

#### Which currencies and languages are supported?

Any currency Airbnb accepts (`currency`, ISO 4217 code) and any Airbnb language (`locale`). Prices are converted by Airbnb into the requested currency; titles, property types and badges come in the requested language. You can also point the scraper at a country site with `domain`.

#### What are flexible dates?

Leave `checkIn`, `checkOut`, `checkInDaysFromNow` and `nights` empty and Airbnb proposes its own stay for each listing (about a week in the coming months): every row comes with its own `checkIn` / `checkOut` / `nights` and the matching price, so the prices are for different stays and not comparable between listings (the log says so at the start of the run). For comparable prices give fixed dates, or a relative stay: `checkInDaysFromNow` 30 and `nights` 3 search a 3-night stay starting in 30 days, recomputed at every run, which suits schedules.

#### Can I search several cities in one run?

Yes. Put up to 50 destinations in `locations`, one per line ("Paris, France", "Rome, Italy", "Barcelona, Spain"…), alongside or instead of `location`. Each destination is a separate search with the same dates, guests, filters and currency, and `maxResults` applies to each destination: 3 cities with `maxResults` 50 give up to 150 listings. Every row says in `searchLocation` which destination found it, and a listing found by two destinations is saved and charged once. Large lists need more page requests: raise `maxRequests` if the log says that some destinations were not searched.

#### Can I use my own Airbnb search URL with filters?

Yes. Paste search result URLs into `startUrls`; their filters (amenities, neighbourhoods, map area, categories…) are kept and the dates, guests, currency and language you set are applied on top. The `location` field can stay empty.

#### How often should I run it, and how do I get only price changes?

Every run fetches live search results. Prices and availability change daily, so schedule the Actor as often as your use case needs (daily is typical for price tracking) and compare runs by `platformListingId`, `checkIn` and `checkOut`, or let the Actor compare them for you: tick `monitorPrices` (and `onlyChanges`), give fixed dates or a relative stay and schedule the run. The first run records every listing of the search and returns it as `new`; each later run flags the listings that appeared since and the listings whose total price changed, with `previousPrice`, `priceDelta` and `priceDeltaPercent`, and with `onlyChanges` returns and charges only those rows. A run without any change succeeds with an empty dataset. The previous prices live in the key-value store `monitorStoreName` of your account; keep the same location, site, currency, dates (or relative stay), guests and filters from run to run.

#### Where do I get support?

Open an issue in the **Issues** tab of this Actor with your run id and input. Bugs and page-layout changes are fixed as a priority; feature requests (listing details, calendars, other platforms of the travel studio) are welcome.

### More scrapers by Flatrow Data Studio

The travel scrapers of the studio share the same core columns, so stays from several platforms land in one table; compare their prices on `priceTotalAllIn`, the total of the stay with taxes and fees, filled with the same rule by each scraper (`null` when a platform does not show the taxes, as on airbnb.com):

- [Airbnb Listing Details & Availability Calendar](https://apify.com/myth84/airbnb-listing-details-scraper): amenities, house rules, six category ratings and a 1–12 month availability calendar for the Airbnb listing URLs you give.
- [Booking.com Hotels Scraper & Price Tracker](https://apify.com/myth84/booking-hotels-scraper): Booking.com hotel prices for your dates with the excluded taxes, review scores and stars, search URLs and price monitoring.
- [Expedia Hotels Scraper: Prices & Ratings](https://apify.com/myth84/expedia-hotels-scraper): Expedia hotel prices per night and for the whole stay, guest ratings and review counts, to compare with the other platforms.

Did this scraper give you the data you needed? A rating on its Store page helps other people find it. For a bug or a missing field, open an issue in the **Issues** tab.

### Legal note

This Actor reads publicly available Airbnb search pages without logging in and collects no personal data. You are responsible for your use of the data and for complying with Airbnb's terms and the laws that apply to you.

Not affiliated with, endorsed or sponsored by Airbnb. Airbnb is a trademark of Airbnb, Inc.

# Changelog

This Actor's version history is a separate document: https://apify.com/myth84/airbnb-listings-scraper/changelog.md

# Actor input Schema

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

One or more destinations, one per line, as typed in the Airbnb search box, e.g. <code>Lisbon, Portugal</code>, <code>Brooklyn, New York</code>. Each one is a separate search with the same dates, guests, filters and currency, and <b>Maximum listings applies to each destination</b> (and to each search URL). A listing found twice is saved and charged once; <code>searchLocation</code> tells which one found it. Up to 50. Default empty: clear it if you paste search URLs below.

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

Airbnb search result URLs copied from your browser, one per line, e.g. <code>https://www.airbnb.com/s/Lisbon--Portugal/homes</code>. Filters that have no field here (amenities, neighbourhoods, map area, categories) are kept; the guests, currency, language, price and size fields below replace the URL's own, and so do dates and room types when you set them. Default empty: a location is enough for most searches.

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

Single destination (kept for compatibility; prefer <code>locations</code>): a city, region, address or landmark as typed in the Airbnb search box, e.g. <code>Lisbon, Portugal</code>, searched before the destinations of <code>locations</code> (up to 50 together). Leave empty when you use locations or paste search URLs.

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

Check-in date, YYYY-MM-DD, e.g. <code>2026-11-20</code>, given with Check-out. Leave both dates empty to use the relative stay below (better for schedules: fixed dates expire). With no date at all (these four fields empty) the search is flexible: Airbnb proposes its own stay for each listing, so the prices are of different stays and not comparable.

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

Check-out date, YYYY-MM-DD, after the check-in date, e.g. <code>2026-11-23</code>. Required when a check-in date is given.

## `checkInDaysFromNow` (type: `integer`):

Used when the dates above are empty: check-in this many days from today (UTC), 30 = in one month. A relative stay never expires in a schedule. Empty with Nights also empty = flexible search (prices not comparable).

## `nights` (type: `integer`):

Used when the dates above are empty: length of the relative stay. If only one of these two fields is set, the other one is 30 days (check-in) or 3 nights.

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

Number of adults, 0 to 16; prices depend on the number of guests. Default 2: a couple, the occupancy of our validation runs.

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

Number of children aged 2 to 12, 0 to 15; they count in the guests column, and prices depend on the number of guests. Default 0: adults only.

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

Number of infants under 2, 0 to 5 (not counted in the guests column). Default 0: no infant.

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

Number of pets travelling with you, 0 to 5; only pet-friendly listings are returned when set. Default 0: every listing, pet-friendly or not.

## `maxResults` (type: `integer`):

Stop after this many unique listings (for each destination and each search URL when you fill Locations); you are never charged for more. A result page holds about 18 listings and Airbnb caps one search at 15 pages (270 listings): keep <b>Split large areas</b> enabled (Advanced section) to go beyond that. Default 100: a city overview in about 6 pages (the form is prefilled with 20 for a quick first run).

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

Keep only listings whose nightly price is at least this amount, a whole number in the selected currency, e.g. <code>50</code> for 50 USD (Airbnb's own price filter). Leave empty for no minimum.

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

Keep only listings whose nightly price is at most this amount, a whole number in the selected currency, e.g. <code>150</code> for 150 USD (Airbnb's own price filter); it must be at least the minimum price. Leave empty for no maximum.

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

Select one or more room types to keep: entire home or apartment, private room, shared room, hotel room (Airbnb's own room type filter). Default empty: every type.

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

Keep only listings with at least this many bedrooms. Leave empty for no minimum.

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

Keep only listings with at least this many beds. Leave empty for no minimum.

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

Keep only listings with at least this many bathrooms. Leave empty for no minimum.

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

ISO 4217 code of the currency used for every price, e.g. USD, EUR or GBP. Pick one from the list or type any code supported by Airbnb. Default USD: one currency for every destination of the run, so prices stay comparable; pick EUR or GBP for local prices.

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

Airbnb language code for titles, property types and badges, e.g. <code>en</code>, <code>fr</code>, <code>es</code> or <code>en-GB</code>: two lowercase letters, optionally followed by a region. Default en: English property types and badges, the same words for every destination.

## `domain` (type: `string`):

Airbnb site to query, e.g. <code>www.airbnb.fr</code> or <code>www.airbnb.co.uk</code>. Listing URLs in the output use this domain; European sites display regulated totals including taxes. Default www.airbnb.com: the international site, used by our validation runs.

## `monitorPrices` (type: `boolean`):

Compare the total price of each listing with the one recorded by your previous run of the same search, and fill changeType, previousPrice and priceDelta. When on, maxResults is the number of listings compared per run. Default off: a plain search.

## `monitorStoreName` (type: `string`):

Name of the key-value store of your account that keeps the previous prices, one record per search. Use one store per schedule if you prefer, e.g. <code>airbnb-lisbon-daily</code>. Letters, digits and dashes only, at most 63 characters; upper case is lowered. Default airbnb-price-monitor: one store for all your monitoring runs.

## `onlyChanges` (type: `boolean`):

With price monitoring on: save and charge only the new listings and the listings whose total price changed since your previous run; a run without any change succeeds with an empty dataset. Default off: every compared listing is saved with its changeType.

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

Apify datacenter proxies (automatic) are enough for Airbnb search pages. If your runs get blocked, switch to Apify residential proxies. Selecting a proxy country records it in the viewCountryCode column. You can also provide your own proxy URLs. Default: Apify datacenter proxies, included in the price.

## `splitSearchArea` (type: `boolean`):

When a search is saturated (15 pages), divide its map area into quadrants and search each of them until the maximum number of listings is reached. Default on: without it one search stops at about 270 listings, too few to cover a whole city.

## `includeMapPins` (type: `boolean`):

Also keep the listings that Airbnb shows only as pins on the map next to the result grid (a couple more per page). Default on: they come with the same page, at no extra request.

## `maxRequests` (type: `integer`):

Safety cap on the number of result pages fetched in one run, all searches included (about 18 listings per page). Default 500: about 9,000 listings, far above a usual run.

## `delayMs` (type: `integer`):

Pause between two page requests of the same search, in milliseconds. Increase it if you get blocked; 0 disables the pause. Default 800: steady pacing that worked in our validation and control runs.

## `debugStoreHtml` (type: `boolean`):

Debugging only: save every fetched page in the run's key-value store. Slows the run down and uses storage. Default off: no page is stored.

## Actor input object example

```json
{
  "locations": [
    "Lisbon, Portugal"
  ],
  "startUrls": [],
  "adults": 2,
  "children": 0,
  "infants": 0,
  "pets": 0,
  "maxResults": 20,
  "roomTypes": [],
  "currency": "USD",
  "locale": "en",
  "domain": "www.airbnb.com",
  "monitorPrices": false,
  "monitorStoreName": "airbnb-price-monitor",
  "onlyChanges": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "splitSearchArea": true,
  "includeMapPins": true,
  "maxRequests": 500,
  "delayMs": 800,
  "debugStoreHtml": false
}
```

# Actor output Schema

## `listings` (type: `string`):

All extracted listings as dataset items, one flat row per listing. Append ?format=csv or ?format=xlsx for spreadsheets. The Overview view is selected by default.

## `runStats` (type: `string`):

Requests made, bytes transferred, blocked pages, area splits, listings seen and pushed, and the estimated platform cost of the run.

# 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": [
        "Lisbon, Portugal"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("myth84/airbnb-listings-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": ["Lisbon, Portugal"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("myth84/airbnb-listings-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": [
    "Lisbon, Portugal"
  ],
  "maxResults": 20
}' |
apify call myth84/airbnb-listings-scraper --silent --output-dataset

```

## MCP server setup

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