Live LOS Records API
Use the Live LOS Records API to synchronize length-of-stay (LOS) prices as JSON. Each record contains the prices for consecutive stay lengths for a rental, day, and occupancy range.
The Live LOS Records API is the long-term replacement for CSV-based price imports. Because it calculates prices when requested, it provides the lowest-latency price data. Prefer it over the LOS export URLs endpoint when it is available for your integration.
Before you begin
Send your access token with every request:
Authorization: Bearer <access_token>
Live LOS records cover a rolling 18-month window beginning yesterday. The kind parameter is required on both endpoints and accepts:
rental_price_before_special_offers— rental price before special-offer discounts.rental_price— rental price after discounts, excluding required fees and taxes.final_price— rental price including required fees and taxes.
Use final_price unless you calculate fees and taxes yourself. Always use a quote to confirm price and availability before creating a booking.
Endpoints
Synchronize all rentals
GET /api/ota/v1/live-los-records returns a cursor-paginated set of all rentals available to your application.
| Parameter | Required | Description |
|---|---|---|
kind | Yes | The LOS price series to return. |
changed_since | No | An ISO 8601 timestamp for a delta sync. It cannot be older than one month. Omit it for a full sync. |
cursor | No | The opaque value from meta.next_cursor on the previous response. Keep it paired with the same kind. |
Use this endpoint for the initial and periodic price synchronizations. For a scheduled sync that starts more than two days after the previous successful sync, run a full sync without changed_since.
Start a full sync:
curl --compressed "$API_URL/api/ota/v1/live-los-records?kind=final_price" \
-H "Authorization: Bearer $TOKEN"
Start a delta sync:
curl --compressed "$API_URL/api/ota/v1/live-los-records?kind=final_price&changed_since=2026-08-13T08:00:00Z" \
-H "Authorization: Bearer $TOKEN"
Continue the same sync until meta.has_more is false:
curl --compressed "$API_URL/api/ota/v1/live-los-records?kind=final_price&cursor=$CURSOR" \
-H "Authorization: Bearer $TOKEN"
The first request fixes the rental set for that synchronization run. A rental is never split across pages, although the number of rentals per page can vary with the amount of LOS data. Prices are calculated when each page is requested, so treat the cursor as a stable rental-set snapshot, not a price-value snapshot.
Synchronize one rental
GET /api/ota/v1/rentals/{rental_id}/live-los-records returns the LOS records for one rental in a single response. Use the rental's synced ID from GET /api/ota/v1/rentals.
It accepts the same kind and optional changed_since parameters as the all-rentals endpoint. It does not accept cursor; it returns meta.has_more: false and meta.next_cursor: null.
curl --compressed "$API_URL/api/ota/v1/rentals/$RENTAL_ID/live-los-records?kind=final_price" \
-H "Authorization: Bearer $TOKEN"
Use this endpoint to refresh a newly available rental, inspect one rental while debugging, or handle a small set of rentals named in a webhook. For initial and periodic synchronization, use the all-rentals endpoint.
Response format
Both endpoints return the same shape:
{
"data": [
{
"account_id": 200,
"rental_id": 500,
"currency": "USD",
"rows": [
["2026-08-13", 2, 4, [
"0.00", "0.00", "128.00", "136.00", "144.00", "152.00", "160.00", "168.00", "176.00", "184.00",
"192.00", "200.00", "208.00", "216.00", "224.00", "232.00", "240.00", "248.00", "256.00", "264.00",
"272.00", "280.00", "288.00", "296.00", "304.00", "312.00", "320.00", "328.00", "336.00", "344.00"
]]
]
}
],
"meta": {
"has_more": true,
"next_cursor": "eyJsYXN0X3JlbnRhbF9pZCI6NTAxLCJzeW5jX2pvYl9pZCI6NDF9"
}
}
Each entry in rows has this positional structure:
| Index | Type | Description |
|---|---|---|
0 | string | Start date in YYYY-MM-DD format. |
1 | integer | Minimum occupancy for the price bucket. |
2 | integer | Maximum occupancy for the price bucket. |
3 | string array | Thirty prices, starting at a one-night stay. Index 0 is one night, index 1 is two nights, and so on. Amounts are decimal strings in the rental currency. A zero price means that stay length is unavailable. |
rows may be empty. A delta is granular to a day: when a change affects any occupancy bucket on a day, the response includes all occupancy rows for that day. Do not assume that every returned price is numerically different from the price you previously stored.
Webhooks
To enable los_records.refreshed webhooks, provide an HTTPS callback URL to your Smily integration contact. Smily will provide the signing secret.
Webhooks are currently coalesced every four minutes to avoid sending excessive notifications; this interval may change. One event can include up to 250 rental IDs. The payload is a signal that LOS records changed; it does not include the updated prices.
{
"id": "42",
"type": "los_records.refreshed",
"rental_ids": [500, 501]
}
Store and deduplicate the event id, and make the subsequent updates idempotent. A delivery can be retried, and a rental can appear in more than one event.
Choose a synchronization strategy
- Per-rental refresh: Fetch the rental IDs from a webhook individually when the event contains a small set of rentals. This is also useful for a newly available rental and for debugging a specific rental.
- All-rentals delta sync: Treat the webhook as a signal to run
GET /api/ota/v1/live-los-recordswithchanged_sinceset to the start time of the last successful sync. This batched strategy is appropriate for integrations of every size and is preferred for regular periodic synchronization. - All-rentals full sync: Omit
changed_sincefor the initial import or when more than two days have passed since the last successful sync.
Verify webhook requests
Every webhook request has these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | BookingSync-Webhook/1.0 |
X-Webhook-Timestamp | Unix timestamp in seconds. |
X-Webhook-Signature | sha256=<hex HMAC> |
Calculate the expected signature with HMAC-SHA256 using your signing secret and the exact, unmodified request body:
HMAC_SHA256(signing_secret, "<timestamp>.<raw request body>")
Compare the result to the signature in constant time. Use the event id to protect against replay while allowing delivery retries. Respond with any 2xx status only after the event has been durably accepted.
Smily retries connection errors, timeouts, 408, 429, and 5xx responses with exponential backoff, starting at two minutes and capped at one hour, for up to 24 hours. Redirects and other 4xx responses are not retried.
Rate limits
Request limits apply per application and are shared by both Live LOS Records endpoints. They depend on the number of rentals available to the application.
Let full_sync_requests be ceil(available_rentals / 3). The API allows:
| Window | Limit |
|---|---|
| Five minutes | full_sync_requests + 10 requests |
| One hour | (full_sync_requests × 6) + 120 requests |
This reserves enough capacity for up to six full synchronizations per hour plus frequent incremental refreshes. Handle 429 Too Many Requests by waiting for the Retry-After value before sending another request. Do not retry immediately or increase concurrency after a 429. If you reach the limit, or expect to need a higher limit, contact the Smily team.
Best practices
- Use
curl --compressed, or sendAccept-Encoding: gzip, for both endpoints to reduce response size and transfer time. - Run a full sync for the first import, whenever your last successful delta sync is more than one month old, or when more than two days have passed since the last successful sync. Otherwise, for incremental updates, send
changed_sincewith the start time of the previous sync, not its completion time. Reprocessing a small overlap is safer than missing changes made while you paginated. - Persist a sync only after every page has been processed successfully. Keep the cursor and
kindtogether, and restart the sync if the cursor is invalid. - For a delta, replace only the dates and occupancy rows returned for each rental and price kind; preserve dates that are omitted from the response. A full sync can replace the entire current price horizon, while historical data should remain intact.
- Use webhooks as a trigger for targeted refreshes or an all-rentals delta sync, but keep a scheduled all-rentals sync as reconciliation. Webhook delivery is asynchronous and coalesced.
- Validate price and availability with the Quotes API immediately before booking. LOS prices are for search and display, not the final booking guarantee.