# Airbnb API - Listings, Prices & Ratings, No 240 Limit (`feedsmith/airbnb-listings-scraper`) Actor

Scrape Airbnb search results as clean JSON: price, nightly/total breakdown, rating, coordinates, photos, room type. Automatically splits large searches past Airbnb's 240-listing cap. Optional per-listing detail enrichment.

- **URL**: https://apify.com/feedsmith/airbnb-listings-scraper.md
- **Developed by:** [TRUONG VAN HOA](https://apify.com/feedsmith) (community)
- **Categories:** Travel, Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 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.

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 Scraper API — listings, prices & ratings, no 240-result limit

An **Airbnb data API** with no login and no API key: search by location or paste your own
`airbnb.com/s/...` search URL and get every matching listing as clean, flat JSON — price (nightly
**and** total, with the full breakdown), star rating, review count, coordinates, photos, room
type, superhost/guest-favorite badges. Turn on **Exhaustive mode** and it automatically works
around Airbnb's own page-cursor limit instead of silently truncating at ~240-270 results like
every other Airbnb scraper on the Store.

- **Breaks the 240-result cap.** Airbnb's own search pagination caps out at 15 pages (~270
  results) *per query*, no matter how many listings actually match. The main incumbent Airbnb
  scraper just returns that capped page and stops — you get 240 rows even if 4,000 listings
  match. This Actor detects the cap live (via the page's own price histogram and map-bounds hint,
  no extra geocoding calls) and recursively splits the search into non-overlapping price bands,
  then map quadrants if a price band is still capped, merging and de-duplicating everything by
  listing id until it is below the cap, `maxItems` is reached, or a depth limit is hit (logged
  loudly if so — see Limits below).
- **Real nightly and total prices.** Airbnb shows a total-for-the-stay price on search cards, not
  a nightly rate. This Actor reads the per-listing night count from the page's own text (it can
  vary listing-to-listing due to minimum-stay rules, even for the same search dates) and derives
  `priceNightly` from `priceTotal / nights`, plus the raw `priceBreakdown` line items.
- **Optional deep enrichment.** Turn on `includeDetails` to fetch each listing's own page for
  description, amenities, house rules, host name + superhost flag, bedrooms/beds/baths and the
  six sub-ratings (cleanliness, accuracy, check-in, communication, location, value).
- **Pay only for results.** No monthly rental; duplicates and filtered-out listings are free.

### Use cases

| Use case | How |
| --- | --- |
| **STR investors / revenue managers** | `locations` + dates, `exhaustive: true`, `maxItems` in the thousands → every comparable listing's price and rating for a market, not just the first 240 |
| **AirDNA-alternative market research** | Same market, multiple date ranges over time (scheduled runs) → nightly-price and occupancy-proxy (`isNew`/`reviewsCount`) trends without a data-subscription contract |
| **Property managers** | `startUrls` with your own saved search filters (room type, price band, amenities) → a live comparable set for a specific neighborhood |
| **Price/rating dashboards** | `includeDetails: true` for a shortlist of listing URLs → full amenity and sub-rating breakdown per competitor |

### Input

| Field | Example | Notes |
| --- | --- | --- |
| `locations` | `["Lisbon, Portugal"]` | Free-text place names; Airbnb resolves them (redirect) to the canonical search page |
| `startUrls` | `["https://www.airbnb.com/s/...`] | Use your own saved Airbnb search URL(s) instead of/with `locations`. Every query param on the URL (price, map bounds, amenities, room type, dates...) is preserved |
| `checkIn` / `checkOut` | `2026-11-05` / `2026-11-08` | YYYY-MM-DD. Leave both empty for flexible-date pricing |
| `adults`, `children`, `infants`, `pets` | `2`, `0`, `0`, `0` | Guest counts |
| `priceMin` / `priceMax` | `40` / `150` | Nightly price filter, in `currency` |
| `minBedrooms`, `minBeds`, `minBathrooms` | `1` | |
| `roomType` | `any` | `entire_home` | `private_room` | |
| `currency` | `"USD"` | 3-letter code |
| `locale` | `"en"` | Language for listing text |
| `maxItems` | `100` | Hard cap on saved (and charged) listings |
| `exhaustive` | `true` (default) | Split past the ~270-result single-query cap instead of returning just the capped page |
| `includeDetails` | `false` | Fetch each listing's own page for description/amenities/house rules/host/sub-ratings. One extra request + one `listing-details` charge per listing |
| `maxConcurrency` | `3` | Parallel detail-page fetches when `includeDetails` is on |
| `proxyConfiguration` | `{ "useApifyProxy": false }` | Recommended for large/exhaustive runs |

