Projections¶
POST /api/v1/projections/
- Scope:
projections:write - Description: estimate a property's annual occupancy, nightly rate and revenue from similar Airbnb listings, given its Airbnb URL or address.
Try it out
Explore parameters, schemas, and live requests in the Swagger UI.
What you get¶
The same projection the Owner Hub projection flow builds in the Beyond app, in one call: Beyond resolves the property, picks the similar Airbnb listings the app would suggest, and returns the 25th, 50th and 75th percentiles of their occupancy, average daily rate (ADR), booked nights and revenue over the trailing year.
The property does not have to be connected to Beyond. Each projection is saved
to the Owner Hub History of the user it is charged to, with the same
downloadable report as a projection made in the app, and the response id is
the saved projection's id.
Cost
Each successful projection spends 4 Owner Hub tokens, the same as a projection in the app, charged to the token's user (for an application token, the application's owner). Requests that fail are not charged.
Request¶
POST /api/v1/projections/
Content-Type: application/vnd.api+json
{
"data": {
"type": "projections",
"attributes": {
"airbnb-url": "https://www.airbnb.com/rooms/53906452",
"bathrooms": 2.5,
"compset-filters": {"radius-km": 15, "bedrooms": ["3", "4"]},
"exclude-airbnb-ids": ["41290385"]
}
}
}
| Attribute | Description |
|---|---|
airbnb-url |
The property's Airbnb listing URL. Send exactly one of airbnb-url or address. |
address |
The property's street address. Send exactly one of airbnb-url or address. |
bedrooms |
Bedroom count. Required with address. |
bathrooms |
Bathroom count; half baths as .5. |
property-type |
Airbnb room type: entire-home, private-room, shared-room or hotel-room. |
market-percentile |
Where the property sits in its market: p25 (budget), p50, p75, p90 or p95 (luxury). |
currency |
ISO 4217 code for adr and revenue. Defaults to the user's display currency, then their billing currency, then USD. |
compset-filters |
Narrow the similar listings with the Comp Sets filters, see below. |
exclude-airbnb-ids |
Up to 100 Airbnb listing ids, as strings, to drop from the similar listings. |
include-airbnb-ids |
Up to 20 Airbnb listing ids, as strings, to add to the similar listings. |
Airbnb URL or address¶
The two ways in behave differently, because Beyond knows more about an Airbnb listing than about an address:
- Airbnb URL. The listing's bedrooms, bathrooms, room type and market percentile fill in any of those you leave out. Send them only to override.
- Address. Nothing is known about the property itself, so
bedroomsis required, andbathroomsandproperty-typeare worth sending: anything you leave out is not matched on. The market percentile defaults to the most common one in the area.
subject.sources in the response says where each value came from.
Comp filters¶
compset-filters takes the same filters as Comp Sets. Every one is optional:
| Filter | Description |
|---|---|
radius-km |
Only listings within this distance, 1 to 100. |
bedrooms |
Bedroom counts as strings; the last one may be open-ended, e.g. ["2", "3+"]. |
beds |
Bed counts, in the same format as bedrooms. |
bathrooms |
Bathroom counts, in the same format as bedrooms. |
rating |
Minimum rating, above 0 and up to 5. |
amenities |
Amenities every listing must have: air-conditioning, heating, washer, dryer, kitchen, parking, hot-tub, internet, tv, pool, waterfront, beachfront, allows-pets. |
listing-types |
Keep only listings of these types: entire-home, private-room, shared-room, hotel-room. |
title |
Text the listing title must contain, up to 100 characters. |
Adjusting the comps¶
The comps are the similar listings the app suggests by default, returned as
compset.airbnb-ids. To change them, call again with exclude-airbnb-ids
(e.g. ids from the previous response) or include-airbnb-ids. An id in both
lists is excluded.
At least 5 of the final comps need priced nights in the window. Fewer is a
422 with code insufficient_compset: loosen the filters, exclude fewer
listings or include more.
Stricter rate limit
Each call can fetch the Airbnb listing or geocode the address, run the similar-listings model and several analytics queries. It carries a stricter limit of 10 requests/minute, on top of the application-level limit. See Rate Limiting.
Response¶
201 Created
{
"data": {
"type": "projections",
"id": "4821",
"attributes": {
"currency": "USD",
"window": {"start-date": "2025-11-02", "end-date": "2026-11-02"},
"subject": {
"airbnb-id": "53906452",
"full-address": "Seagrove Beach, Florida, United States",
"latitude": 30.3191,
"longitude": -86.1197,
"bedrooms": 3,
"bathrooms": 2.5,
"property-type": "entire-home",
"market-percentile": "p75",
"sources": {
"bedrooms": "airbnb-listing",
"bathrooms": "request",
"property-type": "airbnb-listing",
"market-percentile": "airbnb-listing"
}
},
"compset": {
"airbnb-ids": ["18546233", "22931870", "30871442", "37725519", "44108236", "49920817", "51137264", "52088913"],
"priced-listing-count": 8
},
"projection": {
"p25": {"occupancy": 48.0, "adr": 150, "nights": 176, "revenue": 26280},
"p50": {"occupancy": 62.0, "adr": 180, "nights": 227, "revenue": 40734},
"p75": {"occupancy": 74.0, "adr": 225, "nights": 271, "revenue": 60772}
}
}
}
}
| Field | Description |
|---|---|
currency |
ISO code of adr and revenue. |
window |
The trailing year of stays the comps were measured over. |
subject |
The property as resolved: its Airbnb id (null for an address), address, coordinates and the details the comps were matched on. |
subject.sources |
Per detail: request (you sent it), airbnb-listing (from the Airbnb listing), market (the most common tier in the area) or null (unknown, not matched on). |
compset.airbnb-ids |
The comps used, after your exclusions and inclusions. |
compset.priced-listing-count |
How many of them had priced nights in the window and so count towards the percentiles. |
projection.p25 / p50 / p75 |
The 25th, 50th and 75th percentile scenarios. |
Each scenario:
| Field | Description |
|---|---|
occupancy |
Booked nights over all nights, as a percentage. |
adr |
Average daily rate in currency, in whole major units. |
nights |
Booked nights per year. |
revenue |
Gross annual revenue in currency: ADR × occupancy × 365, before fees. It is computed before adr and nights are rounded, so it can differ slightly from adr × nights. |
Percentiles are taken per metric
Occupancy and ADR are percentiles of the comps taken separately, and
revenue multiplies them, matching the app's projection. p75.revenue is
the revenue at p75 ADR and p75 occupancy, not the 75th percentile of the
comps' revenue, so the p25-p75 revenue range is wider than the comps'
actual revenue spread.
Use cases¶
- Owner acquisition. Show a prospective owner what their property could earn from its Airbnb URL or address, before it is connected to Beyond.
- Underwriting. Estimate revenue for a property you are considering buying
or managing, with
p25as the conservative case. - Scenario modelling. Use
occupancyandadrseparately to apply your own assumptions, e.g. your management's typical occupancy uplift. - Comp review. Drop comps that are not comparable with
exclude-airbnb-idsand call again until the set matches your judgement.
Errors¶
| Status | Cause |
|---|---|
400 |
Invalid attributes: neither or both of airbnb-url and address, an address without bedrooms, a URL that is not an Airbnb listing, an unsupported currency, or an invalid filter. |
401 |
Missing or invalid bearer token. |
403 |
Token does not have the projections:write scope or is not bound to a user, or the user's plan does not include Owner Hub projections. |
422 |
The property could not be resolved, or has too few comps. See below. |
429 |
Rate limit exceeded, or the user's Owner Hub tokens are used up until Retry-After. |
503 |
A dependency failed or timed out. Nothing was saved or charged; retry with backoff. |
| Status | code |
Meaning |
|---|---|---|
422 |
property_not_resolved |
The Airbnb listing or the address could not be found. |
422 |
no_similar_listings |
No similar Airbnb listings around the property. |
422 |
insufficient_compset |
Fewer than 5 comps with priced nights. Loosen the filters or adjust the ids. |
503 |
listing_unavailable |
The Airbnb listing could not be fetched right now. |
503 |
geocoding_unavailable |
The address could not be geocoded right now. |
503 |
similar_listings_unavailable |
The similar listings could not be computed in time. |
503 |
projection_unavailable |
The percentile query failed. |