kestrel actors docs

kestrel actors › Guest & customer reviews

Airbnb Reviews Scraper — Guest Reviews, Ratings & Replies

An airbnb reviews scraper that returns every guest review a listing has, as structured rows: airbnb ratings, the review exactly as the guest wrote it, Airbnb's translation, the language it was written in, the reviewer, the date, the stay type and the host's public reply. It is a scrape airbnb reviews tool for hosts and analysts who want airbnb review data in bulk, not one page at a time.

Run Airbnb Reviews Scraper on Apify →

Airbnb reviews scraper pricing: what one row costs

EventWhat it isPriceCharged for
reviewReview row$0.005One guest review: rating, text, original language and translation, reviewer, date, stay type and the host's reply. Listings with no reviews are free.

Pay per event, one event per delivered review row. Free: listing rows, status rows, reviews removed by minRating, listings with no reviews, unrecognised places, and failed jobs. A listing is harvested once per run however many inputs point at it.

A listing with 441 reviews is 441 rows if you ask for all of them — maxReviewsPerListing and minRating are there to make that a deliberate choice. Verified runs behind this listing cost $0.0002–$0.0003 each in platform compute. Exact per-row pricing is on this page's pricing tab.

Airbnb reviews scraper input reference

Every field of Airbnb Reviews, straight from the actor's input schema.