#### Example 1: one city, dates, plain search

```json
{
  "locations": ["Lisbon, Portugal"],
  "checkIn": "2026-11-05",
  "checkOut": "2026-11-08",
  "maxItems": 200
}
```

#### Example 2: exhaustive market census past the 270 cap

```json
{
  "locations": ["Lisbon, Portugal"],
  "checkIn": "2026-11-05",
  "checkOut": "2026-11-08",
  "exhaustive": true,
  "maxItems": 3000
}
```

#### Example 3: your own saved search + detail enrichment

```json
{
  "startUrls": [
    "https://www.airbnb.com/s/homes?query=Coimbra%2C%20Portugal&adults=2&checkin=2026-10-12&checkout=2026-10-15&price_min=40&price_max=120"
  ],
  "includeDetails": true,
  "maxItems": 50
}
```

### Output

One flat record per listing (real output, from a live run against `Lisbon, Portugal`):

```json
{
  "id": "557949219885897700",
  "url": "https://www.airbnb.com/rooms/557949219885897700",
  "name": "St. Anthony V",
  "title": "Apartment in Santa Maria Maior",
  "roomType": "entire_home",
  "searchLocation": "Lisbon, Portugal",
  "latitude": 38.71354,
  "longitude": -9.1357,
  "rating": 4.92,
  "reviewsCount": 201,
  "isNew": false,
  "isSuperhost": false,
  "isGuestFavorite": true,
  "priceTotal": 381,
  "priceNightly": 127,
  "priceOriginalTotal": null,
  "nights": 3,
  "currency": "USD",
  "priceLabel": "$381",
  "priceBreakdown": [
    { "description": "3 nights x $126.98", "price": 380.93, "priceLabel": "$380.93" }
  ],
  "photos": [
    "https://a0.muscache.com/im/pictures/miso/Hosting-557949219885897700/original/24d31335-....jpeg"
  ],
  "checkIn": "2026-11-05",
  "checkOut": "2026-11-08",
  "adults": 2,
  "scrapedAt": "2026-09-18T12:25:59.326Z"
}
```

With `includeDetails: true`, every record also gets (real output, from a live listing page):

```json
{
  "description": "This one-bedroom apartment in the city centre of Coimbra is perfect for couples ...",
  "amenities": ["Waterfront", "Kitchen", "Wifi", "..."],
  "houseRules": "Check-in after 3:00 PM; Checkout before 11:00 AM; Self check-in with lockbox; ...",
  "bedrooms": 1,
  "beds": 3,
  "bathrooms": 1,
  "personCapacity": 4,
  "hostName": "Azul Downtown Apartments",
  "hostIsSuperhost": false,
  "subRatings": {
    "cleanliness": 4.8,
    "accuracy": 4.71,
    "checkin": 4.45,
    "communication": 4.75,
    "location": 4.67,
    "value": 4.61
  }
}
```

Field notes:

- `priceNightly` is *derived* (`priceTotal / nights`), not a field Airbnb serves directly — it can
  differ slightly from your `priceMin`/`priceMax` filter, which Airbnb applies to its own internal
  base rate before some taxes/fees.
- `nights` is read per-listing from the page (minimum-stay rules can make it longer than your
  requested date range), falling back to your `checkIn`/`checkOut` span only when that text is
  missing.
- `rating`/`reviewsCount` are `null` for brand-new listings (`isNew: true`), not a parsing failure.

### Pricing

Pay per event — you are charged only for what is saved:

| Event | When |
| --- | --- |
| `listing-record` | Each listing saved to the dataset (duplicates and filtered-out listings are free) |
| `listing-details` | Each listing whose own page was successfully fetched and merged in (only charged on success) |

**Worked example:** an exhaustive run that saves 3,000 listings with `includeDetails` off costs
3,000 × $0.0012 = **$3.60**. Turning `includeDetails` on for the same run adds 3,000 ×
$0.002 = $6.00, for **$9.60** total. Set **Max total charge** on the run to cap spend — the Actor
stops cleanly (finishes the current batch, no partial charges) when the cap is reached.

### Limits & honest caveats

- **Prices depend on dates, currency and guest count.** The same search run on different dates (or
  with `adults` changed) will show different `priceTotal`/`priceNightly` — this is Airbnb dynamic
  pricing, not a bug.
