Airbnb Occupancy Scraper avatar

Airbnb Occupancy Scraper

Pricing

$19.99/month + usage

Go to Apify Store
Airbnb Occupancy Scraper

Airbnb Occupancy Scraper

Monitor Airbnb occupancy and availability across listings or cities. Retrieve booked nights, seasonal trends, and pricing signals. Designed for researchers, investors, and revenue teams needing trusted data.

Pricing

$19.99/month + usage

Rating

0.0

(0)

Developer

SimpleAPI

SimpleAPI

Maintained by Community

Actor stats

0

Bookmarked

12

Total users

0

Monthly active users

28 minutes ago

Last modified

Share

๐Ÿ“… Airbnb Availability & Rate Scraper

Scrape the forward-looking availability calendar of Airbnb listings and the real per-day fields Airbnb's own calendar returns โ€” check-in / check-out eligibility, the bookable flag, minimum / maximum nights, and the nightly price when it is present. From those real values it derives an honest unavailableRate and, only from real calendar prices, ADR / estimated revenue / RevPAR.

This actor is deliberately honest about what the data is and is not. Read the two notes below before using it.

โš ๏ธ Honesty notes (read these)

  • This is availability, not occupancy. Airbnb's calendar tells you whether a night is available. An unavailable night can be either genuinely booked or blocked by the host โ€” the public calendar cannot distinguish the two. We therefore report unavailableRate = unavailable days รท counted days, clearly labeled "includes host-blocked days; not a booking/occupancy rate." We never call unavailable nights "booked" and never emit an "occupancy rate."
  • Money fields come only from real prices. adr, estimatedRevenue and revpar are computed exclusively from nightly prices that appear in the calendar payload. When a listing exposes no calendar prices, these fields are null โ€” never a regional average, a search-page price, or an interpolated guess. estimatedRevenue / revpar additionally assume every unavailable night is a paid booking, which is an upper bound (it includes host-blocked nights); they are labeled as such.
  • Forward-only. PdpAvailabilityCalendar returns future availability. Past dates are clamped to today. There is no historical calendar available from this endpoint.

How it works

  1. curl_cffi Chrome TLS impersonation. Airbnb sits behind Akamai / PerimeterX. Plain requests/aiohttp are blocked at the TLS (JA3) layer before reaching the app. This actor uses curl_cffi with impersonate="chrome" so requests replay a real Chrome handshake.
  2. Soft-block guard + proxy escalation. An airlock / challenge / empty shell is detected and reported as a block (the run keys on the presence of the expected bootstrap container, not on loose tokens), and the actor retries on a fresh residential proxy IP.
  3. Dynamic persisted-query discovery. The GraphQL persisted-query hash is found by scanning the room page and its JS bundles for the hash next to PdpAvailabilityCalendar โ€” no hardcoded bundle name that breaks on every Airbnb deploy.
  4. Harvest + derive. Every already-in-payload day field is emitted; derived metrics are computed with the honesty rules above.

Input

FieldTypeNotes
listingsarray (required)Room URLs or bare IDs, one per line.
startDatedateAbsolute (2026-08-01) or relative (7 days, 1 month). Empty = today. Past โ†’ clamped to today.
endDatedateAbsolute or relative. Empty = start + 30 days. Up to ~12 months ahead.
billingModeenumper_listing (default, 1 charge/listing) or per_day.
includeDaysbooleanEmit per-day child rows (default true).
includeListingDetailsbooleanBest-effort title/type/capacity/beds/baths/coords (default false).
includeHostProfilebooleanBest-effort superhost/name/response rate (default false).
proxyConfigurationproxyDefault residential (recommended; datacenter is usually blocked).
{
"listings": ["https://www.airbnb.com/rooms/35329051"],
"startDate": "2026-08-01",
"endDate": "2026-10-31",
"billingMode": "per_listing",
"includeDays": true,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}

Output

Two row types share the dataset and are distinguished by type / isChild.

Parent โ€” type: "listing" (one per target):

{
"type": "listing", "isChild": false,
"roomId": "35329051", "listingUrl": "https://www.airbnb.com/rooms/35329051",
"windowStart": "2026-08-01", "windowEnd": "2026-10-31",
"totalDays": 92, "availableDays": 40, "unavailableDays": 52,
"unavailableRate": 0.5652,
"adr": 128.5, "priceCoverage": "40/92", "pricedDays": 40,
"estimatedRevenue": 6682.0, "revpar": 72.63,
"minPrice": 110.0, "maxPrice": 210.0,
"currency": "USD", "scrapedAt": "2026-07-03T12:00:00Z"
}

Child โ€” type: "day" (one per calendar day, mirrored to a per-run days-<runId> dataset):

{
"type": "day", "isChild": true, "parentId": "35329051", "roomId": "35329051",
"date": "2026-08-01", "dayOfWeek": "Saturday", "isWeekend": true,
"available": false, "availableForCheckin": false, "availableForCheckout": true,
"bookable": false, "minNights": 2, "maxNights": 365,
"price": null, "currency": null, "scrapedAt": "2026-07-03T12:00:00Z"
}

price is a real number or null (never 0). unavailableRate, adr, estimatedRevenue, revpar are null when the underlying data is absent.

Billing

Pay-per-event. per_listing (default) charges one listing_result per listing. per_day charges one day_result per calendar-day row (~30 for a month, ~365 for a year). Day rows are always produced; billingMode only controls what is billed. See .actor/pay_per_event.json.

Keywords

airbnb scraper, airbnb availability, airbnb calendar, airbnb rates, nightly price, adr, revpar, short-term rental data, str analytics, vacation rental availability, airbnb api, booking calendar, occupancy alternative, curl_cffi, akamai bypass.

Compliance

Only publicly visible calendar data is accessed. Ensure your use complies with Airbnb's Terms of Service and applicable law.