Airbnb search results tell you the price, the rating and the size of a listing. They do not tell you whether it has a dedicated workspace, whether pets are allowed, what the check-in time is, how the cleanliness rating compares with the location rating, or which nights are free in the next three months. All of that lives on the listing page.
This is a two-step pipeline with two Apify Actors that share their output schema: the Airbnb Listings Scraper for the search, the Airbnb Listing Details Scraper for the pages. The rows join on platformListingId. Neither collects anything about hosts or guests.
Who needs the second step
- Remote-work and relocation searches: filter on amenities (workspace, fast wifi, washer), quiet hours and minimum stay.
- Family or accessibility needs: crib, high chair, step-free access, maximum guests, pets.
- Hosts benchmarking their listing: the six category ratings of comparable listings (cleanliness, accuracy, check-in, communication, location, value) say where the gap is.
- Occupancy estimates: the availability calendar for one to twelve months gives the available nights and minimum stays of competing listings.
Step 1: the search
Run the search Actor with dates, so that prices describe the same stay. The input below is the public task "Entire homes on Airbnb in Lisbon", plus dates:
{
"location": "Lisbon, Portugal",
"checkIn": "2026-12-01",
"checkOut": "2026-12-03",
"adults": 2,
"roomTypes": ["entire_home"],
"currency": "EUR",
"maxResults": 50
}
Each row has a url column that opens the listing with those dates. Export the dataset, or read it from the Apify API, and keep the url (or platformListingId) column: that is the input of step 2.
Step 2: the listing pages
The details Actor takes listing URLs or bare numeric ids. This input is the Actor's validation input, with the same dates as the search and a three-month calendar:
{
"listingUrls": [
"https://www.airbnb.com/rooms/21607939",
"https://www.airbnb.com/rooms/29990558",
"https://www.airbnb.com/rooms/13376882",
"1479864404418316543",
"674363704654416995"
],
"checkIn": "2026-12-01",
"checkOut": "2026-12-03",
"adults": 2,
"includeCalendar": true,
"calendarMonths": 3,
"currency": "EUR",
"locale": "en",
"domain": "www.airbnb.com",
"maxResults": 5
}
The fields not shown keep the Actor's defaults. In Apify, the simplest way to chain the two is to paste the url column into listingUrls; the more robust way is a small script that reads the first run's dataset through the API and starts the second Actor with those URLs. Duplicated URLs are fetched once.
What a detail row contains
Here is a real row from a Lisbon listing (English, EUR, two-month calendar), cut down to what the search did not have:
{
"platformListingId": "21607939",
"title": "Red Bridge Duplex close to LX Factory",
"propertyType": "Entire rental unit",
"neighborhood": "Alcântara, Lisbon",
"rating": 4.96,
"reviewCount": 658,
"ratingCleanliness": 4.96,
"ratingAccuracy": 4.97,
"ratingCheckIn": 4.99,
"ratingCommunication": 5,
"ratingLocation": 4.78,
"ratingValue": 4.93,
"isGuestFavorite": true,
"highlights": ["Top 10% of homes", "Self check-in", "Dedicated workspace"],
"maxGuests": 5,
"bedrooms": 2,
"beds": 3,
"bathrooms": 2,
"sleepingArrangements": ["Bedroom 1: 1 double bed", "Bedroom 2: 1 double bed", "Living room: 1 sofa bed"],
"amenityGroups": {
"Internet and office": ["Wifi", "Dedicated workspace"],
"Family": ["Paid pack ’n play/travel crib - available upon request", "Standalone high chair - always at the listing", "Children’s dinnerware"],
"Parking and facilities": ["Free street parking", "Paid parking lot off premises", "Paid parking on premises"]
},
"unavailableAmenities": ["Exterior security cameras on property", "Dryer", "Essentials"],
"checkInTime": "Check-in after 4:00 PM",
"checkOutTime": "Checkout before 12:00 PM",
"petsAllowed": false,
"smokingAllowed": false,
"eventsAllowed": false,
"quietHours": "10:00 PM - 8:00 AM",
"calendarStart": "2026-10-01",
"calendarEnd": "2026-11-30",
"calendarDays": 61,
"availableDays": 7,
"nextAvailableDate": "2026-11-13",
"minNights": 1,
"isAvailable": true
}
The full row also carries the complete flat amenities list (55 entries for this listing), the full amenityGroups object, the house rules as Airbnb prints them, the safety features, the description as plain text, the photos, and availability: one entry per day with available, checkInAllowed, checkOutAllowed, minNights and maxNights.
Joining and using the two datasets
Both Actors put the same first columns in the same order, so a spreadsheet join on platformListingId gives one wide row per listing: price columns from step 1, everything else from step 2. A few uses:
| Goal | Columns |
|---|---|
| Shortlist for remote work |
amenityGroups["Internet and office"] contains "Dedicated workspace", quietHours not null, minNights
|
| Family stay |
maxGuests, amenityGroups["Family"], petsAllowed, sleepingArrangements
|
| Where comparable listings lose points |
ratingCleanliness … ratingValue against the search's pricePerNight
|
| Occupancy indicator |
availableDays / calendarDays, per listing, per month |
| Price per available night | step 1 priceTotal / nights next to step 2 availableDays
|
The Output tab of the details Actor has five views (Overview, Ratings, Amenities, House rules, Availability) for a quick look before exporting.
Honest limits
-
No prices on listing pages. Airbnb computes the price of a stay after the page loads; the details Actor reads the page only, so every price column is
null. Prices come from step 1, which is why the pipeline has two steps. -
The calendar starts on the first day of the current month, so past days count as unavailable in
availableDays. There is no per-day price in it.isAvailableisnullwhen your dates fall outside the fetched months. -
Texts are Airbnb's texts, user-written and localised: descriptions, rules and amenity names come in the requested
locale. The flags (petsAllowed,smokingAllowed,eventsAllowed) and the calendar are language independent, which is what to filter on. - Removed or private listings yield no row; the log names them. A run whose listings all fail ends as failed, never as an empty success.
- Cost: one event per listing, plus one event per calendar month per listing when the calendar is requested. 100 listings with a three-month calendar are 100 listing events and 300 calendar events. A listing takes about two seconds, three with its calendar.
- No personal data. The listing page names the host and shows reviews; none of it is read or stored. You get listing facts, the average and category ratings and the review count.
Use the data for analysis, respect the platform's terms and local law, and do not republish listings.
Disclosure: I build these Actors and may earn a commission if you sign up to Apify through these links.













