# Airbnb Scraper - Listings, Prices & Ratings (`juanoox/airbnb`) Actor

Extract Airbnb listings from any search URL with price per night, rating, review count, capacity and GPS coordinates. Prices come with their period and night count, so a total for the stay is never compared against a nightly rate.

- **URL**: https://apify.com/juanoox/airbnb.md
- **Developed by:** [Juan ignacio Veltri](https://apify.com/juanoox) (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 $3.00 / 1,000 results

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 — Listings, Prices & Ratings

Paste an Airbnb search URL. Get every listing as clean, structured data — with prices you can actually compare.

Works on any Airbnb country domain: `.com`, `.es`, `.com.ar`, `.mx`, `.com.br`, `.co.uk`, `.fr`, `.de`, `.it`.

***

### The price problem this solves

Airbnb shows **"$337 for 5 nights"** when your search has dates, and **"$67 per night"** when it does not. Most scrapers store the number and drop the qualifier, so a five-night total ends up in the same column as a nightly rate. Every average, every filter, every comparison you build on top of that is wrong.

This Actor always returns three fields together:

```json
"price":       { "amount": 337, "currency": "USD", "raw": "$337 USD for 5 nights" },
"pricePeriod": "stay",
"priceNights": 5
```

So `337 / 5 = 67` is a number you can trust. The `maxPricePerNight` filter does that division for you — a cheap listing is never excluded just because the stay is long.

### What you get per listing

| Field | Example |
|---|---|
| `name` | `Dana Apartments` |
| `propertyType` | `Rental unit in Madrid` |
| `rating` | `4.78` |
| `reviewsCount` | `222` |
| `price` + `pricePeriod` + `priceNights` | `337` · `stay` · `5` |
| `capacity`, `bedrooms`, `beds`, `bathrooms` | when the listing states them |
| `location.latitude` / `longitude` | exact coordinates |
| `isSuperhost` | `true` / `false` |
| `images` | full-resolution URLs |

### How to use it

#### Scrape a destination

Set your dates, guests and filters on Airbnb, then copy the URL:

```json
{
  "startUrls": [{ "url": "https://www.airbnb.com/s/Madrid--Spain/homes" }],
  "maxItems": 300
}
```

#### Well-rated listings under a nightly budget

```json
{
  "startUrls": [{ "url": "https://www.airbnb.com/s/Barcelona--Spain/homes" }],
  "minRating": 4.5,
  "minReviews": 50,
  "maxPricePerNight": 120,
  "onlySuperhost": true
}
```

#### Monitor new listings

Schedule it with `includeSeen` off — each run returns only listings that appeared since the last one.

### About the 300-result cap

**Airbnb limits every search to roughly 300 results.** That is the site's limit, not this Actor's, and no scraper can go past it on a single URL.

To cover a whole city, split the search and pass several URLs — by neighbourhood, by price band, or by date range. The run log tells you when a search has been exhausted so you know when to split.

### Input reference

| Field | Type | Default | What it does |
|---|---|---|---|
| `startUrls` | array | example URL | Airbnb search result URLs |
| `nameKeywords` | array | `[]` | Keep listings whose name matches any word |
| `minRating` | integer | — | Minimum rating, 0–5 |
| `minReviews` | integer | — | Minimum review count |
| `maxPricePerNight` | integer | — | Compared **per night**, not per stay |
| `onlySuperhost` | boolean | `false` | Only Superhost listings |
| `maxItems` | integer | `200` | Hard stop. **Your cost control** |
| `maxPagesPerUrl` | integer | `15` | Pages per URL (~18 listings each) |
| `includeSeen` | boolean | `true` | `false` returns only listings new since the last run |
| `proxyTier` | enum | `datacenter` | Starts cheap, escalates only if blocked |
| `maxProxyTier` | enum | `residential` | Set to `datacenter` to never spend residential bandwidth |
| `maxRequestsPerMinute` | integer | `40` | Throttle. Often more effective than switching proxies |

### Cost

Charged **per listing saved**. Listings removed by your filters or already seen are not charged.

The Actor starts on datacenter proxies and escalates to residential only when it detects an anti-bot challenge, so you do not pay for residential bandwidth unless the site actually demands it.

### FAQ

**Why do some listings have no rating?** Because they are new and have no reviews yet. The field comes back `null` and `reviewsCount` is `0`, rather than being guessed as zero stars.

**Why is `capacity` sometimes empty?** Airbnb only states guests, bedrooms and beds on some cards. Missing values are returned as `null` instead of being inferred.

**Why fewer results than Airbnb shows?** Either `maxItems`, or Airbnb's own ~300-result cap. Split the search into several URLs to go wider.

**Is this legal?** Only public search results are extracted — the same information any visitor sees without logging in. No login is bypassed and no personal data is collected.

### Support

Found a search URL that does not parse? Open an issue with the URL. Reports with a URL get fixed fastest.

# Actor input Schema

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

Paste search result URLs, e.g. https://www.airbnb.com/s/Madrid--Spain/homes. Set your dates, guests and filters on Airbnb first — the Actor copies that exact search. Works on any Airbnb country domain.

## `nameKeywords` (type: `array`):

Keep only listings whose name contains any of these words. Case and accent insensitive.

## `minRating` (type: `string`):

Keep only listings rated at or above this threshold. Listings without a rating are excluded. Apify input schemas cannot express decimals as numbers, so the thresholds are offered as choices.

## `minReviews` (type: `integer`):

Keep only listings with at least this many reviews. Useful to skip brand-new listings.

## `maxPricePerNight` (type: `integer`):

Airbnb often shows the total for the whole stay. This filter divides by the number of nights first, so a cheap listing is not excluded just for a long booking.

## `onlySuperhost` (type: `boolean`):

Keep only listings whose host carries the Superhost badge.

## `maxItems` (type: `integer`):

Stops the run at this many listings. 0 = no limit. This is your direct cost control.

## `maxPagesPerUrl` (type: `integer`):

How many result pages to follow per search URL.

## `includeSeen` (type: `boolean`):

Turn off for monitoring: each run returns only listings that appeared since the previous run.

## `proxyTier` (type: `string`):

Start on datacenter. The Actor escalates to residential automatically if it detects anti-bot challenges, so you do not pay for residential bandwidth unless it is needed.

## `maxProxyTier` (type: `string`):

Set to "Datacenter" to guarantee the run never consumes residential bandwidth.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep it low on Airbnb: aggressive concurrency triggers challenges.

## `maxRequestsPerMinute` (type: `integer`):

Throttles the crawl. Airbnb reacts to request rate, so lowering this is often more effective than switching proxies.

## `maxRequestRetries` (type: `integer`):

How many times to retry a blocked request. Each retry gets a different proxy IP, so a higher number is often what turns a blocked run into a successful one.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.airbnb.com/s/Madrid--Spain/homes"
    }
  ],
  "minRating": "any",
  "onlySuperhost": false,
  "maxItems": 200,
  "maxPagesPerUrl": 15,
  "includeSeen": true,
  "proxyTier": "datacenter",
  "maxProxyTier": "residential",
  "maxConcurrency": 4,
  "maxRequestsPerMinute": 40,
  "maxRequestRetries": 8
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "startUrls": [
        {
            "url": "https://www.airbnb.com/s/Madrid--Spain/homes"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("juanoox/airbnb").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 = { "startUrls": [{ "url": "https://www.airbnb.com/s/Madrid--Spain/homes" }] }

# Run the Actor and wait for it to finish
run = client.actor("juanoox/airbnb").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 '{
  "startUrls": [
    {
      "url": "https://www.airbnb.com/s/Madrid--Spain/homes"
    }
  ]
}' |
apify call juanoox/airbnb --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,juanoox/airbnb"
        }
    }
}

```

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/iIP8cgjGRN4Ph5npt/builds/RoOIbYiyijWyiU6wM/openapi.json
