Airbnb Rooms URL Scraper — Host Portfolio & Co-Host Listings
Pricing
Pay per usage
Airbnb Rooms URL Scraper — Host Portfolio & Co-Host Listings
This actor extracts clean, structured Airbnb room URLs from search results, maps, filters, or specific locations. It automates URL collection for research, pricing analysis, lead generation, and large-scale Airbnb data projects with reliable output.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
0
Bookmarked
4
Total users
0
Monthly active users
9 days ago
Last modified
Categories
Share
Airbnb Rooms URL Scraper — Host Portfolio & Co-Host Listings
Paste an Airbnb room URL and get back the listing, the host behind it, the host's entire public property portfolio, and every named co-host with the portfolio they run — as structured JSON. It also accepts a host profile URL directly, skipping the listing step. No Airbnb login is required, and every field is documented against the live payload, not guessed. Run it now on the Apify Store to see a real host portfolio in seconds.
🏘️ What is the Airbnb Host Portfolio and Co-Host Listings Scraper?
It is an Apify Actor that expands an airbnb.com/rooms/<id> URL into three things: the full listing record, the host's public roster of other properties, and the portfolio each co-host on the listing runs. A airbnb.com/users/show/<id> host profile URL works too, going straight to the portfolio without a listing fetch. No Airbnb account, cookie or API key is needed — everything comes from pages and GraphQL sections Airbnb serves to any visitor. It is built for short-term-rental analysts, acquisition scouts, housing researchers and data teams who need to know how many doors sit behind one "host."
🔓 What Airbnb host and listing data is publicly available to scrape?
A listing page, a host's public profile and the "Meet your host" panel are all visible to any visitor without signing in — that is the entire surface this Actor reads.
| Data category | Publicly available | Restricted |
|---|---|---|
| Listing details (title, description, amenities, house rules, cancellation policy, photos) | ✅ Full listing page | — |
| Ratings and review count | ✅ Category breakdown + total count | ❌ Full review text/photos beyond what the page renders |
| Host identity (name, photo, superhost flag, verifications, about, time as host) | ✅ From the public profile / host card | ❌ Email, phone, exact address |
| Co-hosts named on a listing | ✅ Name + profile photo, resolved to a numeric id | — |
| Host's other properties (roster) | ✅ Up to 10 rows, plus the honest total count | ⚠️ Airbnb itself never serves more than 10 rows on any public profile, logged in or not — this is a platform ceiling, not a login wall |
| Exact nightly price, live availability calendar, messaging the host | ❌ Not resolved by this Actor | 🔒 Booking flow / host inbox |
The Airbnb Host Portfolio and Co-Host Listings Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login wall.
📋 What data can you extract with the Airbnb Host Portfolio and Co-Host Listings Scraper?
Every row carries the full listing record, host and co-host identity, ratings, and — the reason this Actor exists — the host's real portfolio size plus a roll-up of every property in it.
Listing identity and descriptive fields
| Field | Description |
|---|---|
type | "listing" or "hostProfile", depending on which kind of URL was resolved |
inputMode | "roomUrl" or "hostUrl" |
requestedUrl | The exact URL the Actor requested (room URL with stay params, or the host URL as given) |
id / listingId | Airbnb's numeric listing id (null on hostProfile rows) |
url / listingUrl | Canonical rebuilt room URL with locale/currency/stay params (listing rows); url is the host profile URL on hostProfile rows |
title | Listing title |
description / htmlDescription | Plain-text and HTML versions of the long description |
subDescription | {title, items[]} — the "About this space" bullet overview |
propertyType / roomType | e.g. "House" / "Entire home/apt" |
location / locationSubtitle | Short city label and the full "City, Region, Country" string |
locationDescriptions | Array of {title, content, mapMarkerRadiusInMeters} — the "About the area" text blocks |
coordinates | {latitude, longitude} |
breadcrumbs | Array of {linkRoute, linkText} SEO trail |
images | Array of {caption, imageUrl, orientation} |
amenities | Array of {title, values[]} grouped by category; each value is {title, subtitle, icon, available} |
houseRules | {general: [{title, values[]}]}; each value is {title, icon, additionalInfo} |
cancellationPolicies | Array of {title, policyName, policyId} |
highlights | Array of {title, subtitle, icon, type} — Airbnb's "what guests love" chips |
brandHighlights | {hasGoldenLaurel} — Airbnb's Guest Favorite flag |
seoTitle / metaDescription / sharingConfigTitle / thumbnail | Airbnb's own SEO and social-share copy |
androidLink / iosLink | Airbnb app deep links |
descriptionOriginalLanguage / language | Source locale of the description, and the locale the row was scraped in |
checkIn / checkOut | Stay dates echoed back from the request |
timestamp | ISO-8601 build time of the base listing record |
scrapedAt | ISO-8601 UTC time the row was pushed to the dataset |
Host, co-host and rating fields
| Field | Description |
|---|---|
personCapacity | Guest capacity |
homeTier | Airbnb's internal tier id |
rating | {accuracy, checking, cleanliness, communication, location, value, guestSatisfaction, reviewsCount} |
host | {id, contextualUserId, name, profileImage, isSuperHost, isVerified, highlights[], about, ratingCount, ratingAverage, hostDetails[], timeAsHost:{years,months}} — sourced from the listing's own host card. On a hostProfile row this object stays empty (there is no listing to source it from); use hostUserId / hostDisplayName from the portfolio block instead |
coHosts | Array of {contextualUserId, name, profilePictureUrl, id} — the numeric id is resolved from the "Meet your host" panel so co-host portfolios can be looked up |
coHostCount | Number of co-hosts on a listing row; on a hostProfile row, the number of distinct co-hosts found across the scanned roster properties |
Host portfolio and co-host portfolio fields
This is the layer the base Airbnb listing scraper does not have.
| Field | Description |
|---|---|
hostUserId / hostProfileUrl / hostDisplayName / hostProfileImage | Host identity, resolved from the public profile |
hostIsSuperhost / hostProfileLocation / hostProfileAbout | Superhost flag, stated city and bio from the profile |
hostTimeAsHost | {years, months} |
hostIdentityVerifications | Array, e.g. ["Identity", "Email address", "Phone number"] |
hostReviewsFromGuestsCount | Guest review count across the host's account |
hostListingsCount | Airbnb's own total — honest at any portfolio size |
hostListingsRosterCapped | true when Airbnb returned fewer roster rows than the real total |
hostListings[] | One row per portfolio property: listingId, url, title, roomTypeCategory, roomTypeLabel, localizedRoomType, propertyType, bedrooms, beds, bathrooms, ratingAverage, reviewsCount, isSuperhost, instantBookEnabled, isNewListing, thumbnail, isCurrentListing, plus city when mapPortfolioCities is on |
hostPortfolioSummary | Roll-up over the rows actually returned: listingsCount, operatorTier, rosterRows, rosterCapped, ratedListings, unratedListings, averageRating, totalReviewsOnRoster, entirePlaceCount, privateRoomCount, sharedRoomCount, hotelRoomCount, entirePlaceShare, superhostListings, instantBookListings, newListings, totalBedrooms, totalBeds, totalBathrooms, propertyTypeBreakdown[], plus cityBreakdown[], distinctCities, citiesUnresolved when mapPortfolioCities is on |
hostPortfolioResolved | false when the host's profile had no public container to read |
coHostPortfolios[] | One block per co-host: {coHostId, coHostName, coHostProfileUrl, coHostProfileImage, listingsCount, rosterCapped, isSuperhost, location, listings[]} — listings[] is the same shape as hostListings[] |
coHostListingsTotal | Sum of every co-host's real portfolio size (null when no co-host is named) |
host.profile / coHosts[].profile | Only when enrichHostProfiles (or enrichUserProfiles) is on: {id, name, profileImage, about, location, isSuperHost, timeAsHost, timeAsUser, hasIdentityBadge, identityVerificationTypes, reviewsFromGuestsCount}, empty keys stripped |
🛠️ Why not build this yourself?
Airbnb does not publish a self-serve API for host portfolios, co-host relationships or roster data — the fields above only exist inside the site's own web app, behind an internal GraphQL endpoint (StaysPdpSections) and a legacy profile query that are not documented for third-party use. Reaching them means reverse-engineering the persisted-query hash, keeping up with Airbnb's payload shape as it migrates (this Actor already tracks a move from the old sections[] array to pdpPresentation), and getting past Akamai's TLS fingerprinting — a plain Python HTTP client is refused on sight from most datacenter IPs, which is why this Actor impersonates a real Chrome TLS fingerprint and runs a proxy escalation ladder. Maintaining that yourself means re-deriving the query hash and payload paths every time Airbnb ships a front-end change, plus buying and rotating residential proxy capacity. This Actor absorbs all of that; you send URLs and read JSON.
▶️ How to use the Airbnb Host Portfolio and Co-Host Listings Scraper
- Open the Actor on its Apify Store page and click Try for free (or Run, if you already have it in your account).
- Provide the required input:
startUrls— one or more Airbnb room URLs, host profile URLs, or plain search phrases, mixable in the same run. - Set the optional filters worth tuning first:
minHostListingsto keep only serious operators,includeCoHostListingsif you don't need co-host portfolios, andmapPortfolioCitiesif you want market spread by city. - Start the run.
- Download the results as JSON, CSV, Excel, or any other format Apify's dataset export supports, or stream them via the API while the run is still going.
How to scale to bulk host and listing extraction
startUrls is a plain array — add as many room URLs, host profile URLs, or search phrases as you need in one run; there is no schema-level cap on the list length. Internally the Actor processes up to 6 targets concurrently and caches every host/co-host profile fetch per run, so a batch of 200 room URLs that all belong to the same management company costs one profile fetch, not 200.
💼 What can you do with Airbnb host portfolio data?
- 🏙️ Short-term-rental analysts use
hostListingsCountandhostPortfolioSummary.operatorTieracross a neighborhood's listings to measure how much of the visible supply sits behind multi-property operators rather than individual hosts. - 🤝 Acquisition and M&A scouts use
coHostPortfolios[].listingsCountandhostDisplayNameto find the management company actually running a "small host" listing, before reaching out. - 🏛️ Regulators and housing researchers use
hostPortfolioSummary.entirePlaceShareandhostListingsCountas evidence of whole-home commercial operation versus genuine home-sharing. - 📊 Property managers use
hostPortfolioSummary.averageRating,propertyTypeBreakdownandtotalReviewsOnRosterto benchmark a competitor's roster size and review volume. - 🤖 AI agents and RAG pipelines pass
hostProfileAbout,descriptionandhostPortfolioSummarystraight into a vector store or an agent's context window — typed JSON needs no HTML parsing before it is usable as a tool result.
🛡️ How does the scraper handle rate limits and blocking?
Every request is sent with a Chrome 131 TLS fingerprint (curl_cffi's impersonate="chrome131"), because Airbnb sits behind Akamai and refuses a plain Python client's fingerprint from datacenter exits. Proxy use follows a ladder: if you set an explicit proxyConfiguration, that proxy is used from the very first request; if you leave it off, the Actor starts with no proxy and escalates automatically — first to Apify's datacenter proxy, then to a sticky residential proxy — the moment a response comes back without its expected data container (a soft-block signal, not a parsing bug). Room-page fetches get up to 3 retries with an escalation between each attempt; profile fetches get up to 3 retries with a short delay. A listing that genuinely has no payload (de-listed, private, or a dead id) is treated as unavailable rather than retried forever, logged, and skipped — it is not written to the dataset and not charged.
⬇️ Input
| Parameter | Required | Type | Description | Example value |
|---|---|---|---|---|
startUrls | Yes | array | Room URLs, host profile URLs, bare room ids, or search phrases, mixable in one run | ["https://www.airbnb.com/rooms/25166525"] |
includeHostListings | No | boolean | Open the host's public profile and return the rest of their roster in hostListings[]. Default: true | true |
maxHostListings | No | integer (1–10) | Roster rows kept per profile. Airbnb never returns more than 10 on any public profile. Default: 10 | 10 |
includeCoHostListings | No | boolean | Resolve every co-host on the listing and roster their portfolio too. Default: true | true |
minHostListings | No | integer (0–10000) | Drop hosts whose real hostListingsCount is below this. Filtered rows are not written and not charged. Default: 0 | 2 |
mapPortfolioCities | No | boolean | Resolve each portfolio property to a city and add cityBreakdown / distinctCities. Costs up to maxHostListings extra small requests per host. Default: false | false |
enrichHostProfiles | No | boolean | Attach the legacy host.profile / coHosts[].profile cards. Default: false | false |
enrichUserProfiles | No | boolean | Original name of the same switch; kept working for saved base inputs — turning on either one enables the profile cards. Default: false | false |
checkIn | No | string (YYYY-MM-DD) | Sent to Airbnb as check_in on every room URL | "2026-09-15" |
checkOut | No | string (YYYY-MM-DD) | Sent to Airbnb as check_out on every room URL | "2026-09-18" |
adults | No | integer (min 1) | Guest count sent with each room request | 2 |
children | No | integer (min 0) | Children (ages 2–12) sent with each room request | 0 |
infants | No | integer (min 0) | Infants (under 2) sent with each room request | 0 |
pets | No | integer (min 0) | Pets sent with each room request | 0 |
locale | No | string, enum (93 values) | Language of titles, property types, amenities and location subtitles. Default: "en-US" | "en-US" |
currency | No | string, enum (48 values) | Currency requested from Airbnb for any price-bearing field. Default: "USD" | "USD" |
proxyConfiguration | No | object | Apify Proxy or your own proxy URLs. Default: {"useApifyProxy": false} | {"useApifyProxy": false} |
Example input
{"startUrls": ["https://www.airbnb.com/rooms/25166525","https://www.airbnb.com/rooms/53309811","https://www.airbnb.com/users/show/213799039"],"includeHostListings": true,"maxHostListings": 10,"includeCoHostListings": true,"minHostListings": 2,"mapPortfolioCities": false,"enrichHostProfiles": false,"enrichUserProfiles": false,"checkIn": "2026-09-15","checkOut": "2026-09-18","adults": 2,"children": 0,"infants": 0,"pets": 0,"locale": "en-US","currency": "USD","proxyConfiguration": { "useApifyProxy": false }}
⬆️ Output
Typed, normalized JSON with a consistent schema across runs: one row per resolved room URL (type: "listing") and one row per host profile URL (type: "hostProfile"). Every listing row carries the full base listing record first, with the host-portfolio and co-host blocks merged on top. Export as JSON, CSV, Excel, XML, or RSS directly from the Apify dataset, or read it through the API while the run is still active.
Example output
{"type": "listing","inputMode": "roomUrl","requestedUrl": "https://www.airbnb.com/rooms/53309811?adults=2&check_in=2026-09-15&check_out=2026-09-18","id": "53309811","listingId": "53309811","url": "https://www.airbnb.com/rooms/53309811?locale=en-US¤cy=USD&adults=2","listingUrl": "https://www.airbnb.com/rooms/53309811","title": "Miami Lux Lake Front Retreat","description": "Wake up to lake views in this bright, fully renovated home minutes from Wynwood.","propertyType": "Entire home","roomType": "Entire home/apt","personCapacity": 12,"location": "Miami","locationSubtitle": "Miami, Florida, United States","coordinates": { "latitude": 25.7889, "longitude": -80.2 },"rating": {"guestSatisfaction": 4.98,"cleanliness": 4.98,"communication": 4.99,"location": 4.9,"reviewsCount": 60},"host": {"id": "268185052","name": "Omg Homes","isSuperHost": true,"ratingCount": 974,"ratingAverage": 4.94},"coHosts": [{ "id": "471724265", "contextualUserId": "471724265", "name": "Ricardo" }],"amenities": [{ "title": "Bathroom", "values": [ { "title": "Hair dryer", "available": true } ] }],"checkIn": "2026-09-15","checkOut": "2026-09-18","hostUserId": "268185052","hostProfileUrl": "https://www.airbnb.com/users/show/268185052","hostDisplayName": "Omg Homes","hostIsSuperhost": true,"hostListingsCount": 3,"hostListingsRosterCapped": false,"hostListings": [{"listingId": "1392286122370334235","url": "https://www.airbnb.com/rooms/1392286122370334235","title": "Everglades-Homestead. Tropical Retreat. Miami-Keys","roomTypeCategory": "entire_home","roomTypeLabel": "Entire place","propertyType": "House","bedrooms": 4,"beds": 6,"bathrooms": 3.5,"ratingAverage": 4.98,"reviewsCount": 54,"isSuperhost": true,"instantBookEnabled": true,"isNewListing": false,"isCurrentListing": false}],"hostPortfolioSummary": {"listingsCount": 3,"operatorTier": "small_operator","rosterRows": 3,"rosterCapped": false,"averageRating": 4.98,"totalReviewsOnRoster": 335,"entirePlaceCount": 3,"entirePlaceShare": 1.0,"superhostListings": 3,"totalBedrooms": 12,"totalBeds": 18,"totalBathrooms": 11.5,"propertyTypeBreakdown": [ { "propertyType": "House", "count": 3 } ]},"hostPortfolioResolved": true,"coHostCount": 1,"coHostListingsTotal": 3,"coHostPortfolios": [{"coHostId": "471724265","coHostName": "Ricardo","coHostProfileUrl": "https://www.airbnb.com/users/show/471724265","listingsCount": 3,"rosterCapped": false,"isSuperhost": false,"listings": [ "same shape as hostListings[]" ]}],"language": "en-US","scrapedAt": "2026-08-04T00:00:00Z"}
⚙️ How does it work?
Requests go out over curl_cffi with a Chrome 131 TLS fingerprint, since Airbnb sits behind Akamai and a plain Python client's fingerprint is refused before a page is even served. The listing content is read from the same pdpPresentation and StaysPdpSections structures Airbnb's own web app renders from; the host's roster comes from their public profile page, and co-host identities come from the "Meet your host" panel — all surfaces that require no login, cookie or API key. A missing data container in an HTTP 200 response is treated as a soft block and triggers a proxy-ladder escalation, while a genuinely de-listed or private listing is recognized as such and skipped rather than retried. The output schema stays the same shape regardless of which Airbnb layout served the page, so a UI change on Airbnb's side does not change your downstream field names.
🔌 Integrations
Works with anything that can call the Apify API — no separate signup or credential beyond your Apify API token.
Calling the scraper programmatically
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_API_TOKEN>")run = client.actor("<YOUR_USERNAME>/airbnb-rooms-urls-scraper-host-portfolio-and-co-host-listings").call(run_input={"startUrls": ["https://www.airbnb.com/rooms/25166525"],"includeHostListings": True,"includeCoHostListings": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["hostDisplayName"], item["hostListingsCount"])
Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request.
No-code tools (n8n, Make)
In n8n, use the HTTP Request node pointed at the Actor's run-sync-get-dataset-items endpoint with your input JSON as the body. In Make, the Apify app module runs the Actor and passes the resulting dataset items straight into the next step of your scenario — no separate database wiring needed.
⚖️ Is it legal to scrape Airbnb host and listing data?
Scraping publicly accessible data — the same information any visitor sees without logging in — is generally lawful, including under precedent such as hiQ Labs v. LinkedIn in the United States. The Airbnb Host Portfolio and Co-Host Listings Scraper returns only publicly available data: listing content plus the host and co-host identities and rosters Airbnb itself displays to any visitor. Because this data includes personal data about identifiable individuals — a host's or co-host's name, photo, bio and portfolio — GDPR (in the EU/UK) and the CCPA (in California) govern how you may store, use and retain it once collected; you need a lawful basis for processing personal data and should honor deletion or access requests from data subjects. Consult legal counsel if your use case involves bulk storage of personal data.
❓ Frequently asked questions
What Airbnb host and listing fields does this scraper return?
The top fields are hostListingsCount (the host's real portfolio size), hostListings[] (the roster itself), coHostPortfolios[], hostPortfolioSummary, and the full base listing record (title, description, amenities, rating). See What data can you extract above for the complete field set.
Does this scraper require an Airbnb account or login?
No. Every surface it reads — the room page, the host's public profile, and the "Meet your host" panel — is public and requires no cookie, session or API key.
How many Airbnb hosts or listings can I extract in one run?
startUrls accepts any number of room URLs, host profile URLs or search phrases in a single run — there is no schema-level cap on the list. A search phrase resolves up to 20 listing ids per keyword. Internally, up to 6 targets are processed concurrently.
What happens if a listing is de-listed or a host has no public profile?
A de-listed, private or removed listing still returns HTTP 200 with an empty payload; the Actor recognizes this as an unavailable listing (not a block), logs it, and does not write a row for it. A host profile with no parseable public container is handled the same way — hostPortfolioResolved is false on rows that do get written, or the target is skipped entirely if it was the only thing being resolved. Skipped and filtered rows are never written to the dataset and never charged, so check the Actor's run log for FAILED entries if a dataset row count is lower than your input list.
Can I scrape multiple Airbnb hosts or listings at once?
Yes. Mix room URLs, host profile URLs and search phrases in the same startUrls array; each is resolved independently and profile fetches are cached per run, so overlapping hosts across many listings only cost one fetch each.
Does this scraper work with Claude, ChatGPT and other AI agent tools?
It is callable as a standard HTTP endpoint by any agent framework — start a run via the Apify API or apify_client and read the resulting dataset. No MCP server is documented for this Actor.
How does this differ from the standalone Airbnb Rooms URL Scraper?
It is a superset: all of the base listing scraper's 11 input keys still work exactly as before, and every one of the 35 base listing fields (verified against data_extractor.build_output) is produced first, unchanged. This variant adds the host-portfolio block, the co-host-portfolio block, and the roster roll-up on top — nothing from the base row is renamed or removed.
Does this scraper return data in a format LLMs can use directly?
Yes. Typed, normalized JSON with consistent field names across runs — no HTML parsing, no CSS selectors. Pass it directly to an LLM's context, index it into a vector store, or feed it to an agent tool.
What happens when Airbnb changes its layout or anti-bot system?
The Actor is maintained and the output schema is designed to stay stable across Airbnb front-end changes — the source already tracks one such migration, from Airbnb's older sections[] array to the current pdpPresentation structure. No specific update turnaround time is published.
Can I use this scraper without managing proxies or browser infrastructure?
Yes. It ships with a Chrome-TLS-fingerprint HTTP client and an automatic proxy ladder (none → Apify datacenter → Apify residential) that escalates on its own when a response looks blocked. You can also supply your own Apify Proxy or third-party proxy URLs via proxyConfiguration.
Which fields work best for AI training data and RAG indexing?
For RAG, index description, hostProfileAbout and host.about — the highest-information free text. For structured training features, hostListingsCount, hostPortfolioSummary, and rating return as consistent typed primitives (integers, floats, booleans) across every row.
🔗 Related scrapers
| Scraper | What it extracts |
|---|---|
| ../tripadvisor-review-scraper | Hotel reviews, ratings, reviewer profiles and owner responses — the reviewer side of the hospitality data this Actor covers from the host side |
| ../Google-Maps-Extractor | Business name, address, phone, website, rating and coordinates for any place on Google Maps — useful for cross-referencing a host's portfolio addresses against real-world business listings |
💬 Your feedback
Found a bug or missing a field? Let us know through the Actor's Issues tab on the Apify Console, or leave a review on the Store listing — it is the fastest way to get a fix prioritized.