Airbnb Scraper — Listings Search & Detail
Pricing
from $3.40 / 1,000 listing results
Airbnb Scraper — Listings Search & Detail
Scrape Airbnb listings into clean structured JSON. Search any location with dates, guests, price and room-type filters — automatic price-range splitting gets past Airbnb's ~270-result cap. Or pass listing URLs for full detail: price, rating, coordinates, host, amenities count, description.
Scrape Airbnb listings into clean, structured JSON — search any location with dates, guests, price, room-type and bedroom/bed filters, or resolve listing URLs to full detail records (optionally including an availability calendar). Built for reliability: automatic price-range splitting gets past Airbnb's ~270-result cap, blocks are classified and logged (never crash a run), and every record comes back in one flat, predictable shape.
What Airbnb data does this scraper extract?
One flat JSON record per listing:
| Field | Meaning |
|---|---|
id | Airbnb listing id |
url | Direct airbnb.com/rooms/... link |
title | Listing name |
propertyType | e.g. "Entire rental unit", "Apartment in Lisbon" |
price / currency / pricePer | Numeric price, ISO currency, qualifier (total / night) |
rating / reviewsCount | Guest rating and review count |
roomType | Entire home/apt, Private room, Shared room, Hotel room |
beds / bedrooms / baths | Where Airbnb exposes them |
lat / lng | Listing coordinates |
hostName | Host first name (detail mode) |
imagesCount | Number of photos |
amenitiesCount | Number of amenities (detail mode) |
personCapacity / isSuperhost / description | Detail mode extras |
calendar | [{month, year, availableDays}, ...] — only populated when calendarMonths > 0 (detail mode); otherwise null |
mode | search or detail |
Input
| Name | Type | Default | Description |
|---|---|---|---|
mode | string (search | detail) | "search" | Search a location, or resolve specific listing URLs/ids to full detail records. |
location | string | — | Where to search, as typed on airbnb.com (e.g. "Lisbon, Portugal"). Required in search mode. |
listingUrls | array of strings | — | Listing URLs or bare numeric ids. Required in detail mode. |
checkIn / checkOut | string (YYYY-MM-DD) | — | Optional dates. With both set, prices become stay totals. |
priceMin / priceMax | integer | 0 | Nightly price filter bounds. 0 = no bound. |
adults / children | integer | 0 | Guest counts. |
roomType | string (enum) | "any" | any, entire_home, private_room, shared_room, hotel_room. |
minBedrooms | integer | 0 | Only listings with at least this many bedrooms. 0 = no filter. |
minBeds | integer | 0 | Only listings with at least this many beds. 0 = no filter. |
minBathrooms | integer | 0 | Only listings with at least this many bathrooms. 0 = no filter. |
calendarMonths | integer | 0 | Detail mode only. Fetch this many months (0–6) of availability into the calendar output field. 0 = skip. Best-effort (see Known limitations). |
maxItems | integer | 100 | Hard cap on listings returned. |
currency | string | "USD" | ISO currency code for returned prices. |
concurrency (Advanced) | integer | 2 | Parallel listing fetches in detail mode (max 4). Keep low — Airbnb rate-limits aggressively. |
maxPriceSplits (Advanced) | integer | 8 | How many times a search may auto-split its price range past the ~270-result cap. Lower it to cap cost on dense markets. |
proxyConfiguration | object | Apify Residential | Proxy group. Residential is strongly recommended — see Pricing below for its separate billing. |
How to scrape Airbnb listings with this Actor
Search mode — set location (e.g. "Lisbon, Portugal"), optionally checkIn/checkOut, priceMin/priceMax (per night), adults, roomType, minBedrooms/minBeds/minBathrooms and maxItems. With dates set, prices are stay totals for those dates. When a query hits Airbnb's ~270-result ceiling, the Actor automatically splits the price range into sub-ranges (up to maxPriceSplits times) and searches each — so large markets still come back complete up to your maxItems.
Detail mode — set mode to detail and pass listingUrls (full URLs or bare numeric ids). Each listing resolves to a full record including coordinates, per-category ratings source, host, photo and amenity counts, a plain-text description, and — if you set calendarMonths — a month-by-month availability summary.
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("nomad-agent/airbnb-scraper").call(run_input={"mode": "search","location": "Lisbon, Portugal","checkIn": "2026-09-23","checkOut": "2026-09-28","priceMax": 150,"maxItems": 200,"proxyConfiguration": {"useApifyProxy": True,"apifyProxyGroups": ["RESIDENTIAL"]},})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["price"], item["currency"], "|", item["title"], item["url"])
curl -X POST \"https://api.apify.com/v2/acts/nomad-agent~airbnb-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"mode": "detail", "listingUrls": ["https://www.airbnb.com/rooms/796929"]}'
Output example
Real push_data shape (detail mode, live-fetched 2026-07 — description truncated for brevity):
{"id": "796929","url": "https://www.airbnb.com/rooms/796929","title": "Rental unit in Lisbon","propertyType": "Entire rental unit","price": 465.0,"currency": "EUR","pricePer": "total","rating": 4.92,"reviewsCount": 463,"roomType": "Entire home/apt","beds": 2.0,"bedrooms": 1.0,"baths": 1.0,"lat": 38.71991,"lng": -9.13289,"hostName": "Bruno","imagesCount": 44,"amenitiesCount": 63,"personCapacity": 4,"isSuperhost": true,"description": "A very cool flat in a great location! ...","calendar": null,"mode": "detail"}
With calendarMonths set (e.g. 2), calendar is populated instead of null:
"calendar": [{"month": 7, "year": 2026, "availableDays": 2},{"month": 8, "year": 2026, "availableDays": 8}]
Pricing
Pay per event: $0.05 per Actor start and $0.004 per listing returned. 100 listings ≈ $0.45. No subscription — pay only for what you fetch.
Residential proxy is billed separately. The default proxyConfiguration prefill ({"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}) turns Apify's Residential Proxy on by default, and that traffic is billed separately from the actor's own pay-per-event pricing above — per GB, on top of your Apify plan. As of 2026, Apify lists Residential Proxy at roughly $7–8/GB depending on plan tier (Free/Starter ≈ $8/GB, Scale ≈ $7.5/GB, Business ≈ $7/GB) — check Apify's current proxy pricing for the up-to-date rate, since this changes independently of this Actor. If cost-sensitive, you can switch proxyConfiguration to a datacenter group or disable it, at the risk of more frequent blocks (see Anti-blocking notes).
Integrations
Dataset results export straight to JSON, CSV, Excel/XLSX, or a plain HTML table from the Apify Console. Pull them into Make, Zapier or n8n via Apify's native integrations, call run-sync-get-dataset-items from any HTTP client (see the curl example above), or use the Apify MCP server to let an LLM agent drive this Actor and read results directly.
Why this Actor?
Airbnb scrapers are notorious for flaky runs and messy nested output. This one is built around three choices: (1) it reads the same structured payloads Airbnb's own site renders from, not brittle HTML selectors; (2) every record is one flat object with stable field names — no digging through sections[12].section.cardData; (3) blocks and rate limits are detected and reported with the partial results you already paid for, instead of a crashed run.
Anti-blocking notes
Airbnb aggressively blocks datacenter and shared IPs. Residential proxies are strongly recommended — set proxyConfiguration to Apify residential (the default prefill). The Actor uses a fixed browser User-Agent, randomized 1–2.5 s delays between requests, and low concurrency (default 2, max 4). A 403/429/challenge response stops the run gracefully and keeps everything collected so far.
Use cases
- Market and pricing research for short-term rentals (STR analytics)
- Competitive sets for property managers and revenue managers
- Feeding availability/pricing data into investment models
- Monitoring specific listings (price, rating, review count over time)
FAQ
Is it legal to scrape Airbnb? The Actor only reads publicly visible listing data — the same data any logged-out visitor sees. Review Airbnb's terms and your local regulations for your use case; you are responsible for how you use the data.
Why do I get fewer results than Airbnb claims exist? Airbnb itself only serves ~15 pages (~270 listings) per query. The Actor splits price ranges to go past that; extremely dense markets may still need narrower filters (dates, room type).
Why are prices totals, not per night?
With checkIn/checkOut set (or when Airbnb picks default dates), listing cards show stay totals — pricePer tells you which one you got. Divide by nights for a nightly rate.
The run says "blocked" — what now? Switch to residential proxies, lower concurrency to 1, and retry. Partial results are kept.
Can I filter by property type (house, apartment, guesthouse, ...) instead of room type?
Not currently. roomType (entire home / private room / shared room / hotel room) is a confirmed, documented-by-reverse-engineering raw filter on Airbnb's search page. A separate "property type" facet (house vs. apartment vs. guesthouse) does not have a similarly discoverable raw query param on the public search page — Airbnb only exposes that facet through its interactive UI, not a stable URL param we could verify. propertyType is still returned per-listing in the output (e.g. "Entire rental unit") — it just isn't a request-side filter.
How reliable is calendarMonths?
It replays an internal, unauthenticated Airbnb GraphQL persisted query (PdpAvailabilityCalendar) using a known-good operation hash — live-verified working as of 2026-07. Unlike the amenities count (whose operation hash is rediscovered from a JS bundle on every run), this hash is not currently re-discoverable at runtime from any bundle we could find referenced by the listing page, so it's a static snapshot. If Airbnb ever rotates it, calendar fetches fail closed (best-effort — the rest of the record is unaffected) and calendar comes back null for that run until the Actor is updated.
Something broken or missing? Open an issue on the Actor's Issues tab — it is monitored and fixes ship fast.
Related Actors
- Greenhouse Jobs Scraper — Company Job Boards
- Lever Jobs Scraper — Company Job Postings API
- LinkedIn Jobs Scraper — No Login, No Cookies
Attribution
See NOTICE. Price-range-splitting pagination strategy inspired by digital-engineering/airbnb-scraper (GPL-3.0) — algorithm inspiration only, no code copied, translated or derived. All code in this Actor was written from scratch against Airbnb's current site (2026) and is MIT-licensed.