OTA dominance over small accommodation providers comes from three compounding advantages: a decade of sustained ad spend that built search presence before most small properties had websites, review volumes accumulated over tens of thousands of transactions, and full-time optimization staff running daily. None of that closes in a season. Commission rates that most people outside the industry underestimate per booking are the visible cost; the structural cost is that every OTA booking reinforces the OTA’s data flywheel, not the property’s own brand.
The nine B&Bs I work with on the Hengchun peninsula had the same gap: websites with no search visibility, ad accounts with no conversion signal, GA4 dashboards that owners logged into but couldn’t act on. Solving any one of those in isolation doesn’t move the needle. A site that ranks starts generating behavioral data the ads system needs to learn from. Ad data without a read loop never adjusts bids. A weekly report no one can interpret doesn’t generate decisions. The unit of work was the stack, not the components.
The load-bearing work that never appears in a deliverable
The sites are built in Astro — bilingual Chinese and English, room listings with seasonal pricing, local activity guides, and direct contact paths through LINE, phone, and WeChat. An admin layer lets owners swap seasonal banners and manage photo galleries without touching code, which matters when a busy-season shift happens in two weeks and the owner is already fully booked.
The hours that determined whether everything downstream would work were in performance. Getting Lighthouse mobile scores above the threshold where Google begins suppressing search rankings. Eliminating cumulative layout shift on sites where owners had uploaded full-resolution event banners as room thumbnails. Rewriting image delivery so a hero photo doesn’t cost a mobile visitor 65 MB over 4G. None of this surfaces in a client deliverable, but a site scoring 48 on Lighthouse mobile has no traffic to convert — the conversion problem doesn’t exist yet.
Every site connects to GA4, with Search Console and Google Ads conversion tracking layered on top. The one implementation point that determines whether everything downstream works: click-proxy events — phone call CTAs, WeChat initiation buttons, book-now links — must fire before the visitor is handed off to the external channel. Fire it after the handoff and GA4 misses the signal entirely. Without that signal, the Ads system optimizes against empty space. This is where half the setups I’ve audited break. The fix is two lines of JavaScript, but only once you know the exact firing order.
A four-stage pipeline with an anti-fabrication gate
The weekly reporting engine is built in TypeScript and runs on GitHub Actions on a Monday cron covering three properties. The pipeline has four stages: fetch pulls GA4 and Search Console data for the current and prior week into a structured JSON; analyze passes that JSON to an LLM to extract structured insights; render writes a Markdown report; verify checks every number in the draft against source data.
The verify stage is where the interesting design decision lives. The model generates prose — section headers, diagnostic framing, recommended actions — but never numbers. Every figure in the final report is computed deterministically from GA4 API responses in the fetch stage. The verify pass reads the rendered Markdown, extracts every number, and traces each one back to the source JSON within a small tolerance. Any number it can’t trace causes the stage to fail. The model also gets a list of forbidden phrases — consultant jargon that sounds analytical but means nothing operationally — and any match is a rejection.
When verify fails, it writes a structured failures snapshot. On the next attempt, render reads that snapshot and enters fix mode: the LLM receives both its previous draft and a structured description of what failed, then rewrites only the failing sections. The pipeline allows up to five attempts before aborting the run. In practice, a first-draft failure plus one fix pass converges reliably; the five-attempt ceiling is a hedge against edge cases where the fix introduces new orphan numbers.
This architecture means the operator receives a report they can forward directly without a human review step. Remove the constraint that the model can’t write numbers, and you get reports with plausible but wrong figures — which is worse than no report, because the operator acts on them.
Per-site context and a two-tier model architecture
Before any analysis runs, the engine assembles a site-specific context for each property: peak season windows, traveler profile, what a meaningful conversion looks like at that specific operation, and interpretation rules that modify how the same GA4 signals should be read. A family guesthouse one kilometer from Kenting’s main beach and a diving-focused guesthouse near Houbihu interpret the same session data differently, and the prompt has to know which is which before Stage B starts.
The weekly report runs on Gemini 2.5 Pro. The Ads health check — a separate, on-demand pipeline that audits campaign structure, keyword quality, spend waste, and Final URL coverage — runs the same four-stage architecture but with a model choice at dispatch time: standard tier uses Gemini 2.5 Pro, premium tier upgrades to Claude Sonnet for deeper reasoning on accounts with complex campaign structures or prolonged data anomalies. The health check is triggered manually rather than on a fixed cron, which means it runs when there’s a specific question about account performance rather than on a calendar schedule.
The four sites covered by the ads health pipeline span nine B&Bs — several properties share a single Google Ads manager account, so each audit traverses multiple sub-accounts within the same run.
Why this service category didn’t exist before
Website build. Analytics instrumentation. Ads management. Weekly reporting per property. In the traditional service model, those are four separate labor functions with separate retainers. Combined, they’re priced out of reach for a small B&B running on direct-booking margins. So properties went without, kept paying OTA commission on every booking, and never accumulated the behavioral data that would let them compete differently. That wasn’t a market failure — it was a margin structure constraint.
AI collapsed three of those labor functions — context-aware summary writing, ad account diagnostics in plain language, and translating analytics data into decisions a non-technical owner can execute — into a system running at single-digit dollars per property per week. The nine B&Bs I work with aren’t receiving a cheaper version of a service that existed before. They’re receiving a service category that had no viable price point for this type of client.
The floor moved. That’s the only accurate framing.