Airbnb Calendar & Date-Specific Nightly Price
Pricing
from $8.00 / 1,000 price quotes
Airbnb Calendar & Date-Specific Nightly Price
Per-date Airbnb availability plus the real bookable nightly price for those dates, in the currency you request (USD/EUR/GBP/TRY), with per-night breakdown and an explicit capacity flag. Airbnb's calendar returns price:null; this fetches the actual quote. Pay only for delivered data.
Pricing
from $8.00 / 1,000 price quotes
Rating
0.0
(0)
Developer
MaviLinkLabs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Airbnb Calendar & Real Date-Specific Price Scraper
Get per-date availability for one or many Airbnb listings and the real bookable nightly price for those dates — for an exact date range or months ahead, in the currency you request.
Airbnb's calendar tells you whether a date is free, but its calendar response returns price: null. The real price for a specific date comes from a separate booking quote. This actor fetches that quote and returns it as a structured, honest record.
What it does
- Per-date availability calendar — up to 12 months, with
available, check-in / check-out eligibility, and each date's ownminNights/maxNights. - Real, date-specific nightly price — for available windows it asks Airbnb's own booking engine for the price quoted for those exact dates, rather than the
nullthe calendar returns. What you get is the total Airbnb displays for that window — which may or may not include tax, depending on the response (see Honest limits; every quote reports which viataxScope) — not a final checkout total. - Exact date range OR months ahead — set
startDate+endDatefor a specific window, or usecalendarMonthsto look a number of months into the future. - One stay in, one quote out — if you ask for one specific stay (
startDate+endDate), you get exactly one quote for exactly that stay, and you are charged for exactly that one quote. The actor does not add sampled side-quotes you did not ask for. See Asking for one stay vs. asking for a price curve below. - Two pricing modes:
sampled(default) — a handful of quotes spread across months (one per month first), giving a low-cost price curve across the horizon.allAvailableDates— price every eligible check-in date; requiresmaxPricedDatesas an explicit cost budget.
- Controlled cost, no silent truncation — in
allAvailableDatesmode the budget is enforced byonCostLimitExceeded:error(default: price nothing for that listing, 0 quote charge,status: "cost-limit-exceeded") ortruncate(price the first N chronologically and flag exactly how many dates were omitted). - Requested currency — every price is returned in the ISO currency you set (
USD,EUR,GBP,TRY, ...), not whatever the listing happens to use. - Multiple listings per run — pass a list of room URLs or IDs.
- Min-stay aware quotes — each quote window respects that date's own minimum-nights rule (or your fixed
quoteNights), so quotes don't fail on min-stay mismatches. - Explicit guest-capacity handling — you set
adults; the actor reads the listing'smaxGuestsand reportsrequestedAdults,effectiveAdults, andadultsReducedToFitCapacity. If your guest count exceeds capacity, it re-quotes at the fitting count instead of returning no price — and flags that it did. - Structured price breakdown —
nightlyBase,nights,accommodationSubtotal,subtotal,fees[],discount,taxes,totalPrice, andcurrency, each read from Airbnb's own price-detail lines instead of being derived by dividing a rounded total.accommodationSubtotalis the accommodation line on its own;subtotalis the pre-tax total;totalPriceis what the lines add up to. Three different numbers, three separate fields — no field is ever used with two meanings. - Cent-accurate total — Airbnb's headline price is rounded up for display (a $305.75 stay shows as "$306").
priceRaw/priceTotalkeep that displayed value, whilepriceBreakdown.totalPrice— and theperNightaverage derived from it — carry the exact figures from the price-detail lines. - Long stays are priced as long stays — when Airbnb quotes by the month or week instead of by the night, that line is read as the accommodation (never as a fee) and the period is named. If the window you asked for is not one such period (a 46-night stay priced monthly), the actor reports the monthly rate and withholds the window totals instead of passing a monthly figure off as a 46-night one. See Long stays below.
- The tax scope is reported, not assumed — Airbnb's response for the same listing and window is not always the same: sometimes it itemizes a
Taxesline, sometimes it bundles oneTaxes and feesline, sometimes it declares the total is before taxes. Every quote says which one it got viataxScopeandtotalIncludesTaxes, so a total is never silently mixed up with a pre-tax one. cancellationPolicyandcanInstantBook— read from the live listing response, when a quote request is made (see limits below).- Pay only for successfully delivered events — calendars and quotes are charged only when real data was delivered.
Asking for one stay vs. asking for a price curve
These are two different questions, and the actor bills them differently.
One stay — you give startDate + endDate and want the price of that stay:
{ "listings": ["..."], "startDate": "2026-09-01", "endDate": "2026-09-05" }
You get one quote for 2026-09-01 → 2026-09-05 and are charged for one price-quote event. The actor treats this as an explicit stay when all of the following hold:
- both
startDateandendDateare set, and pricingModeissampled(the default), andquoteNightsequals the number of nights in the range or is0(the default, "use the date's own minimum-nights rule").
In that case no sampled calendar windows are quoted at all — only the stay you asked for.
A price curve — you want many dates priced, not one stay. Use one of:
pricingMode: "allAvailableDates"withmaxPricedDates— prices every eligible check-in date in the window. This is never treated as an explicit stay, in or out of a date range.calendarMonthswithoutstartDate/endDate— sampled quotes spread across the months ahead (one per month first), up tomaxQuotesPerListing.startDate/endDateplus aquoteNightsthat differs from the range length — e.g. a 30-day window withquoteNights: 3asks "what does a 3-night stay cost across this window", so sampling stays on and you additionally get the whole-window quote.
Known consequence — read this if you use a wide range.
sampled+ a widestartDate/endDate+quoteNights: 0is an explicit stay, so a 90-day range returns one quote for a single 90-night stay, not four sampled windows inside it. No calendar sampling happens at all on that path — the single exact quote is the entire price output for the listing.And if Airbnb will not price that long stay, you get zero quotes for the listing. Very long stays are frequently above a listing's
maxNights, or simply not offered, and the actor never invents a price it did not receive: the run then delivers the availability calendar and no price quotes (you are charged for the calendar, and for zero price-quote events). The run log names the window and the reason. If you wanted prices across the span rather than one long stay, usepricingMode: "allAvailableDates"withmaxPricedDates, orcalendarMonthswithout a range, or set aquoteNightsthat differs from the span.This is documented here as current behavior, not defended as the right default. Open product decision for the next release: whether a wide range +
quoteNights: 0should keep meaning "one long stay" or should fall back to a sampled price curve when the long stay is unpriceable. Behavior is unchanged in this version.
maxQuotesPerListing is a hard cap. In sampled mode, every delivered and charged quote — including the direct quote for your exact window — counts against it. maxQuotesPerListing: 4 with a date range delivers at most 4 quotes, never 5. (In allAvailableDates mode the budget is maxPricedDates; maxQuotesPerListing does not apply there.)
Honest limits (please read)
-
Live quote, not a fixed price. Prices are the live quote at scrape time. Airbnb prices are dynamic and change through the day — re-run for a fresh quote. This actor does not claim a permanent or forever-exact price.
-
Taxes: whatever Airbnb prints, plus a label for what it means. The actor never calls the checkout endpoint and never estimates a tax. It reports the tax lines that are in the response, and
taxScopestates which situation produced the numbers:taxScopemeaning taxestaxesAndFeesCombinedtotalIncludesTaxestaxes-itemizeda separate Taxesline was in the responseamount nulltrueorfalsetaxes-and-fees-combinedAirbnb printed one Taxes and feeslinenullamount trueorfalsetaxes-itemized-and-combinedboth shapes arrived in the same response amount amount trueorfalseambiguousa tax line exists but the lines do not reconcile with the total as read as read nullbefore-taxes-declaredno tax line; Airbnb's own total says "before taxes" nullnullfalseunclassified-linesno tax line was recognized, but the response carries extra line(s) that were passed through unclassified in fees[](e.g.VAT,Sales tax,Resort fee)nullnullnullno-tax-lineno tax line, no declaration, and no extra line of any kind nullnullnullnot-classified-localenon-English locale: labels not classified at allnullnullnullno-price-detailsthe response carried no price-detail lines; the whole breakdown is nullnullnullnulltotalIncludesTaxesis decided arithmetically whenever a tax line is present:trueif the total equals the sum of all lines,falseif it equals the sum without the tax terms, andnull(withtaxScope: "ambiguous") if neither matches. It is never inferred from wording alone.taxesDeferredistrueexactly when no tax figure of any kind was isolated (taxesandtaxesAndFeesCombinedare bothnull). It does not by itself mean Airbnb charges no tax — readtaxScopefor that:no-tax-lineis the only value that says the response really had nothing extra, whileunclassified-linessays extra lines were there and we refused to guess what they were.A bundled
Taxes and feesline is not split — guessing the tax share of it would be inventing a number, so it is reported as its own field and never astaxes. -
Whether a total includes tax depends on the response, not on us. The same listing and window can return a tax-inclusive total on one request and a before taxes total on another; we have observed both and do not claim to know why. Read
totalIncludesTaxeson the quote rather than assuming. -
priceBreakdown.fees[]is a passthrough, not a classification. Fee lines are taken from Airbnb's own price-detail line items as{label, amount}with the label exactly as Airbnb localized it (e.g."Resort fee"). The actor does not decide what a fee means, and it never estimates a fee that Airbnb did not print. A recognized tax line is never put infees[]. The one exception is a non-Englishlocale, where labels are not classified at all: then every additive line stays infees[]unclassified andtaxScopeisnot-classified-locale. -
Unclassifiable prices stay
null. If a response does not carry the price-detail lines (or their stay length disagrees with the window that was requested), the breakdown fields arenullrather than a plausible-looking guess. On hotel-style listings, where the headline price is read from the display components rather than a flat price field, that price is additionally cross-checked against the price-detail lines: it must be the ceiling of their sum. If it is not, no quote is delivered and none is charged for that window — the mismatch is logged instead of shipping a number we cannot stand behind. -
Room type / rate plan is Airbnb's default, not a choice you make. The actor does not send a
selectedRatePlanId, so Airbnb quotes whatever room type and rate plan it serves by default for that listing and window. Some listings (hotels in particular) label their own breakdown "Price may vary by room type", and the actor passes that reality through rather than hiding it: the quote is the default-room price, not the cheapest or a specific room. Selecting a rate plan is not supported in this version and no field claims otherwise. -
maxGuests,cancellationPolicyandcanInstantBookare read from the booking-quote response (the listing's PDP sidebar), not from the calendar. That means they are populated only when at least one price quote is requested for that listing. If a listing has no priceable dates in the selected window, or you run withenrichPrices: false, these fields staynull. This is deliberate: the actor does not fire an extra request (and an extra charge) just to fill them. -
When capacity is reduced (
adultsReducedToFitCapacity: true), the quoted price is for the reduced guest count that fits the listing, not the number of adults you requested. The reduction is always flagged on the quote (requestedAdultsvseffectiveAdults), never hidden. -
On hotel / multi-room listings,
maxGuestsdescribes the default room, not the listing. Airbnb's own response can reportmaxGuestCapacity: 1for a hotel whose booking form happily accepts — and prices — three guests. The number is passed through because it is real, but the claim is lowered honestly:maxGuestsReliableisfalse,maxGuestsScopeis"default-room"(versus"listing"), andmaxGuestsCaveatstates why. Do not treat it as the listing's guest limit. -
A host discount on your dates does not confuse the breakdown. Airbnb sometimes prints the accommodation line itself as a discounted line (
2 nights × $190.91→$381.81, with a struck-through$448.11and no minus sign). That is a stay charged at a discounted rate, not a deduction: it is reported asnightlyBase/accommodationSubtotal, the struck-through original goes toaccommodationOriginalSubtotal/accommodationDiscount, and only a line that really carries a minus is ever subtracted. -
Availability and price come from Airbnb's public web responses; if Airbnb changes its response format the actor may need an update.
Input
{"listings": ["https://www.airbnb.com/rooms/14926879", "48819442"],"startDate": "2026-09-01","endDate": "2026-09-30","pricingMode": "sampled","maxQuotesPerListing": 6,"quoteNights": 0,"adults": 2,"currency": "EUR"}
Key inputs:
listings(required) — Airbnb room URLs or plain listing IDs.startDate+endDate— optional exact ISO window (YYYY-MM-DD). Provide both or neither. When set,calendarMonthsis ignored and emitteddays[]are filtered to that window.endDatemust be afterstartDateand within the 12-month availability horizon. The window itself is always quoted directly (markedquoteSource: "exact-range-direct"), because Airbnb's availability calendar can report a window as unavailable that the booking endpoint still prices happily. That quote is skipped when the same window was already quoted, counts againstmaxQuotesPerListing, and — like every quote — is charged only if a price actually came back. With the defaultquoteNights: 0this is an explicit stay: it is the only quote you get and the only one you pay for (see Asking for one stay vs. asking for a price curve).calendarMonths(1–12, default 3) — months of availability to fetch when no exact range is given.pricingMode—sampled(default) orallAvailableDates.maxPricedDates— required whenpricingMode = allAvailableDates; the per-listing cost budget. It has no default on purpose, so cost is a contract rather than a surprise.onCostLimitExceeded—error(default) ortruncate.maxQuotesPerListing(default 4) — hard cap on quotes delivered and charged per listing insampledmode; spread one-per-month first. The exact-range direct quote counts against it too, so the cap is never exceeded by one.quoteNights(default 0) — stay length for sampled quotes;0uses each date's own minimum-nights rule. Together withstartDate/endDateit also decides whether your request is one explicit stay or a price curve — see the section above.adults(default 2) — guest count for quotes.currency(defaultUSD) — ISO 4217 currency code for all prices. Case and spacing do not matter (usd," try "), and every record reports the normalized code that was actually used. Anything that is not a real 3-letter currency code — a country code likeTR, a symbol like$, a name likeUS Dollar, or an empty value — stops the run before any request is made and before anything is charged, rather than letting Airbnb price in a currency of its own choosing and labelling the result wrongly. There is no "no currency" mode: omit the field to take the default.marketCountry(defaultUS) — ISO 3166-1 alpha-2 country the requests exit from. Airbnb prices the market you ask from, so the same listing and dates can return a different nightly rate or an extra tax line from a different country. Pinning it fixes the country dimension only — it does not make a price permanently reproducible: prices are still live and change over time, and the city, the exit IP and whatever experiment Airbnb serves are not pinned.USis an arbitrary but fixed default (Airbnb's largest single market); a fixed default beats a random one, but it is not "the real price" — setmarketCountryto whichever market you actually want priced. Independent ofcurrency—currencypicks the money,marketCountrypicks the market (EUR+USis a valid pair). An invalid code stops the run before anything is spent. If yourproxyConfigurationalready pinsapifyProxyCountry, that explicit pin wins, is logged, and is what gets reported.locale(defaulten) — language for localized strings (en,de,tr,en-GB). A value that is not a language tag at all stops the run before anything is requested or charged; valid tags are passed through unchanged.proxyConfiguration— Apify Proxy with residential groups is the default.
Output (one item per listing)
{"listingId": "14926879","url": "https://www.airbnb.com/rooms/14926879","status": "ok","currency": "EUR","marketCountry": "US","calendarMonths": 3,"daysTotal": 30,"daysAvailable": 12,"availabilityRate": 0.4,"firstAvailableCheckin": "2026-09-03","dateRange": { "startDate": "2026-09-01", "endDate": "2026-09-30" },"pricingMode": "sampled","maxGuests": 4,"maxGuestsSource": "sidebar","cancellationPolicy": { "id": 44, "name": "Firm", "tooltip": "Free cancellation before ..." },"canInstantBook": true,"days": [{ "date": "2026-09-03", "available": true, "availableForCheckin": true, "availableForCheckout": true, "minNights": 2, "maxNights": 365 }],"quotesAttempted": 1,"quotes": [{"checkIn": "2026-09-03","checkOut": "2026-09-05","nights": 2,"currency": "EUR","requestedAdults": 2,"effectiveAdults": 2,"adultsReducedToFitCapacity": false,"maxGuests": 4,"maxGuestsReliable": true,"maxGuestsScope": "listing","maxGuestsCaveat": null,"maxGuestsSource": "sidebar","capacityModel": "requested","priceRaw": "€284","priceQualifier": "for 2 nights","priceTotal": 284,"perNight": 141.99,"priceIsPeriodRate": false,"pricePeriod": null,"priceCoversRequestedWindow": true,"priceSource": "primaryLine","quoteSource": "calendar-candidate","priceBreakdown": {"nightlyBase": 129.99,"accommodationRate": 129.99,"accommodationRatePeriod": "night","nights": 2,"accommodationSubtotal": 259.98,"accommodationOriginalSubtotal": null,"accommodationDiscount": null,"subtotal": 271.98,"fees": [{ "label": "Resort fee", "amount": 12.00 }],"discount": null,"taxes": 11.99,"taxesAndFeesCombined": null,"taxesDeferred": false,"totalIncludesTaxes": true,"taxScope": "taxes-itemized","totalPrice": 283.97,"currency": "EUR","breakdownAnomaly": null}}],"scrapedAt": "2026-08-09T00:00:00.000Z"}
The breakdown always adds up, so you can check it yourself:
subtotal = accommodationSubtotal + sum(fees) - discount # the pre-tax totaltotalPrice = subtotal + (taxes + taxesAndFeesCombined, only when totalIncludesTaxes is true)
Field meanings, so none of them can be mistaken for another:
| field | meaning |
|---|---|
nightlyBase | the per-night rate read from Airbnb's "N nights × rate" line (never a rounded total divided by nights). null on a long stay, where Airbnb prints no nightly rate — see Long stays below |
accommodationRate / accommodationRatePeriod | the same rate together with the period one unit of it buys: "night", or "month" / "week" on a long stay. nightlyBase is the night-only view of it and stays null for any other period, so a monthly figure can never be read as a nightly one |
accommodationSubtotal | the amount of that accommodation line on its own, before fees, discounts and taxes. null when the line is not readable — never a guess. (It equals nightlyBase × nights up to Airbnb's own cent rounding; the printed line amount is what is reported.) |
accommodationOriginalSubtotal | when the host discounts those dates, Airbnb prints the stay line with a struck-through original (e.g. $448.11 → $381.81). This is that original. null when Airbnb prints none — the actor makes no discount claim it cannot see. |
accommodationDiscount | accommodationOriginalSubtotal − accommodationSubtotal, reported for information only. It is not part of the arithmetic below, because the accommodation amount is already the discounted one. |
discount | a separate, explicitly negative discount line (e.g. Special offer −$40.05). Only a line that really carries a minus is subtracted. |
subtotal | the pre-tax total: accommodation + fees − discount |
taxes / taxesAndFeesCombined | the tax figures actually printed, kept apart (see taxScope) |
totalPrice | the exact total of the price-detail lines; totalIncludesTaxes says whether tax is inside it |
priceTotal / priceRaw | Airbnb's displayed headline price, which Airbnb rounds up to the whole unit |
perNight (on the quote, not in priceBreakdown) | the average cost of one night of this window, containing exactly what totalPrice contains. It is totalPrice ÷ nights — the exact total, not the rounded headline — and null when there is no exact total to divide (including a long stay whose window is not one period) |
priceIsPeriodRate / pricePeriod / priceCoversRequestedWindow (on the quote) | whether Airbnb's displayed headline is a rate per month/week rather than the total for your window — see Long stays |
The one exception to the arithmetic above is taxScope: "ambiguous": there Airbnb's own lines did not reconcile with the total it printed, and reporting that fact is the point of the value — no number is bent to make the sum work.
Prices are never negative. Money cannot be negative, so a negative value anywhere in the breakdown is a classification error, not a price. Every money field is checked — nightlyBase, accommodationRate, accommodationSubtotal, accommodationOriginalSubtotal, accommodationDiscount, subtotal, discount, taxes, taxesAndFeesCombined, totalPrice and every fees[].amount. Each offending field is reported as null (a fee keeps its label, which is real observed data), the run logs it loudly naming the exact fields, and the quote carries breakdownAnomaly: "negative-total" when a total was affected, "negative-money" when only other fields were. When breakdownAnomaly is set, the arithmetic identities above no longer hold — that is the point of the flag. You may get no number; you will not get a negative one.
breakdownAnomaly is null on a healthy quote. Its other values, most severe last: "long-stay-unreconciled" (a period-priced response whose own lines do not add up), "period-rate-window-mismatch" (the response is priced per period and your window is not one period — see Long stays), "negative-money" and "negative-total".
Long stays (monthly / weekly pricing)
On a long stay Airbnb stops printing "N nights × rate" and prices the accommodation by the period instead, with a single line such as Average monthly price $1,922.48. When it does that, every figure in the response is a per-period figure — the accommodation line, the discount lines and Airbnb's own Price after discount total alike. That matters, because a monthly total is not a 46-night total.
accommodationRatecarries the printed rate andaccommodationRatePeriodsays"month"(or"week"). These two are a direct read of what Airbnb printed and are always reported.nightlyBasestaysnull. Airbnb printed no per-night rate, and the actor does not manufacture one by dividing the monthly rate — the same rule that keepsnightlyBaseoff the rounded display total.- The period line is not a fee. It is the accommodation charge, so
fees[]does not carry it andtaxScopeis free to reportno-tax-linewhen the response really has no tax line. - Lines whose label merely mentions the period (
Monthly stay discount,Airbnb monthly stay savings) are still discounts and are still subtracted.
When the window is exactly one period (28–31 nights for monthly, 7 for weekly) the period figure is the window figure, and the full breakdown is published as usual — accommodationSubtotal, subtotal, totalPrice and perNight all mean what they always mean.
When the window is not one period — a 46-night stay priced monthly, or a 4-night stay carrying a monthly line — the window-scoped fields are withheld rather than guessed:
withheld (null) | accommodationSubtotal, accommodationOriginalSubtotal, accommodationDiscount, subtotal, discount, taxes, taxesAndFeesCombined, totalPrice, every fees[].amount, and perNight |
| still reported | accommodationRate + accommodationRatePeriod (the rate Airbnb printed), the fee labels, priceRaw / priceTotal verbatim, and the raw lines in quote.breakdown |
| flag | breakdownAnomaly: "period-rate-window-mismatch", plus a loud line in the run log |
No window total is ever computed from the rate. Airbnb did not publish one for that window, so neither does the actor — not rate × nights ÷ 30, not by any other arithmetic. You get the real monthly rate and the honest statement that it is not your window's total; the multiplication, with all the assumptions it carries, is yours to make or not.
The displayed headline price is labelled too. priceRaw / priceTotal are whatever Airbnb showed — on a long stay that is a rate, e.g. "$1,340" displayed as monthly. priceRaw is never rewritten; three additive fields on the quote say what it means:
| field | meaning |
|---|---|
priceIsPeriodRate | true when the headline is a rate for a period rather than the total for the requested window |
pricePeriod | "month" / "week", or null on an ordinary stay |
priceCoversRequestedWindow | whether one such period spans the window you asked for |
Why length and nothing else decides this: the same listing quoted at 30 nights (correct) and at 46 nights (wrong) returns byte-identical scope wording — qualifier monthly, headline "… monthly, originally …", highlight label "… per month" — and the lines reconcile perfectly in both, because all of them are monthly. No text and no arithmetic check can tell the two apart. What separates them is that 30 nights fit inside one month and 46 do not. The bands are calendar facts, not tuned values: a calendar month is never shorter than 28 or longer than 31 days (and 28 is also the shortest stay Airbnb prices monthly), and a week is exactly 7.
Billing: these quotes are delivered and charged, like any other. Airbnb returned a real, bookable market price — a monthly rate is precisely the answer for a monthly rental — and the record no longer claims it is anything else. Refusing to deliver would return nothing at all for the entire long-stay segment, which is worse than returning a correctly labelled rate. The rule stays what it always was: a quote is charged when, and only when, a usable price was delivered.
marketCountry on the record is the country actually used, not the one asked for: it is read back off the proxy that minted the requests. It is null — never a guessed code — when no exit country could be pinned (custom proxy URLs, no Apify Proxy, or a local run).
quotesAttempted vs quotes. quotesAttempted counts every window the actor asked Airbnb about; quotes[] holds only the ones that came back with a usable price. When the two differ, the missing windows are named in the run log with the reason — and the difference costs you nothing: only the entries in quotes[] are charged.
In allAvailableDates mode, when a listing has more eligible dates than maxPricedDates, the record carries cost-budget meta: either status: "cost-limit-exceeded" with plannedQuoteCount and maxPricedDates (under onCostLimitExceeded: "error"), or pricedDatesTruncated: true with pricedDatesOmitted and eligibleDatesTotal (under "truncate").
Use cases
- Competitor rate research — real nightly prices per date for a set of listings you choose.
- Occupancy analysis — availability rate per listing per month for market studies.
- STR pricing input — a real price curve plus availability as raw data for your own pricing work.
- Change tracking — put it on an Apify Schedule to re-pull price and availability for the same listings over time.
Pricing — pay only for delivered data
Pay-per-event billing:
- calendar — charged once per listing, only when calendar days were actually delivered.
- price-quote — charged only per successfully delivered price quote.
Failed listings, blocked requests, and empty results are never charged. When onCostLimitExceeded: "error" stops a listing, no quote events are charged for it.
What counts as a "successfully delivered price quote". A quote is delivered — and charged — only when it carries a real, readable price: a price value from Airbnb's response that parses to a positive number and whose source is named on the quote (priceSource). Airbnb sometimes answers with a price block that contains no usable amount (the dates were just booked, the stay length is not offered, the number could not be verified against the price-detail lines). Those attempts are not delivered and not charged, on every quote path. They still appear in quotesAttempted, and each one is logged with its window and the reason, so a listing that returns no prices costs you the calendar event and nothing in price-quote events.
You are never charged for quotes you did not ask for:
- asking for one stay (
startDate+endDate, default settings) costs one price-quote event per listing; - in
sampledmode the total number of charged quotes per listing never exceedsmaxQuotesPerListing; - in
allAvailableDatesmode it never exceedsmaxPricedDates.
FAQ
Do I need an Airbnb login or API key? No. The actor uses Airbnb's public, unauthenticated web endpoints.
Which proxies? Apify Proxy with residential groups is the default configuration.
I gave a date range and only got one quote — where are the others? That is the intended behavior: a startDate + endDate with the default quoteNights: 0 is read as one explicit stay, so you get (and pay for) exactly that stay. To price many dates, use pricingMode: "allAvailableDates" with maxPricedDates, or drop the range and use calendarMonths, or set a quoteNights different from the length of your range. See Asking for one stay vs. asking for a price curve.
Why does subtotal include the fees? Because subtotal is the pre-tax total (accommodation + fees − discount). If you want the accommodation line on its own, use accommodationSubtotal, which is reported separately on every quote.
Can I pick a specific room type or rate plan? No. The actor quotes whatever room type and rate plan Airbnb serves by default for that listing and window; some listings even print "Price may vary by room type" in their own breakdown. See "Honest limits".
Why is taxes null on some quotes? Because that response had no separate tax line. Check taxScope: no-tax-line means Airbnb printed none, before-taxes-declared means Airbnb's own total is stated before taxes, and taxes-and-fees-combined means the tax was bundled into one Taxes and fees line that we refuse to split — the amount is in taxesAndFeesCombined. The actor never calls the checkout endpoint, so it only ever reports what the listing response contains.
Why did the same listing and dates return a different total than before? Airbnb's own response varies: we have seen the same listing/window come back with an itemized tax line on one run and with no tax line at all on another, which changes the total by exactly that line (e.g. $283.97 vs $305.75). We do not claim to know what drives it. Each quote reports taxScope and totalIncludesTaxes so you can tell which one you received, and a live re-run is always the current answer.
Why does priceTotal differ from priceBreakdown.totalPrice by a few cents? priceTotal is Airbnb's displayed headline price, which Airbnb rounds up to the whole unit. priceBreakdown.totalPrice is the exact sum of the price-detail lines. Both are returned; use the breakdown when you need cents.
The listing page shows a struck-through price — where is it in the output? In accommodationOriginalSubtotal (the pre-discount stay amount Airbnb printed) and accommodationDiscount (the difference). accommodationSubtotal, subtotal and totalPrice are already the discounted amounts, so the original is reported beside them rather than folded into them. If Airbnb prints no original, both fields are null.
maxGuests says 1 but the listing clearly sleeps more — why? That is a hotel / multi-room listing, where Airbnb reports the capacity of the default room. Check maxGuestsScope: "default-room" means exactly this, maxGuestsReliable is false, and maxGuestsCaveat spells it out.
Why are maxGuests / canInstantBook null on some listings? They are read from the booking-quote response, so they are filled only when at least one quote is requested for that listing. See "Honest limits".
Is scraping Airbnb legal? The actor only accesses publicly available data. You are responsible for using the data in compliance with applicable laws and Airbnb's terms.