FieldTypeDefaultWhat it does
startUrlsarray[]Listing URLs (https://www.airbnb.com/rooms/12345678) to take reviews from, or search URLs (https://www.airbnb.com/s/Lisbon--Portugal/homes) to take reviews from every listing they return.
listingIdsarray[]Numeric Airbnb listing ids, e.g. 11562247 — the fastest input when you already know the listings.
locationQueriesarray[]Places typed as you would on Airbnb: "Lisbon, Portugal", "Algarve". Every listing found is harvested for reviews. An unrecognised place costs nothing instead of returning listings from all over the world.
maxListingsPerQueryinteger20How many listings to take from each place search before harvesting their reviews. Ignored for direct listing inputs. Range 1-280.
maxReviewsPerListinginteger1000 = every review the listing has (Airbnb pages them 50 at a time). N = the first N in the order below. This is the main cost control. Range 0-5000.
reviewsSortstring (enum)"most_recent"Which reviews come first. "Lowest rated" is the fastest way to find complaints; "Most recent" is the one to schedule. One of: most_recent, highest_rated, lowest_rated, most_relevant.
minRatinginteger00 = keep every review. 1-5 = keep only reviews rated at or below this, so a complaint feed costs nothing for the happy ones. Filtered reviews are not charged. Range 0-5.
localestring"en"Language for Airbnb's translations, e.g. en, es, fr, de. Every review also carries its original text and the language it was written in.
includeListingRowbooleantrueAlso emit one row per listing with its title, rating, review count and host — useful context alongside the reviews. Never charged.
sessionsinteger4How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16.
perIpnumber1Pace for each session. Airbnb tolerates about 1/s per IP. Range 0.1-3.
proxyConfigurationobject{"useApifyProxy": true}Apify Proxy is required: Airbnb blocks datacentre traffic that is not rotated. The default group works.

Example input

{ "listingIds": ["11562247", "45307024"], "maxReviewsPerListing": 0, "reviewsSort": "lowest_rated", "minRating": 3 }
{ "locationQueries": ["Lisbon, Portugal"], "maxListingsPerQuery": 50, "maxReviewsPerListing": 100 }

Airbnb reviews scraper output fields

Three row types share one dataset: review (one guest review, charged), listing (context for a listing, free) and status (per listing or place, free).

Dataset views in the Apify Console: Overview, Reviews, Listings, Run status.

FieldTypeWhat it holds
typestringRow type: review, listing or status.
idstringAirbnb listing id the row belongs to.
urlstringListing URL.
namestringListing title, when known.
reviews_totalintegerHow many reviews Airbnb reports for the listing.
reviews_fetchedintegerListing rows: how many reviews were fetched for it.
localestringLanguage requested for translations.
review_idstringAirbnb's id for the review.
ratingstring | integerStar rating the guest gave, 1-5.
textstringThe review as written, in its original language.
text_localizedstringAirbnb's translation into the requested language, when it differs.
languagestringLanguage the review was written in.
created_atstringWhen the review was posted (UTC).
date_labelstringDate as Airbnb shows it, e.g. "1 week ago".
reviewer_namestringReviewer's first name.
reviewer_idstringReviewer's Airbnb id.
reviewer_locationstringWhere the reviewer says they are from.
reviewer_is_superhostbooleanWhether the reviewer is themselves a Superhost.
host_replystringThe host's public reply, when there is one.
host_reply_datestringWhen the host replied.
highlightstringThe sentence Airbnb highlights from the review.
stay_typestringAirbnb's tag for the stay, e.g. a group or family trip.
statusstringStatus rows: ok, unknown_area, no_results or error.
listingsintegerStatus rows: listings harvested.
reviewsintegerStatus rows: review rows delivered.
filteredintegerStatus rows: reviews dropped by the rating filter, never charged.
duplicatesintegerStatus rows: listings already harvested by another input.
listing_idstringStatus rows: the listing this job was for.
querystringStatus rows: the place searched.
errorstringStatus rows: why the job failed, null when it did not.
fetched_atstringUTC timestamp when the row was fetched.

Example row from a real run

Taken from the actor's README — this is the shape your dataset holds.

{
  "type": "review", "id": "11562247", "url": "https://www.airbnb.com/rooms/11562247",
  "name": "Charming studio in Baixa", "reviews_total": 441, "locale": "en",
  "review_id": "1752111840493470784", "rating": "5",
  "text": "El departamento es muy lindo. Esta todo muy bien ubicado.",
  "text_localized": "The apartment is very nice. Everything is very well located.",
  "language": "es", "created_at": "2026-08-14T10:53:27Z", "date_label": "1 week ago",
  "reviewer_name": "Luis Armando", "reviewer_id": "313623872",
  "reviewer_location": "Buenos Aires, Argentina", "reviewer_is_superhost": false,
  "host_reply": null, "host_reply_date": null, "highlight": null, "stay_type": null
}
{ "type": "listing", "id": "11562247", "url": "https://www.airbnb.com/rooms/11562247", "name": "Charming studio in Baixa", "reviews_total": 441, "reviews_fetched": 100, "locale": "en" }

Airbnb reviews scraper in Python, JavaScript and curl

Python

from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/airbnb-reviews-scraper").call(run_input={
    "listingIds": ["11562247"], "maxReviewsPerListing": 0, "reviewsSort": "lowest_rated", "minRating": 3,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "review":
        print(row["rating"], row["language"], row["text"][:80])

JavaScript (Node)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/airbnb-reviews-scraper').call({
    locationQueries: ['Lisbon, Portugal'], maxListingsPerQuery: 20, maxReviewsPerListing: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'review').length, 'reviews');

curl

curl -X POST "https://api.apify.com/v2/acts/kestrel~airbnb-reviews-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{"listingIds":["11562247"],"maxReviewsPerListing":50}'

The same three calls are runnable files in the examples repository: examples/airbnb-reviews-scraper.py|.js|.sh.

Limits and honest notes

  • rating is a string as Airbnb returns it ("5"), not a number. Cast it if you are aggregating.
  • reviewer_location is self-declared and often blank — treat it as a hint, not a fact.
  • Translations are Airbnb's, not ours. text is always the original; text_localized is only present when Airbnb translated it.
  • An unrecognised place is reported, not guessed. Airbnb answers a nonsense place with listings from all over the world; the run detects that, marks the job unknown_area and charges nothing.
  • Airbnb pages reviews 50 at a time. Deep listings take several requests; maxReviewsPerListing bounds it.
  • No host contact details. Not scraped, not exposed, not an input — by design.

FAQ

Does it need an Airbnb API key, login or cookies?

