Introduction
You type in "three nights from August 1st, six guests" and the site immediately tells you whether it's free and what the total comes to. On a booking site that feels completely ordinary — but behind the scenes, every one of those lookups fires a request at the channel manager.
This article covers the traps we actually fell into while building the availability and rate display, and how we got out of them. Lodging rates are far more complicated than they look, and if you build the obvious thing, the numbers will always end up wrong.
Never Calculate the Rate Yourself
Summing Daily Prices Will Always Be Wrong
The first version I built fetched daily rates and added them up. At ¥190,000 a night, three nights should be ¥570,000. Except the figure the channel manager actually quoted was nothing like that.
Extra-guest fees, multi-night discounts, and minimum-stay rules never make it in, so the number drifts from the real charge
The system calculates the total for "these dates, this party size" and hands it back. There's nothing left to get wrong
Lodging rates stack rule on top of rule: surcharges by party size, discounts for longer stays, seasonal pricing, minimum-stay requirements. All of that is configured inside the channel manager, and you simply cannot reproduce it from the outside by looking at daily rates.
So we switched to a quote API — hand it the stay dates and the party size, get the total back — and adopted the rule that we never do any of the arithmetic ourselves. The per-night figures shown on the calendar are indicative only and are never used for billing.
Count Guests the Same Way Everywhere
This property has a rate rule where "up to two children aged five and under stay free when sharing a bed." Which means what the screen calls "4 adults + 2 children" has to be treated as "4 guests" for pricing purposes.
The tricky part is that this conversion has to happen both when displaying and when confirming the booking. Fix only one of them and the screen says ¥570,000 while confirmation comes out at ¥600,000 — and the booking dies with a "the amount has changed" error.
So we pulled the guest-count conversion into a single function that both paths call. Don't write the same calculation in two places. It sounds obvious, but it's the kind of thing you usually only notice after something has already gone wrong.
Don't Turn "Couldn't Check" Into "Sold Out"
Four States, Not Two
Showing availability isn't a simple choice between "free" and "sold out". In practice we distinguish four states.
The most important one is the last: never, ever display "couldn't check" as "sold out". Show sold out because of a connection error and a guest gives up on dates that were actually free. Don't translate an error into an inventory status. That principle applies to plenty of other external integrations too.
When It's Sold Out, Offer an Alternative
Just saying "sold out" sends the guest straight back out the door. So when we detect a sold-out result, we automatically look for open stretches on either side of those dates and suggest them: "these dates are available."
Don't build dead ends. On a booking site, that alone moves conversion.
Living With the Cache
Cache for Exactly 60 Seconds
When the same dates get searched over and over, hitting the API each time is wasteful and pushes you toward the rate limit. So we cache search results for 60 seconds.
Lodging inventory doesn't move second by second, so information that's up to a minute stale is fine in practice. That said, right before a booking is confirmed we always bypass the cache and re-fetch. The display can be slightly stale; the moment money moves, it has to be current. That's the line we drew.
Never Forget to Clear It
The scariest thing about a cache is stale data hanging around after inventory has moved. We actually hit a bug where the screen kept saying "sold out" right after a guest came back from the payment page. Placing the hold had moved inventory, but the cache hadn't been updated.
Put cache invalidation where inventory moves
Copying the cache-clearing call into every caller guarantees you'll forget one eventually. We built the invalidation into the operations that actually move inventory — creating and releasing holds — so there's no room left to forget.
Inventory drops by one, so the cache is dropped immediately
Inventory comes back, so the cache is dropped immediately
It might be an OTA booking, so drop the cache regardless of the contents
Drop the cache if the change affects inventory
Wrapping Up
Three things mattered most in getting availability and rates on screen.
- Don't calculate rates yourself — leave it to the API that returns a total, and keep daily prices as rough guidance only
- Don't translate errors into "sold out" — distinguish four states and be honest when you can't check
- Clear the cache inside the code that moves inventory — don't leave it to the caller; make forgetting structurally impossible
Holding inventory at the moment a booking is confirmed is covered in "A Booking Registration Flow That Prevents Double-Booking".
A Booking Registration Flow That Prevents Double-Booking
How a hold with a built-in availability re-check prevents accidents, and why order matters.
Reliable Sync with Webhooks Plus Hourly Reconciliation
How we detect changes made outside the site and keep up with them.
Sync Design Between a Channel Manager (Beds24) and Our Own Site
For the whole sync architecture, start with this hub article.