Calendar¶
| Endpoint | Method | Scope | Description |
|---|---|---|---|
/api/v1/listings/<id>/calendar/ |
GET | reservations:read |
Get calendar data for a listing |
Try it out
Explore parameters, schemas, and live requests in the Swagger UI.
Get Listing Calendar¶
Returns daily calendar data for a listing, including pricing, availability, and pricing factors — one entry per date in the requested range.
Calendar price fields are returned in the listing's own currency — the one the
listing resource reports as currency. It is not the owner's
billing currency, which is what market insights rates are priced
in.
Request¶
GET /api/v1/listings/123/calendar/
?filter[start-date]=2026-03-10
&filter[end-date]=2026-03-11
&sort=date
| Parameter | Description |
|---|---|
filter[start-date] |
First date (YYYY-MM-DD). Defaults to today in the listing's timezone. |
filter[end-date] |
Last date (YYYY-MM-DD). Defaults to today + 365 days. Must not be before filter[start-date]. |
sort |
date (ascending, default) or -date. Any other field is a 400. |
fields[calendar-entries] |
JSON:API sparse fieldset — return only the named attributes. |
page[number], page[size] |
Standard pagination. Default page size 366, maximum 731. See below. |
Pagination¶
This endpoint's page size is much larger than the API-wide default, so a normal date range arrives in a single response:
| Parameter | Default | Maximum |
|---|---|---|
page[size] |
366 | 731 |
The default 365-day window therefore returns one page. Do not paginate it into 25-entry pages — that would turn one request into fifteen and, across a portfolio, run you into rate limits.
The filter names are filter[start-date] and filter[end-date]
This endpoint reads those two parameters by exact name. Any other
filter[…] key — filter[start] and filter[end] included — is ignored
without an error, and you get the default today → today + 365 window back
with a 200. If the dates in the response are not the ones you asked for,
check the parameter names.
Response¶
{
"data": [
{
"type": "calendar-entries",
"id": "2026-03-10",
"attributes": {
"date": "2026-03-10",
"availability": "available",
"price": 101,
"price-posted": 100,
"effective-min-price": 80,
"effective-max-price": 250,
"price-override-type": null,
"factors": [
{
"key": "seasonality",
"order": 1,
"ratio": 0.12,
"amount": 11
},
{
"key": "event",
"order": 2,
"ratio": 0.05,
"amount": 5,
"reason": "City Marathon",
"event-impact": "high"
},
{
"key": "feature_occupancy_premium",
"order": 3,
"ratio": 0.03,
"amount": 3,
"metadata": {
"listing-occupancy": 45.45,
"market-occupancy": 27.32
}
}
]
}
}
],
"meta": {
"pagination": {"page": 1, "pages": 1, "count": 1}
}
}
An entry's id is its date string, so type + id is unique per listing
request.
Availability Values¶
| Value | Description |
|---|---|
available |
The date is available to be booked |
booked |
The date has a corresponding reservation |
blocked |
The date is blocked off for a hold |
unavailable |
The date cannot be booked for an unknown reason |
Price Fields¶
All price fields are integers in whole currency units: a price of 189
on a USD listing is $189 per night. Values are never expressed in cents —
do not divide by 100.
| Field | Description |
|---|---|
price |
Beyond Pricing suggested price (shown in the BP dashboard) |
price-posted |
Last price posted to the channel/PMS (null when never posted) |
effective-min-price |
Effective minimum price floor; the modeled price will not go below this |
effective-max-price |
Effective maximum price ceiling; the modeled price will not exceed this (null when unset) |
price-override-type |
Which manual override, if any, produced price (null when the price is purely modeled) |
Tip
Use price-posted when you need the price that will actually be quoted if a booking is made. Fall back to price if price-posted is null.
Manual Override State¶
price-override-type tells you whether someone has taken over pricing for a date:
| Value | Meaning |
|---|---|
null |
No manual override. price is Beyond's modeled price and moves as the model updates. |
fixed |
A fixed price is pinned to the date. price is that exact amount and Beyond will not reprice the date. |
percentage |
A percentage adjustment is applied on top of the modeled price. price still moves as the model updates. |
The override values — and the ability to set or clear them — live on the manual overrides customization.
Pricing Factors¶
Each calendar entry includes a factors array describing the individual components that shaped the modeled price (e.g. seasonality, day-of-week, events).
| Field | Description |
|---|---|
key |
Factor identifier (e.g. seasonality, dow, event) |
order |
Display/application order of the factor |
ratio |
Factor's contribution as a ratio (e.g. -0.19 means -19%) |
amount |
Factor's contribution to the modeled price, in the same whole currency units as price (e.g. -19 lowered the price by $19 on a USD listing) |
reason |
Human-readable reason for the factor (e.g. event name); omitted for factors that carry none, and null for an event with no name |
event-impact |
Event impact level (low, medium, high); only present for event factors |
metadata |
Extra context specific to the factor's type; only present for the factors listed below |
Note
amount values do not necessarily sum to (price − base price). Minimum/maximum price limits and final rounding are applied between factors and are not attributed to any single factor, and factors whose effect rounds to less than 1 report 0.
Factor Metadata¶
A few factor types carry extra context in a metadata object explaining what drove them. Each key belongs to one factor type and is absent for all the others, so read metadata against the factor's key rather than assuming a fixed shape.
| Key | Present on | Description |
|---|---|---|
listing-occupancy |
feature_occupancy_premium, feature_occupancy_discount |
The listing's occupancy over the factor's date range, as a percentage (e.g. 45.45 means 45.45%). null when Beyond has no reading for the listing |
market-occupancy |
feature_occupancy_premium, feature_occupancy_discount |
The listing's market's occupancy over the same range, as a percentage. null when Beyond has no reading for the market |
bedroom |
pacing_premium, pacing_discount |
Bedroom count the pacing factor was looked up under |
bad-review-date |
reputation_discount |
Date of the review that triggered the discount (YYYY-MM-DD), once the triggering review is known |
Together these let you explain a factor rather than just report it — for example, a feature_occupancy_premium with listing-occupancy: 45.45 and market-occupancy: 27.32 raised the price because the listing is filling faster than its market.
Use cases¶
- Price display. Show
price-posted(falling back toprice) beside your PMS rates to confirm what Beyond is publishing for each date. - Price explanation. Render the
factorsarray — withreason,event-impact, andmetadata— to explain why a date is priced the way it is, instead of just reporting the number. - Override audit. Scan a range for
price-override-type != nullto find dates where a person has taken over pricing, then read the values from the manual overrides customization. - Floor/ceiling checks. Compare
pricewitheffective-min-price/effective-max-priceto see which dates are pinned to a limit rather than floating with demand.
Errors¶
| Status | Cause |
|---|---|
400 |
Listing not yet clustered (see below); invalid dates (end before start, malformed YYYY-MM-DD); invalid sort field; insufficient cluster data. |
401 |
Missing or invalid bearer token. |
403 |
Token does not have the reservations:read scope. |
404 |
No listing with that id is visible to your application, or the listing has no active channel listing. |
429 |
Rate limit exceeded. See Rate Limiting. |
Not Yet Clustered (400)¶
A listing that has no pricing cluster assigned cannot serve its calendar: the
endpoint returns a 400 with a "not yet clustered" message. Most listings are
clustered as part of the initial import, so this is uncommon — it happens when
a listing was created without a cluster, or lost its cluster later. The
listing resource surfaces
this state through in-active-market, which is false while the listing has
no cluster (and true only when the calendar is available). The
listing.in_active_market_changed
webhook tells you when a priced listing's in-active-market changes in
either direction — no polling needed.