# Airbnb Scraper (`s-r/airbnb-scraper`) Actor

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

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Airbnb Scraper

An Airbnb scraper that returns listings for a location and a date window, with the nightly rate and the stay total as separate numbers, plus coordinates, rating, review count and badges. No login, no cookie, no API key.

It reads the search payload Airbnb already ships inside its own page, so every row comes from the same data the site uses to render the card.

### What you get

- **Nightly rate and stay total as separate fields.** Airbnb displays a total for the whole stay, which is not comparable between listings with different minimum-night rules. `price_nightly` is parsed out of Airbnb's own price breakdown, not divided out by hand
- **Coordinates on every row**, so results drop straight into a map or a distance calculation
- **Rating and review count** split into numbers rather than left as "4.97 (315)"
- **Currency you choose**, by naming the country the search runs from. Airbnb quotes in the currency of the visitor's country, so an unpinned scrape returns whatever currency the proxy landed in
- **Exact pagination.** Airbnb publishes a cursor list saying how many pages exist, so the walk stops at the real end instead of hammering until something breaks
- **Badges** such as Guest favourite, and the full image set per card
- **The dates the price actually applies to** on every row, so a stored result is still interpretable months later
- **No actor-start fee.** A run that returns nothing costs nothing

### Why scrape Airbnb

Short-let pricing is the most volatile retail price there is. It moves with the season, the day of the week, a football fixture and a conference, and none of that is visible from a single snapshot. For a host setting a rate, a property manager benchmarking a portfolio, or an analyst measuring what short-lets do to a city's housing, the useful thing is the same query repeated with the same dates.

Airbnb has no public listings API. What it does have is a server-rendered search page carrying its entire result set as JSON, which is what this actor reads. That is more stable than driving a browser and far cheaper, and it is why a run over a city takes seconds rather than minutes.

The detail that decides whether the data is usable is dates. Airbnb prices a stay, not a night, so a scrape without an explicit window returns totals for windows the site picked, of differing lengths, in a currency decided by wherever the request came from. The rows look fine and the numbers cannot be compared to each other. This actor makes the window explicit, reports it back on every row, and pins the currency.

### Input

| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
| `location` | string | yes | `Amsterdam, Netherlands` | Where to search, as you would type it on Airbnb |
| `checkin` | string | no | 30 days out | Check-in date, YYYY-MM-DD |
| `checkout` | string | no | 5 nights later | Check-out date, YYYY-MM-DD |
| `adults` | integer | no | `1` | Guest count, which changes eligibility and price |
| `children` | integer | no | `0` | Children in the party |
| `room_type` | select | no | `any` | `entire_home`, `private_room`, `shared_room`, `hotel_room` |
| `price_min` / `price_max` | integer | no | – | Airbnb's own price filter bounds |
| `proxy_country` | string | no | `nl` | Two-letter code for the country the search runs from, which decides the currency |
| `limit` | integer | no | `36` | Listings to return, 1 to 270 |
| `retries` | integer | no | `4` | Retry attempts per page, each with a rotated user agent and TLS fingerprint |

### Output

```json
{
  "position": 1,
  "listing_id": "1070144720855983147",
  "url": "https://www.airbnb.com/rooms/1070144720855983147",
  "title": "Guesthouse in Almere-Stad",
  "name": "Nice studio close to Amsterdam",
  "subtitle": "Cozy houseboat with parking in the center of Amsterdam",
  "rating": 4.71,
  "reviews_count": 77,
  "latitude": 52.3686,
  "longitude": 5.2142,
  "price_total": 435.0,
  "price_nightly": 87.0,
  "price_nightly_display": "€87.00",
  "nights": 5,
  "price_display": "€435",
  "price_qualifier": "total",
  "currency": "€",
  "badges": ["Guest favourite"],
  "image": "https://a0.muscache.com/im/pictures/miso/...",
  "checkin": "2026-09-30",
  "checkout": "2026-10-05",
  "adults": 1,
  "search_location": "Amsterdam, Netherlands"
}
```

### Use cases

**Setting a nightly rate as a host.** Run your own city with your own dates and guest count, sort by `price_nightly`, and you have the actual market for the week you are pricing. Filtering by `room_type` to match your own property is the difference between a useful benchmark and an average dragged around by hotel rooms and shared spaces.

**Portfolio benchmarking for property managers.** Schedule the same location and rolling date window daily, store `listing_id` and `price_nightly`, and you get a rate curve per property. Because listing ids are stable and coordinates come on every row, you can restrict the comparison to the few streets that actually compete with your unit rather than the whole city.

**Housing and tourism research.** Coordinates plus rating plus review count is enough to estimate density and activity per neighbourhood. Review count is the closest public proxy for how much a listing is actually let, and it comes as an integer rather than a string you have to unpick.

**Event-driven pricing.** Point the same location at the week of a conference or a match and at a control week, then compare `price_nightly`. Because both runs use explicit dates and the same currency, the delta is the event premium rather than an artefact of when you happened to run the scrape.

### How it compares

| | this actor | `tri_angle/airbnb-scraper` | `automation-lab/airbnb-listing` |
|---|---|---|---|
| Per 1.000 listings | **$2,50** | $4,00 | $5,00 |
| Actor-start fee | **none** | $0,0001 | **$0,005** |
| Store rating | – | **2,54** from 13 reviews | 5,00 from 3 |
| 30-day run failure rate | 0% across validation runs | **6,9%** | 2,0% |
| Nightly rate as its own field | **yes** | no | not stated |
| Coordinates | **yes** | yes | not stated |
| Currency pinned by country | **yes** | no | no |
| Explicit date window reported per row | **yes** | no | no |

