Airbnb Scraper: Listings, Prices & Availability by Location
Pricing
Pay per event
Airbnb Scraper: Listings, Prices & Availability by Location
Search Airbnb by city or pasted search URL and export listings with prices, ratings, rooms and coordinates. Add dates for exact stay prices and availability, or pass listing URLs for full details: host, amenities, rules. Public logged-out data, no login.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Changefeeds Tools
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
By Changefeeds Tools. An Airbnb scraper that searches by location, prices by date and reports availability. Search a city or paste an airbnb.com search URL to export listings with prices, ratings, room counts, coordinates and badges. Add check-in and check-out dates for real stay prices, or pass specific listing URLs for full details: description, amenities, house rules, host and sub-ratings, plus price and availability for your dates. It reads the same public pages a logged-out browser on airbnb.com gets. No Airbnb account, cookie, or login.
Who it is for: short-term-rental analysts comparing a market, hosts watching competitors' prices, travel and relocation tools, and anyone who needs a spreadsheet of what Airbnb shows for a place and a date range.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
locations | list of strings | A place name per line ("Lisbon, Portugal"), or a full airbnb.com/s/... search URL copied from your browser. A pasted URL is used as-is, with all its filters (room type, amenities, map area). | |
listingUrls | list of strings | airbnb.com/rooms/<id> URLs (any Airbnb country domain) or bare numeric ids. One detail row each. | |
checkIn / checkOut | YYYY-MM-DD | none | Optional; give both or neither. With dates, search prices are for your stay and listing URLs also get a price and availability. |
adults | integer 1-16 | 2 | Guests. Changes which listings show and their prices. |
currency | 3-letter code | USD | EUR, GBP, JPY, ... A pasted search URL that already has currency= keeps its own. |
minPrice / maxPrice | integer | none | Airbnb's own per-night price filter, in currency. Searches only. |
maxItems | integer 1-270 | 100 | Max listings per location. |
includeDetails | boolean | false | Also open each search result's listing page and add the detail fields (one extra page per listing; see Pricing). |
proxy | proxy settings | Apify Proxy, residential | Airbnb blocks many datacenter IPs, so requests go through residential proxy by default. |
Search a city, cheapest way:
{ "locations": ["Lisbon, Portugal"], "maxItems": 20 }
Priced for your dates, with details:
{ "locations": ["Porto, Portugal"], "checkIn": "2026-11-20", "checkOut": "2026-11-23", "currency": "EUR", "minPrice": 60, "maxPrice": 150, "includeDetails": true, "maxItems": 50 }
Specific listings, with availability:
{ "listingUrls": ["https://www.airbnb.com/rooms/12537150", "1144816383633864634"], "checkIn": "2026-11-28", "checkOut": "2026-12-03" }
Sample output
A search row (live run, 2026-09-30, no dates given; images shortened):
{"type": "listing","id": "12537150","url": "https://www.airbnb.com/rooms/12537150?check_in=2026-11-28&check_out=2026-12-03&adults=2","name": "Deluxe apartment in Chiado","title": "Apartment in Santa Maria Maior","subtitle": "Álvaro Siza Vieira building in Chiado, with a modern interior and A/C","roomType": null,"propertyType": "Apartment","area": "Santa Maria Maior","city": null,"lat": 38.7118,"lng": -9.13868,"rating": 4.95,"reviewsCount": 625,"isNew": false,"isSuperhost": null,"guestFavorite": true,"price": {"amount": 795,"currency": "USD","qualifier": "total","nights": 5,"originalAmount": null,"label": "$795 for 5 nights","checkIn": "2026-11-28","checkOut": "2026-12-03","datesChosenByAirbnb": true,"breakdown": [{ "description": "5 nights x $158.95", "amount": 794.73, "text": "$794.73" }],"notes": []},"available": null,"unavailableReason": null,"priceError": null,"images": ["https://a0.muscache.com/im/pictures/airflow/Hosting-12537150/original/2121117c-5284-43d2-8bdb-dc3723827676.jpg"],"badges": ["Guest favorite"],"paymentMessages": ["Free cancellation"],"bedrooms": 1,"beds": 2,"bathrooms": 1,"guests": null,"propertyId": null,"searchInput": "Lisbon, Portugal","position": 1,"hasDetails": false,"detailsError": null,"description": null,"amenities": null,"houseRules": null,"highlights": null,"host": null,"subRatings": null,"scrapedAt": "2026-09-30T05:37:35.346Z"}
With includeDetails on, or for any listingUrls row, the same row also
carries the listing page's fields (live run, shortened):
{"roomType": "Entire home/apt","propertyType": "Entire rental unit","city": "Porto","isSuperhost": true,"guests": 2,"hasDetails": true,"description": "Beautiful studio apartment set in a fully renovated 18th-century building, with air conditioning for heating and cooling…","amenities": ["River view", "Garden view", "Hair dryer", "Shampoo", "…38 in total"],"houseRules": ["Check-in: 3:00 PM - 12:00 AM", "Checkout before 11:00 AM", "Self check-in with keypad", "2 guests maximum", "No pets"],"highlights": ["Top 10% of homes", "Self check-in", "Vibrant neighborhood"],"host": { "id": "245989121", "name": "Isabel & Igor", "isSuperhost": true, "isVerified": true, "rating": 4.9, "reviewsCount": 4616, "yearsHosting": 7, "responseRate": "Response rate: 100%", "responseTime": "Responds within an hour" },"subRatings": { "accuracy": 4.93, "checkIn": 4.97, "cleanliness": 4.91, "communication": 4.98, "location": 4.95, "value": 4.89 }}
A listing URL with dates it is booked for comes back as
"available": false, "unavailableReason": "Those dates are not available", "price": null.
With dates it is free for, price holds the booking-box total, e.g.
"$795 for 5 nights", with "available": true.
A failed input gets a free status row instead:
{ "type": "status", "input": "https://www.airbnb.com/rooms/1", "source": "listingUrls", "status": "not_found", "error": "Listing 1 not found (removed, unlisted, or never existed).", "checkedAt": "2026-09-30T05:37:53.680Z" }
status is one of:
not_found: the listing is gone, or Airbnb did not recognise the place name (it fell back to a generic map area).invalid: the entry is not a place, search URL, listing URL or id (a/rooms/URL put inlocationsis pointed atlistingUrls), or a duplicate listing.blocked: Airbnb's bot protection refused the request from every proxy IP tried.error: any other HTTP or network failure.
The key-value store record OUTPUT holds a run summary: counts by type,
charged_events, stopped_reason, truncated_inputs, and per location the
search URL used, Airbnb's own heading ("Over 1,000 homes in Lisbon"), pages
fetched and why it stopped. Every run leaves at least one dataset row, so a
quiet run is never mistaken for a broken one.
Pricing
Pay per event, nothing else:
listing-returned: $0.003 per listing row ($3 per 1,000 listings).details-added: $0.002 extra per listing with details (search results withincludeDetailson, and everylistingUrlsrow, since those always come from the listing page). A search row whose listing page could not be read is delivered withdetailsErrorand charged as a plain listing.
So a plain search costs $3 per 1,000 listings, and a search with details or a
list of listing URLs costs $5 per 1,000. Proxy traffic is included in the
price. Failed inputs (not_found, invalid, blocked, error) are never
charged. If you set a maximum total charge for the run, the actor checks the
remaining budget before fetching each page, stops cleanly once it is
reached, and says so in OUTPUT.stopped_reason
("max_total_charge_reached").
Limits, stated plainly
- Airbnb shows at most 15 pages of 18 results per search, and the pages
overlap. A live 15-page run for "Lisbon, Portugal" returned 242 unique
listings, although Airbnb says "Over 1,000 homes". When a search hits that
wall the actor marks it
truncatedand lists it inOUTPUT.truncated_inputs. To get more, split the search: run the same place in price bands (minPrice/maxPrice, e.g. 0-80, 80-150, 150-300, 300+), by neighbourhood, or paste smaller map-area search URLs.maxItemsis capped at 270 for this reason. - Without dates, the prices are for dates Airbnb picked. Airbnb then
prices each result for its own sample stay (often 5 nights, different per
listing). The row says so:
price.datesChosenByAirbnb: true, with those dates inprice.checkIn/price.checkOut. Give dates for comparable prices. - Prices are what the card or booking box shows, in the display format
Airbnb chooses for your currency and region. Depending on the region that
may be before taxes.
price.breakdownandprice.notescarry Airbnb's own lines. Hotels can show "Price may vary by room type". isSuperhostisnullon plain search rows unless the card shows a Superhost badge; search cards do not say when a host is not one. The listing page (includeDetails) gives the real value.- Listing URLs get a price only with dates. The listing page has no price
on its own. With dates, the actor makes one more request for the listing's
booking box. If that call fails, the row is still delivered with
priceError. - Search results include what Airbnb puts in them, such as "Featured
hotel" placements (
badges,propertyIdset). - Not supported: vanity
/h/...links (use the/rooms/<id>URL), experiences, reviews text, calendars beyond the dates you give, and anything that needs a login. The actor never solves CAPTCHAs and never uses an account. If Airbnb challenges a request, it retries from a new proxy IP (up to 8 times), then reports a freeblockedrow. - It stays polite: search pages one at a time, at most 2 listing pages in flight, at least 500 ms between requests, 20 s timeouts, and HTTP 429/5xx retried with backoff (up to 3 retries, Retry-After honoured and capped at 60 s).
- Airbnb changes its site. The actor reads the data embedded in Airbnb's
own pages, and it finds the booking-box query id in Airbnb's current
JavaScript at run time rather than hard-coding it. A layout change can
still break a field until the actor is updated; a page without the
expected data becomes an
errorrow, never a silently empty result.
FAQ
Do I need an Airbnb account? No. Only public, logged-out pages.
Can I use my own search with filters? Yes. Set up the search on
airbnb.com (any country domain), copy the URL from the address bar and put
it in locations. Its filters are kept. Your checkIn, checkOut,
adults, currency and price fields are only added where the URL does not
already set them.
Why fewer listings than "Over 1,000 homes"? See the 15-page limit above: split the search into price bands.
Why is price null? On a listing URL without dates, Airbnb shows no
price. On one with dates, the listing may be unavailable for them (see
available and unavailableReason), or the booking-box call failed
(priceError).
Local development
pnpm --filter @mmnm/airbnb test # unit tests on saved Airbnb responses, no networkpnpm --filter @mmnm/airbnb build
node src/main.ts runs the actor locally with Apify's local storage
(./storage, input in storage/key_value_stores/default/INPUT.json);
leave proxy out of the input to fetch directly from your own connection.