# Airbnb Scraper - Search Prices, Stay Dates & Guests (`benthepythondev/airbnb-search-scraper`) Actor

Export Airbnb listings for exact stay dates and guest counts with room IDs, property IDs, total prices, fees, ratings and public page cursors.

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

## Pricing

from $4.00 / 1,000 records

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?

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 Scraper: search prices for specific stays

Export public Airbnb search observations for a destination, exact stay dates and guest counts. Results preserve stable room IDs, separate hotel property IDs, displayed prices, total-price labels, price breakdowns, ratings and public source links.

Use this for date-specific accommodation comparisons, repeat price observations or a property research feed. A search result is an observation from Airbnb, not a reservation, live checkout quote or complete inventory of available homes.

**Compare the same stay:** set both dates, guest counts and currency before repeating a price observation. Free-plan result events cost **$5 per 1,000 listings**, plus the start event. The displayed total is source evidence, not a guaranteed final booking price; fee coverage follows the source labels.

### Quick start

Start with a tested example for Berlin stay prices:

- [Berlin two-night stays for two adults](https://apify.com/benthepythondev/airbnb-search-scraper/examples/berlin-two-night-stays-two-adults)

Open the example and choose **Try for free** to copy its input into your account. Check the input and set a maximum run charge before starting; Actor fees still follow the pricing below. When the run finishes, open the run's dataset and select the **Overview** view and download JSON for nested fields or CSV/Excel for a table.

| Your goal | Input to use |
|---|---|
| A fresh two-night comparison | Omit both dates; the stay starts seven days after each run |
| Track the same future stay | Set both `checkIn` and `checkOut` |
| Compare guest or currency scenarios | Set occupancy and EUR, USD or GBP explicitly |

The [real output example](#example-output) shows the record shape. The [Python workflow](#python-api-workflow) saves your own export to a local JSON file with a $0.30 maximum Actor charge.

### Input

```json
{
  "location": "Berlin, Germany",
  "adults": 2,
  "currency": "EUR",
  "maxResults": 30,
  "maxPages": 2
}
```

Supply `checkIn` and `checkOut` together as YYYY-MM-DD dates. If both are omitted, the Actor chooses a two-night stay starting seven days after the run's UTC date. Explicit stays must span 1 to 365 nights. `location` is a place name; Airbnb can include nearby areas, so this is not a strict municipal-boundary filter.

Adults default to two. Children, infants and pets default to zero. The requested currency can be EUR, USD or GBP. `maxResults` caps unique exports at 300, while `maxPages` bounds source pagination at 15 pages. These limits do not guarantee a particular result count. No account cookie, login or upstream scraper subscription is required.

### Exact stay matching

Airbnb sometimes places “similar dates” and alternative guest-count suggestions among search results. This Actor checks each record's explicit stay overrides and excludes records whose dates or guest counts differ from the request. It also verifies that redirects preserve the requested search parameters.

The summary counts excluded alternative stays. A listing can have both an exact-stay offer and another-date suggestion; its matching observation can still be exported. The Actor does not infer that a missing listing became occupied, sold or unavailable.

Pagination follows the cursors supplied by the public page. Listing IDs are deduplicated across the embedded data and subsequent pages. A repeated page containing only previously exported IDs raises an error rather than silently charging for duplicates. Search order may change between requests, so a bounded export is not guaranteed to cover every matching property.

### Example output

One complete record from a verified September 9, 2026 cloud run. Values and public media URLs can change or expire; this is a dated sample.

```json
{
  "listing_id": "28064699",
  "property_id": null,
  "source_location": "Berlin, Germany",
  "url": "https://www.airbnb.com/rooms/28064699?check_in=2026-09-16&check_out=2026-09-18&adults=2&children=0&infants=0&pets=0&currency=EUR",
  "title": "Room in Lichtenberg",
  "subtitle": "King bed, 2-minute walk to S-Bahn",
  "name": "Sai Gon - Private room of a vacation home",
  "price_display": "€129",
  "price_qualifier": "for 2 nights",
  "total_price": 129,
  "price_accessibility_label": "€129 for 2 nights",
  "price_breakdown": {
    "__typename": "DisplayPriceExplanationData",
    "title": "Price details",
    "subtitle": null,
    "priceDetails": [
      {
        "__typename": "DisplayPriceExplanationLineGroup",
        "items": [
          {
            "__typename": "DefaultExplanationLineItem",
            "description": "2 nights x €64.50",
            "priceString": "€129.00",
            "explanationData": null
          }
        ],
        "renderBorderTop": false,
        "collapsable": null
      }
    ]
  },
  "currency": "EUR",
  "check_in": "2026-09-16",
  "check_out": "2026-09-18",
  "adults": 2,
  "children": 0,
  "infants": 0,
  "pets": 0,
  "rating": null,
  "review_count": null,
  "coordinates": {
    "__typename": "Coordinate",
    "latitude": 52.50571,
    "longitude": 13.51949
  },
  "image_urls": [
    "https://a0.muscache.com/im/pictures/83d047de-14a9-455e-a8e9-30b995698fe0.jpg",
    "https://a0.muscache.com/im/pictures/c2d7af5a-ae09-4251-a7c0-d0bfe2afd94c.jpg",
    "https://a0.muscache.com/im/pictures/43380c02-908b-45d7-8200-61da273804ec.jpg",
    "https://a0.muscache.com/im/pictures/0da7c2e7-ae4a-47bb-af8e-0b711c41d0ac.jpg",
    "https://a0.muscache.com/im/pictures/c4ee3272-295d-4090-ab3e-621a1fb591dd.jpg",
    "https://a0.muscache.com/im/pictures/7a344792-9d48-4e7d-8afd-6a36545be6e5.jpg"
  ],
  "badges": [
    "Superhost"
  ],
  "matches_requested_stay": true,
  "search_url": "https://www.airbnb.com/s/Berlin--Germany/homes?checkin=2026-09-16&checkout=2026-09-18&adults=2&children=0&infants=0&pets=0&currency=EUR&query=Berlin%2C+Germany",
  "location_query": "Berlin, Germany",
  "source_page": 1,
  "scraped_at": "2026-09-09T20:13:09.965783+00:00"
}
```

### Prices and identities

| Field | Meaning |
|---|---|
| `listing_id` | The stable room/listing ID used in the public `/rooms/` URL. |
| `property_id` | Separate hotel property ID when supplied; never substituted for the room ID. |
| `price_display` | Original source display price, including its currency symbol. |
| `price_qualifier` | Original source label, such as `total` or `for 2 nights`. |
| `total_price` | Parsed displayed stay price when the source says `total` or names the exact requested number of nights and the display matches the requested currency. Otherwise null. Source rounding and fee coverage still apply. |
| `price_breakdown` | Source explanation, including fees, taxes and room-type disclaimers when exposed. |
| `check_in`, `check_out`, guest fields | The requested exact stay carried into the output and listing link. |
| `matches_requested_stay` | The source result has no conflicting date or occupancy override; not booking confirmation. |

The dataset also contains title, subtitle, localized name, rating, review count, approximate source coordinates, image URLs, badges, search URL, source page and UTC extraction time. `source_location` preserves Airbnb's resolved destination separately from your location query. The parser reads the primary search results and excludes unrelated recommendation sections. Coordinate precision comes from Airbnb; do not treat it as an exact property address.

During September 9, 2026 verification, the Berlin example included The Social Hub Berlin Mitte with a displayed €262 total. Its source breakdown exposed two nights and taxes/fees, while its hotel property ID differed from the bookable room ID. Both identities are preserved. Rates change and the same result may no longer be available when you rerun the example.

Currency is the requested display currency. A value that cannot be safely parsed remains in the original display fields with a null numeric total. The Actor does not convert currencies, divide a total into an invented nightly rate, or calculate a checkout total from incomplete fee data.

### Run summary and failure handling

The Output tab links to the dataset and `SUMMARY`. Inspect pages read, exported records, duplicates, alternative stays excluded and the stop reason. Complete-market coverage is never claimed. A missing public data payload, changed request parameters or repeated pagination page produces an explicit failure; previously saved records can remain in the dataset.

If a source page returns valid records but all are alternative stays, the run may export none. This is different from a blocked page or an empty JavaScript shell, which fails. If the source stops exposing a field, optional fields remain null instead of receiving invented values.

### Pricing and workflows

The Free price is $0.005 per exported listing plus a $0.00005 start at 512 MB. Thirty exported listings cost $0.15005 before Store plan discounts. Per 1,000 listings, Bronze is $4.50, Silver $4.25, and Gold, Platinum and Diamond are $4.00 at the currently saved rates. Start costs $0.000045 on Bronze, $0.0000425 on Silver and $0.000040 on Gold or higher. The Store pricing panel and actual run usage are authoritative.

Filtered alternatives and duplicate IDs do not create result events. The maximum charge control can stop a run before its result cap, and the summary records that stop. Repeated exports should be reconciled using listing ID plus stay dates, occupancy and currency; comparing prices for different contexts can produce misleading changes.

A saved Task can provide a repeat observation through API, n8n, Make or Zapier. Keep its stay dates current. This version collects search observations; it does not extract full host profiles, complete reviews, all room types or historical availability calendars.

### Python API workflow

Copy the first input JSON above into `input.json` and save this code as `export.py`. In a Python virtual environment, install `apify-client` with `python -m pip install apify-client`. Set `APIFY_TOKEN` to your own Apify token in your environment, then run `python export.py`. Keep the token out of source files and shared screenshots.

```python
import json
import os
from decimal import Decimal
from pathlib import Path

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("benthepythondev/airbnb-search-scraper").call(
    run_input=json.loads(Path("input.json").read_text(encoding="utf-8")),
    max_total_charge_usd=Decimal("0.30"),
    memory_mbytes=512,
    timeout_secs=300,
)
if not run or run["status"] != "SUCCEEDED":
    raise RuntimeError(f"Inspect the run before retrying: {run and run['id']}")

rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
Path("berlin-stays.json").write_text(
    json.dumps(rows, ensure_ascii=False, indent=2), encoding="utf-8"
)
print(f"Saved {len(rows)} records from run {run['id']}")
```

This starts a run in your account, waits for completion and saves `berlin-stays.json`. The $0.30 limit bounds Actor charges; it is not a promised bill or result count. Check the printed run ID and server log if the result is shorter than expected. If your client loses its connection, inspect the existing run in Console before starting another one. See the [official Python client](https://docs.apify.com/api/client/python/) for other run options.

For price comparisons, keep `listing_id`, both stay dates, all guest counts and `currency` together as the record key. Add `scraped_at` for an observation history. Use fixed future dates when tracking one stay; rolling dates compare different stays. Do not label a missing search result as booked.

For recurring runs without writing a scheduler, copy a tested public Task into your account and attach an Apify Schedule to that copy. Keep its maximum-charge limit and avoid overlapping runs when using monitor state. The Python example above is also suitable for an existing scheduler; run it from the directory containing `input.json` and provide the token through that scheduler's secret settings.

### Questions

**Are prices per night?** `total_price` accepts an explicit total or a price for the exact requested number of nights. A nightly label or mismatched stay length leaves it null. The Actor does not invent a nightly price or add fees missing from the source.

**Does a missing result prove it was booked?** No. Ranking, pagination limits and source changes can all remove a property from an observed search window.

**Can I request every property in a city?** The public search has its own bounds and changing order. This Actor makes no complete-inventory claim.

### Related tools

[Zumper](https://apify.com/benthepythondev/zumper-rental-scraper) supports rental-listing and price-change monitoring. [Furnished Finder](https://apify.com/benthepythondev/furnished-finder-scraper) covers furnished-rental listings with its own source and monitoring limits. Keep their rental-price observations separate from Airbnb totals for a dated stay.

Keywords: Airbnb search scraper, exact stay dates, accommodation prices, room IDs, hotel property IDs, total prices, occupancy, price observations, vacation rentals, JSON and CSV export.

# Actor input Schema

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

Place name passed to Airbnb search; source may include nearby areas.

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

Exact YYYY-MM-DD arrival date. Supply with checkOut; omitting both selects a two-night stay starting seven days from now.

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

Exact YYYY-MM-DD departure date; supply with checkIn for a stay of 1..365 nights.

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

Requested adults; alternative guest-count suggestions are excluded.

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

Requested children.

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

Requested infants.

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

Requested pets.

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

Display currency requested from Airbnb. Original display strings and total qualifiers are preserved.

## `maxPages` (type: `integer`):

Follow public source cursors, up to 15 pages. This is not complete market coverage.

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

Upper bound on unique listing IDs for the requested stay.

## Actor input object example

```json
{
  "location": "Berlin, Germany",
  "adults": 2,
  "children": 0,
  "infants": 0,
  "pets": 0,
  "currency": "EUR",
  "maxPages": 2,
  "maxResults": 30
}
```

# Actor output Schema

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

No description

## `summary` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("benthepythondev/airbnb-search-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("benthepythondev/airbnb-search-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 '{}' |
apify call benthepythondev/airbnb-search-scraper --silent --output-dataset

```

## MCP server setup

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