Airbnb Scraper - Stays, Reviews, Hosts & Prices
Pricing
from $0.90 / 1,000 listing records
Airbnb Scraper - Stays, Reviews, Hosts & Prices
Scrape Airbnb stays by destination or link: nightly and total price, rating breakdown, host stats, amenities, house rules, coordinates, photos, and the full review history with text and star ratings. Dual mode, filters, incremental monitoring, resume, MCP export.
Pricing
from $0.90 / 1,000 listing records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Airbnb Scraper: Stays, Reviews, Hosts & Prices
Airbnb Scraper turns Airbnb search and listing pages into structured data: nightly and total price, the full rating breakdown, host stats, amenities, house rules, coordinates, photos, and the complete review history with text, star rating, date, reviewer first name and the host's reply. Search by destination or paste listing and search links, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Two ways to find stays. Search by destination with your own filters, or paste listing and search links and mix both in the same run.
- The complete review history, not a sample. Every review carries its text, star rating, exact date, language, the reviewer's first name and the host's public reply when there is one.
- Full listing detail on demand. Description, amenities, house rules, host stats and the complete rating breakdown, added only when you turn it on, so a plain search stays cheap.
- Goes past the site's own result pager. The website's pager stops at 270 stays per search; a run keeps returning stays until the destination genuinely runs out.
- Built for recurring monitoring. Incremental mode returns only new, updated and reappeared listings on repeat runs, and a run that cannot connect fails loudly instead of returning an empty dataset.
- Resume a large pull. Continue an interrupted run from its run ID or dataset ID without paying twice for listings it already collected.
- Filters that mirror the site. Price range, stay type, minimum bedrooms, beds and bathrooms, Superhost, Instant Book and Guest favourite, the same facets the site itself offers.
Use Cases
- Travel planning and price comparison: pull every stay in a destination for your dates and guest count to compare price, rating and location in one dataset.
- Short-term rental market research: track nightly rates, availability and stay types across neighbourhoods or cities over time.
- Host and property analytics: collect Superhost status, response rate, review scores and amenity mix across a market or a portfolio of listings.
- Review mining and reputation analysis: gather the full review history, text and star ratings included, for sentiment or trend analysis.
- Recurring price and availability monitoring: schedule the actor with incremental mode to get only what changed since the last run, and know when a tracked listing disappears.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
kind | "listing" |
listingId / url | "900000000000000001" / Airbnb link to the listing |
title / subtitle | "Example stay in Sample District" / "Hosted by Example Host" |
stayCategory / roomType / propertyType | "Entire home/apt" / "Entire home/apt" / "Entire rental unit" |
pricePerNight / pricePerNightListed | 180.0 / 210.0 |
priceTotal / priceCurrency | 540.0 / "EUR" |
nights / checkIn / checkOut / adults | 3 / "2026-11-10" / "2026-11-13" / 2 |
rating / reviewsCount | 4.9 / 57 |
ratingCleanliness / ratingAccuracy / ratingCheckin / ratingCommunication / ratingLocation / ratingValue | 4.95 / 4.9 / 4.95 / 5.0 / 4.85 / 4.8 |
isGuestFavorite / isSuperhost / badges | true / true / [] |
personCapacity / bedrooms / beds / bathrooms | 4 / 2 / 3 / 1.0 |
latitude / longitude / city / country | approximate coordinates / "Sample City" / "Sample Country" |
images / imagesCount | list of photo URLs / 24 |
description / highlights / amenities / amenitiesCount | listing text / bullet list / amenity names / 38 (when detail is read) |
houseRules / safetyAndProperty / cancellationPolicy / licenseInfo | free text sections (when detail is read) |
hostName / hostId / hostIsSuperhost / hostRating / hostReviewsCount | "Example Host" / "700000001" / true / 4.9 / 57 |
hostResponseRate / hostResponseTime | "100%" / "within an hour" |
reviews[] | reviewId, createdAt, rating, comment, reviewerName, hostResponse (when reviews are read) |
reviewsFetched / reviewsAverageOfFetched | 30 / 4.87 |
detailLoaded | true |
changeType / changedFields / firstSeenAt / lastSeenAt | "UPDATED" / ["pricePerNight"] / timestamps (incremental mode only) |
How to Use
- Pick a mode:
search(destination, dates, guests and filters) orurl(paste listing and search links, used as given). - Fill in your destinations or links, stay dates and guest counts, and turn on full detail or the review history if you need them.
- Set Max items to control run size and cost, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Search a destination with filters:
{"mode": "search","searchQueries": ["Lisbon, Portugal"],"checkIn": "2026-11-10","checkOut": "2026-11-13","adults": 2,"roomTypes": ["Entire home/apt"],"superhostOnly": true,"maxItems": 50}
Full detail and reviews for a pasted listing link:
{"mode": "url","listingUrls": ["https://www.airbnb.com/rooms/900000000000000001"],"fetchDetails": true,"includeReviews": true,"maxReviewsPerListing": 100}
A pasted search link, used verbatim:
{"mode": "url","listingUrls": ["https://www.airbnb.com/s/Kyoto--Japan/homes?adults=2"]}
Recurring monitoring with incremental mode:
{"mode": "search","searchQueries": ["Austin, Texas"],"checkIn": "2026-12-01","checkOut": "2026-12-05","incrementalMode": true,"emitExpired": true,"maxItems": 0}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/airbnb-stays-reviews-scraper").call(run_input={"mode": "search", "searchQueries": ["Lisbon, Portugal"], "maxItems": 50})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["pricePerNight"], listing["rating"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/airbnb-stays-reviews-scraper').call({mode: 'search', searchQueries: ['Lisbon, Portugal'], maxItems: 50,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Search filters and listing links
Destination search filters: minimum and maximum price per night, stay type (entire home or apartment, private room, shared room, hotel room), minimum bedrooms, beds and bathrooms, Superhost only, Instant Book only, Guest favourites only. With several destinations, each gets an equal share of Max items.
A pasted listing link (https://www.airbnb.com/rooms/12345678, or just the id) is read in full: description, amenities, house rules, host and rating breakdown, plus reviews when you turn them on. Because it is always read in full, each successfully read listing link carries one Listing enrichment charge, even with Read full detail off. A pasted search link is used verbatim: the search filter fields above are deliberately not merged into it, and a link that already points at a later stretch of results continues from there. Both shapes can be mixed in one run.
Full detail and the review history
Turn on Read full detail for every listing to add description, amenities, house rules, host stats and the full rating breakdown. Turn on Read the review history to add reviews up to Max reviews per listing (default 20, set 0 to skip); reviewsCount always reports the listing's own total even when the cap collected fewer, and reviewsFetched tells you how many you actually got. Both share a single Listing enrichment event per listing, so a listing with 300 reviews costs the same as one with 3, and a listing whose read fails still ships the base search record, uncharged.
How Max items shapes a run
Max items stops a run after that many listing records (0 means no limit; the run then stops when every destination runs out on its own). A destination search walks result pages forward, past the site's own 270-result pager, and stops when a page returns nothing new.
In incremental mode, a listing that comes back UNCHANGED does not spend the item budget: the walk keeps reading further pages looking for new or changed rows. With a finite Max items, though, each destination stops reading after a scan ceiling of 10 times its share of Max items, and never less than 200 results. That ceiling also counts listings a resumed run skips.
Known limit: anything past that ceiling is never reached. For example, with Max items at 5 and 216 unchanged listings ranked ahead of a new one, the run reads 200 results, returns nothing, and still succeeds. The same happens when a resumed run's earlier results fill the ceiling before it reaches anything new. To monitor a large destination completely, or to resume a pull much bigger than the new Max items, set Max items to 0.
Known limit: Emit expired records is not capped by Max items. When a run completely scans a tracked search, every previously tracked listing that has genuinely disappeared is returned and billed as EXPIRED in that same run, whatever Max items is set to. In one reproduction, a 1,000-listing baseline that shrank to 80 results under Max items 100 returned 920 EXPIRED rows. If you monitor a destination you expect to empty out (for example a seasonal search going quiet), expect that run to cost more than Max items alone suggests.
Known limit: with a finite Max items, a run only counts as a complete scan when it reads fewer results than Max items. A run that reads the whole search but at least Max items results never reports EXPIRED. Put together with the limit above, a finite Max items reports EXPIRED only once a search has shrunk below Max items, and then all at once. For steady EXPIRED tracking, set Max items to 0.
Resume and recurring updates
-
Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips every listing already collected there, so you don't pay twice. -
Incremental mode (
incrementalMode) is for scheduled runs over the same search. Each listing is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unless Emit unchanged is on),REAPPEAREDorEXPIRED(only after a run that scanned the whole tracked search, and only with Emit expired).stateKeynames or shares the stored state; without it, state is kept separately for every destination, filter and lookup setup. -
Without pinned stay dates, the site quotes its own rolling window, so the stay dates, length and price move on their own and are left out of the change comparison. Pin Check-in and Check-out when you want to monitor price.
-
Known limit: resume and incremental mode do not fully combine yet. Listings a resumed run skips (because a previous run already collected them) are excluded from the dataset, correctly avoiding a duplicate row, but they are also left out of the incremental baseline that run saves. A later scheduled run over the same search sees those listings for the first time and returns (and bills) them again as
NEW. If they disappear before then, they are never reported asEXPIRED, because they were never tracked. A resumed run never reportsEXPIREDitself. Workaround: let one run finish a full scan withoutresumeFromRunIdbefore relying on incremental mode for that search, so the baseline is built from a run that saw every listing itself. -
Known limit: a destination stops early at a page made up entirely of listings this run has already returned or skipped, as long as that destination has already returned at least one new listing. Pages like that at the start of a walk are read past correctly. This guard stops a search whose pages start repeating, but it also cuts two real cases short:
- A resumed run whose results have been reordered since the original run. In one reproduction, the resumed run found 1 new listing on page 1, then hit a page of 18 already-collected listings. It stopped there and never reached the 36 new listings behind it.
- Two overlapping destinations in one run, such as
ParisandParis, France. A later destination stops at the first page made up entirely of the earlier destination's listings.
These runs still succeed and never report
EXPIREDfor listings they did not reach. To lower the risk, avoid overlapping destinations in one run, and resume soon after the interruption.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify, Settings, API & Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where listing pages should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (destination search with the fields below) or url (paste listing and search links, read verbatim). |
searchQueries | array | ["Paris, France"] | Search mode: destinations written the way you would type them into the site, for example Paris, France or Kyoto, Japan. |
listingUrls | array | sample rooms link | URL mode: listing links, bare listing ids, or search links, read in order. |
urls | array | (none) | Alias for Listing and search links, accepted under the name other Apify actors use for URL mode. |
checkIn | string | (none) | Stay start date as YYYY-MM-DD. Applies in both modes; pin it (with Check-out) to monitor price. |
checkOut | string | (none) | Stay end date as YYYY-MM-DD. Ignored unless Check-in is also set. |
adults | integer | 1 | Number of adults the stay is priced for (1 to 16). |
children | integer | 0 | Number of children, ages 2 to 12 (0 to 15). |
infants | integer | 0 | Number of infants under 2 (0 to 5). |
pets | integer | 0 | Number of pets; narrows a search to stays that accept them (0 to 5). |
currency | string | USD | Three-letter currency code prices are quoted in, for example USD, EUR, GBP, AUD, JPY, BRL. |
priceMin | integer | (none) | Search mode: lowest nightly price, in the currency above. |
priceMax | integer | (none) | Search mode: highest nightly price, in the currency above. |
roomTypes | array | (none, all types) | Search mode: keep only these stay types (Entire home/apt, Private room, Shared room, Hotel room). |
minBedrooms | integer | (none) | Search mode: keep only stays with at least this many bedrooms (0 to 8). |
minBeds | integer | (none) | Search mode: keep only stays with at least this many beds (0 to 8). |
minBathrooms | integer | (none) | Search mode: keep only stays with at least this many bathrooms (0 to 8). |
superhostOnly | boolean | false | Search mode: keep only stays whose host carries the Superhost badge. |
instantBookOnly | boolean | false | Search mode: keep only stays bookable without waiting for host approval. |
guestFavoriteOnly | boolean | false | Search mode: keep only stays carrying the Guest favourite badge. |
maxItems | integer | 20 | Stop after this many listing records (0 = no limit; the run then stops when every search runs out). |
fetchDetails | boolean | false | Add description, amenities, house rules, host stats and the full rating breakdown per listing; one Listing enrichment event each. |
includeReviews | boolean | false | Add the review history to every record, up to Max reviews per listing. |
maxReviewsPerListing | integer | 20 | Cap on reviews collected per listing when Read the review history is on (read 24 per page); 0 skips reviews. |
resumeFromRunId | string | (none) | Previous run ID or dataset ID to continue a large pull without returning listings already collected there. |
incrementalMode | boolean | false | Recurring monitoring: the first run marks every listing NEW; later runs return only NEW, UPDATED and REAPPEARED. |
stateKey | string | (none) | Optional name for an incremental-mode monitoring campaign; auto-derived from the search setup when empty. |
emitUnchanged | boolean | false | Incremental mode only: also return (and bill for) listings unchanged since the last run. |
emitExpired | boolean | false | Incremental mode only: also return (and bill for) listings no longer found, once a run fully scans the tracked search. |
mcpConnectors | array | (none) | Pipe results into apps you already use over Model Context Protocol; never changes the dataset output. |
notionParentPageUrl | string | (none) | URL or id of the Notion page under which listing pages are created (Notion connector only). |
maxNotifyListings | integer | 50 | Cap on listings written to each connector per run (1 to 1000); does not affect the dataset. |
proxyConfiguration | object | Apify Proxy on | Connection settings; the defaults are enough, on every Apify plan. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"kind": "listing","listingId": "900000000000000001","url": "https://www.airbnb.com/rooms/900000000000000001","title": "Example stay in Sample District","subtitle": "Hosted by Example Host","categoryLabel": "Entire home/apt","stayCategory": "Entire home/apt","roomType": "Entire home/apt","propertyType": "Entire rental unit","pricePerNight": 180.0,"pricePerNightListed": 210.0,"priceTotal": 540.0,"priceCurrency": "EUR","priceCurrencyRequested": "EUR","nights": 3,"checkIn": "2026-11-10","checkOut": "2026-11-13","adults": 2,"rating": 4.9,"reviewsCount": 57,"ratingCleanliness": 4.95,"ratingAccuracy": 4.9,"ratingValue": 4.8,"isGuestFavorite": true,"isSuperhost": true,"personCapacity": 4,"bedrooms": 2,"beds": 3,"bathrooms": 1.0,"latitude": 41.15,"longitude": -8.61,"city": "Sample City","country": "Sample Country","amenitiesCount": 38,"hostName": "Example Host","hostId": "700000001","hostIsSuperhost": true,"hostRating": 4.9,"hostReviewsCount": 57,"hostResponseRate": "100%","hostResponseTime": "within an hour","reviews": [{"reviewId": "900000000000000101","createdAt": "2026-08-01T00:00:00Z","rating": 5,"comment": "Example review text describing the stay.","reviewerName": "Example Guest","hostResponse": "Example host reply, thank you for staying!"}],"reviewsFetched": 30,"reviewsAverageOfFetched": 4.87,"detailLoaded": true}
Plan Requirement
The default proxy setting works out of the box, and the actor rotates connections automatically when one is refused. For large or frequent runs, the residential proxy group gives more headroom; pick it under Connection.
FAQ
How much does it cost?
You pay per listing record returned, plus one Listing enrichment charge for each listing whose full detail or review history was read (never per review page, never per retry, and never charged when the read failed). The Pricing tab shows the current rates. Use Max items to cap the cost of a run, and see "How Max items shapes a run" above for the one case (Emit expired) where a run's cost is not fully bounded by it.
Is it legal to scrape Airbnb?
This actor collects only publicly available stay listings. You are responsible for how you use it: follow Airbnb's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Host and reviewer names and photos may be subject to third-party privacy rights; treat that data accordingly.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared listings, and unchanged ones are not billed unless you turn on Emit unchanged. Set Max items to 0 so every run checks the whole search. With a finite limit, each run only checks a leading slice of results (see "How Max items shapes a run").
Why does a search filter return listings that do not seem to match?
stayCategory is the category line the search result itself carries (for example "Room in Sample City" or "Hotel in Sample District"). It overlaps the Stay types filter rather than matching it one to one: in the site's own taxonomy a hotel room is also a private room, so a Private room search can legitimately return rows whose stayCategory is Hotel room. roomType is the value the listing page itself publishes and only appears once full detail is read. Also, the price filter narrows on the nightly rate after any discount, the same quantity reported as pricePerNight, so a discounted listing can sit inside a price cap even though its listed rate looks higher.
Why did my run fail instead of returning an empty dataset?
If the site refuses every request, the run stops with a clear connection message so "no stays for this search" is never confused with "nothing could be read". Run it again in a few minutes. If only some listings or pages could not be read, you still get what was found.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
๐ Want more travel data?
Pair this actor with these related scrapers from the same team:
| ๐จ Hipcamp Scraper Scrape Hipcamp campgrounds, RV parks, glamping and unique stays by US or AU region or... | ๐จ Booking.com Hotels, Prices and Reviews Scraper Scrape booking.com hotels, apartments, villas and hostels. Search any destination with... |
| ๐จ Expedia Hotels Scraper From $1/1K. Scrape Expedia.com hotel listings and reviews for any destination. Extract... | ๐จ Vrbo Scraper Extract vacation rental data from Vrbo.com by location, region, coordinates, property... |
| ๐จ Yanolja Scraper Scrape Yanolja (NOL), Korea's largest accommodations marketplace, by region or URL. Every... | ๐จ Rover Pet Care Scraper Scrape Rover.com pet-care providers: sitter search by city, rates per stay type, ratings... |
๐ Browse all abotapi scrapers
๐ฌ Support & custom scrapers
- ๐ Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- ๐ ๏ธ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
- โญ Enjoying it? A quick review on the actor page helps other users find it.