Skip to main content

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.

ParameterRequiredDescription
kindYesThe LOS price series to return.
changed_sinceNoAn ISO 8601 timestamp for a delta sync. It cannot be older than one month. Omit it for a full sync.
cursorNoThe 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:

IndexTypeDescription
0stringStart date in YYYY-MM-DD format.
1integerMinimum occupancy for the price bucket.
2integerMaximum occupancy for the price bucket.
3string arrayThirty 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-records with changed_since set 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_since for 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:

HeaderValue
Content-Typeapplication/json
User-AgentBookingSync-Webhook/1.0
X-Webhook-TimestampUnix timestamp in seconds.
X-Webhook-Signaturesha256=<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:

WindowLimit
Five minutesfull_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 send Accept-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_since with 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 kind together, 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.