No. There is no public airbnb review api to hold a key for. This uses the same public GraphQL endpoint airbnb.com's own front-end calls, with no session, and it refreshes Airbnb's query hashes automatically if they change.

Can I download Airbnb reviews as CSV or Excel?

Yes. Every run writes an Apify dataset, which exports to CSV, Excel, JSON or XML from the Console, or over the API with ?format=csv. That is the short path to download airbnb reviews for a listing or a market.

How do I get only Airbnb negative reviews?

Set minRating to the ceiling you care about — 2 for one- and two-star, 3 to include mediocre stays — and reviewsSort to lowest_rated. Reviews above the ceiling are dropped before billing, so a complaints feed on a heavily reviewed listing costs cents.

Can I get reviews for every listing in a city?

Yes, with locationQueries plus maxListingsPerQuery. Airbnb caps a single search at roughly 280 listings, so for a large city, run several searches with different filters (price bands, room types) and let the de-duplication merge them.

Are reviews translated?

Each review carries language (what it was written in) and text (the original). When Airbnb offers a translation into your locale, it arrives as text_localized. So airbnb review translation is available without you calling a translation API, and the source text is never lost.

Does it include host replies?

Yes — host_reply and host_reply_date when the host answered publicly. That is how you find listings where negative reviews were left unaddressed.

How many reviews can one listing return?

All of them. Airbnb pages 50 at a time and the run follows the pages until it has what you asked for; 300 unique reviews from a single listing was verified in testing. reviews_total tells you how many exist, so you can check you got the lot.

Can I scrape Airbnb reviews without an API?

That is exactly what this is — airbnb reviews without api access, no key to apply for and no approval process. You call an Apify actor and read rows back.

What does a run cost for bulk work?

Bulk airbnb reviews are billed per delivered row, so cost scales with data rather than with time. Filtering with minRating or capping with maxReviewsPerListing cuts it directly — those rows are never charged.

Which languages and locales are supported?

Any locale Airbnb supports for translations — en, es, fr, de, pt, it, ja, ko and the rest. Set locale and text_localized follows it. The language field is independent: it tells you what the guest actually wrote in.

How to use Airbnb Reviews Scraper

Ready-made runs you can open and start

Each one is a saved input on the Apify Store — open it, press Start, and it runs with the fields already filled in. All 163 across the suite.

Ready-made runWhat it does
Airbnb Negative Reviews Across a MarketLowest-rated first, three stars and below only: the complaints from ten Lisbon listings, with the happy reviews filtered out before billing.
Airbnb Sentiment Analysis Dataset (CSV)Five hundred recent Barcelona guest reviews with rating, original text, language, date and host reply - a labelled corpus, exportable as CSV.
Bulk Airbnb Reviews by Listing IDThree known listing ids, a hundred reviews each, no search step: the fastest way to refresh reviews for properties you already track.
Scrape Airbnb Reviews from a Search URLPaste an Airbnb search URL and every listing behind it is harvested for reviews - the filters you set in the UI decide the sample.
Airbnb Host Reviews, Highest Rated FirstPull the five-star guest reviews for your own listings first - the quotes that belong on a direct booking site, with reviewer name and date.
Airbnb Review Translation into SpanishEvery Madrid review in Spanish plus its original text and detected language, so a mixed-language market reads as one column.
Download Airbnb Reviews for One ListingThe full review history of a single listing, up to a thousand reviews in one run - due diligence before buying or taking over a property.
Review Monitoring for Your Own ListingsTen newest reviews for each of five listings, cheap enough to run every morning - a guest feedback feed you can wire to Slack or email.
Airbnb Reviews for a City, by ListingA census of an Algarve market: every listing with its total review count and how many were fetched, so you can rank properties by traction.
Airbnb Competitor Reviews for a Comp SetTwenty Miami Beach competitors, forty reviews each: eight hundred rows of guest feedback about the properties you compete with, not your own.

Run Airbnb Reviews Scraper

The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/airbnb-reviews-scraper