- **Airbnb may rate-limit or serve an empty page-shell variant** (a ~2 MB response with no data,
  reproduced live roughly 1 request in 5 in this Actor's own testing) instead of blocking outright;
  the Actor retries both automatically (up to 3× for the page-shell variant, with back-off for
  actual 403/429/5xx) and stops a branch cleanly (with a warning, not a crash) after repeated
  failures, so very rarely a handful of listings on one page are skipped rather than the whole run
  failing.
- **Exhaustive mode has a depth limit** (4 price-band splits, then 3 map-quadrant splits). For an
  extremely dense micro-area that is still over the cap at maximum depth, the Actor accepts that
  capped leaf and logs a clear warning rather than splitting forever.
- **Use a proxy for large or frequent exhaustive runs.** All live testing for this Actor was done
  from a plain datacenter IP with zero blocking; a residential/datacenter proxy via
  `proxyConfiguration` is still recommended once you run at scale or on a schedule, to stay polite
  and reduce the page-shell-variant rate.
- **Public listing data only.** No host contact details, no guest names, nothing behind login.

### Code example

A runnable Python script (only `requests` needed) that calls this Actor and writes a CSV: [airbnb/city\_price\_summary.py](https://github.com/ankaibua-spec/feedsmith-examples/blob/master/airbnb/city_price_summary.py) — pull a city's listings for your dates and print nightly-price stats by room type.

### FAQ

**Is this affiliated with Airbnb?** No. This is an independent tool that reads Airbnb's public
search and listing pages. Nothing here is investment advice.

**Why did I get fewer than `maxItems`?** The market you searched has fewer matching listings than
`maxItems`, or exhaustive mode hit its depth limit on a very dense sub-area (check the run log for
"Depth limit reached" warnings).

**Why is `priceNightly` slightly outside my `priceMin`/`priceMax`?** See the field notes above —
Airbnb's own filter and this Actor's derived nightly rate aren't always computed on the exact same
base (taxes/fees).

**Can I use a `startUrls` search with map bounds or amenity filters I set on airbnb.com?** Yes —
copy the URL from your browser after setting filters; every query parameter is preserved, only
pagination/split parameters are added on top.

**Something wrong or missing?** Open an issue on the Actor's Issues tab.

# Actor input Schema

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

Free-text places to search, e.g. "Lisbon, Portugal" or "Austin, Texas". Each location becomes its own search (results are combined and de-duplicated). Leave empty if you use Start URLs instead.

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

Airbnb search URLs to use as-is instead of (or in addition to) Locations, e.g. a URL you copied from your browser after setting filters/map bounds on airbnb.com. All of the URL's own query parameters are preserved.

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

YYYY-MM-DD. Leave both dates empty to search with no dates (Airbnb then shows flexible-date pricing). Must be provided together with Check-out.

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

YYYY-MM-DD, must be after Check-in. Leave both dates empty to search with no dates.

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

Number of adult guests.

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

Number of children (ages 2-12).

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

Number of infants (under 2).

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

Number of pets travelling with the guest.

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

Only listings priced at or above this per-night amount, in the selected Currency.

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

Only listings priced at or below this per-night amount, in the selected Currency.

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

Only listings with at least this many bedrooms.

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

Only listings with at least this many beds.

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

Only listings with at least this many bathrooms.

## `roomType` (type: `string`):

Restrict the search to one listing type.

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

3-letter currency code Airbnb should price results in, e.g. USD, EUR, GBP.

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

Language/locale code for listing text, e.g. en, pt, es.

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

Stop after this many listings. You are only charged for listings actually saved. Default 100 keeps a first test run around $0.12; raise it (thousands are fine) for full-market exports.

## `exhaustive` (type: `boolean`):

When a search hits Airbnb's page limit (~240-270 results), automatically split it into price-band and then map-quadrant sub-searches and merge the de-duplicated results, up to Max listings. Turn off to fetch only what one plain search page returns.

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

Fetch each listing's own page for description, amenities, house rules, host name/superhost flag, bedrooms/beds/baths, sub-ratings and all photo URLs. Adds one extra request per listing and is charged as a separate enrichment event.

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

How many listing pages to fetch in parallel when Include listing page details is on.

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

Recommended for large or exhaustive-mode runs to reduce blocking. Leave off for small runs.

## Actor input object example

```json
{
  "locations": [
    "Lisbon, Portugal"
  ],
  "adults": 2,
  "children": 0,
  "infants": 0,
  "pets": 0,
  "roomType": "any",
  "currency": "USD",
  "locale": "en",
  "maxItems": 100,
  "exhaustive": true,
  "includeDetails": false,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One record per Airbnb listing: price (nightly + total breakdown), rating, coordinates, photos, room type, and optional detail-page enrichment.

# 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"
    ]
};

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

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

```

## MCP server setup

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