Honest about the other side: `tri_angle/airbnb-scraper` has 761 monthly users against our zero, and `curious_coder/airbnb-scraper` will do a thin listing row for $0,20 per 1.000, which is a twelfth of our price. If all you need is a title and a URL, that one is cheaper and it works.

### Pricing

One event. `listing` costs $0,0025 per listing returned, which is $2,50 per 1.000. All pricing is pay-per-event, so you only pay for listings you actually receive. No actor-start fee, no per-compute-unit charges, and a run that returns nothing costs nothing.

### Limits and gotchas

- **Set your dates.** Without them the run picks a fixed window 30 days out and flags `datesWereDefaulted` in the summary. That keeps runs comparable to each other, but it is not the window you probably meant.
- **`proxy_country` decides the currency.** Leaving it empty means the proxy picks a random country and Airbnb quotes in that country's currency, which makes rows incomparable. This is the single most common way an Airbnb scrape produces unusable numbers.
- **Airbnb redirects to the country domain.** From a UK exit, airbnb.com answers with a small page that redirects to airbnb.co.uk in JavaScript rather than over HTTP. The actor detects this and follows it; without that, a UK-based search returns nothing at all.
- **Search results are not a complete inventory.** Airbnb ranks and caps what it shows, so a city with tens of thousands of listings still returns a few hundred per query. Narrow by neighbourhood, price band or room type to see further into the tail.
- **Prices include Airbnb's current display setting.** The total is what a guest would see for that window and guest count, which may or may not include fees depending on region and the site's own rules at the time.
- **Rating and review count are empty on brand new listings.** That is correct rather than missing, and typically affects one or two rows per page.

### FAQ

**Can I scrape Airbnb without an API key?**
Yes. Airbnb server-renders its search results and embeds them as JSON for the browser to display. There is no credential field in this actor's input.

**Why do I get prices in the wrong currency?**
Because Airbnb quotes in the currency of the country the request comes from. Set `proxy_country` to the market you care about and the currency follows. Leaving it empty gives you whatever country the proxy landed in.

**Can I get the nightly price rather than the stay total?**
Yes, as `price_nightly`, parsed from Airbnb's own price breakdown along with the number of nights it covers. Most Airbnb scrapers return only the total, which cannot be compared between listings with different minimum stays.

**How many listings can I get for one location?**
Airbnb publishes a cursor list, so the actor knows exactly how many pages exist and the summary reports it. Expect a few hundred per query rather than a full city inventory, and narrow the search to go deeper.

**Does it return coordinates?**
Yes, latitude and longitude on every row, which is what makes distance filtering and map work possible without a second geocoding step.

### Related Actors

- [Google Hotels Scraper](https://apify.com/s-r/free-google-hotels-scraper) — hotel prices across booking sites
- [Google Maps Scraper](https://apify.com/s-r/free-google-maps-scraper) — places, ratings and contact details
- [Google Maps Reviews Scraper](https://apify.com/s-r/free-google-maps-reviews-scraper) — reviews for any place

# Actor input Schema

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

Where to search, as you would type it on Airbnb. City and country works best, for example 'Amsterdam, Netherlands' or 'New York, United States'.

## `checkin` (type: `string`):

Check-in date, YYYY-MM-DD. Leave empty and the run picks a fixed window 30 days out so prices stay comparable between runs. Airbnb prices a stay rather than a night, so without dates the totals are not comparable at all.

## `checkout` (type: `string`):

Check-out date, YYYY-MM-DD.

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

Number of adults. Changes which listings are eligible and therefore the prices.

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

Number of children.

## `room_type` (type: `string`):

Restrict to one kind of space.

## `price_min` (type: `integer`):

Lower bound of Airbnb's own price filter, in the currency of the search.

## `price_max` (type: `integer`):

Upper bound of Airbnb's own price filter.

## `proxy_country` (type: `string`):

Two-letter country code for the exit used to reach Airbnb, for example nl, gb, us. This decides the currency Airbnb quotes in. Leave empty to let the proxy choose, which returns a random country's currency.

## `limit` (type: `integer`):

How many listings to return, 1 to 270. Airbnb pages 18 at a time and publishes an exact cursor list, so the walk stops at the real last page.

## `retries` (type: `integer`):

Retry attempts per page, each with a rotated user agent and TLS fingerprint.

## Actor input object example

```json
{
  "location": "Amsterdam, Netherlands",
  "checkin": "2026-10-10",
  "checkout": "2026-10-13",
  "adults": 1,
  "children": 0,
  "room_type": "any",
  "proxy_country": "nl",
  "limit": 36,
  "retries": 4
}
```

# Actor output Schema

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

One row per Airbnb listing.

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

itemCount, the dates actually searched, pagesAvailable and pagesFetched.

## `errors` (type: `string`):

Failures with a code and a redacted message. Absent when the run had none.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "location": "Amsterdam, Netherlands",
    "adults": 1,
    "proxy_country": "nl",
    "limit": 36,
    "retries": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/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 = {
    "location": "Amsterdam, Netherlands",
    "adults": 1,
    "proxy_country": "nl",
    "limit": 36,
    "retries": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/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 '{
  "location": "Amsterdam, Netherlands",
  "adults": 1,
  "proxy_country": "nl",
  "limit": 36,
  "retries": 4
}' |
apify call s-r/airbnb-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/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/WK46fRKFbGdNIt4E3/builds/aUT0LWEbtOo8VXxlt/openapi.json
