Travelpass
A hotel marketplace that pulls the same room from a dozen suppliers, works out which listings are actually the same room, and shows the traveller the one price that wins.

The problem
Every supplier sells the same room under a different name, and none of them agree on what it costs.
A single hotel in Las Vegas might reach the marketplace through eight channels: a global API, three wholesalers, a direct chain connection, and a handful of contracted rate feeds. One calls the room Superior King. Another calls it 1 King Bed, Non-Smoking, Superior. A third calls it Deluxe King City View. Two of those are the same physical room. One isn't, and it's the cheapest.
That is the entire business problem in one paragraph. Pulling more supply is easy and everyone does it — what's hard is knowing what you've actually pulled. Show a traveller the lowest number without resolving it and you either sell them a worse room than they thought they booked, or you bury a genuinely better rate under a listing nobody recognised as the same thing. Both destroy the only reason a marketplace exists, which is that the traveller trusts it to have already done the comparison.
Prices made it worse. Suppliers quote differently — some pre-tax, some with resort fees excluded, some with a cancellation policy that makes a $12 saving a bad trade. A number cannot be compared until it has been made comparable, and a rate that was correct four minutes ago may not survive to checkout.
What I built
A pipeline that collapses many suppliers into one honest set of choices.
I built the system end to end: the supplier ingestion layer, the room-type matching and rate normalisation pipeline, the search and results experience, the member rate gating, and the booking flow through to confirmation and agent handoff.
Availability requests fan out to every connected supplier in parallel and come back as a mess of overlapping, differently-named, differently-priced offers. The pipeline resolves those into canonical room records — one entry per real room, with every supplier's offer attached underneath it — then normalises each offer into a true all-in cost with its cancellation terms and inclusions made explicit. What the traveller sees is a short list of rooms they can actually tell apart, each showing the best qualifying rate, with the alternatives available rather than hidden.
- Multi-supplier ingestion across API, wholesaler and direct-chain feeds, normalised into one property and rate schema
- Room-type matching engine resolving supplier listings to canonical rooms
- Rate normalisation: taxes, resort fees, inclusions and cancellation terms rendered comparable
- Member-gated private rates with contractual display rules enforced at the API layer
- Search and results experience built for comparison rather than volume
- Booking flow with rate revalidation before capture, and handoff to the 24/7 agent team
- Supplier performance monitoring: fill rate, latency, and booking failure rate per channel
Room-type matching
Deciding two listings are the same room is the whole product.
Supplier room names are free text written by different people over twenty years, so string similarity alone fails immediately — Superior King and Deluxe King are one character apart in spirit and two different rooms in practice, while 1 King Bed NS Superior and Superior King Non-Smoking are identical and share almost no tokens.
Matching ran on structured signals instead of names: bed configuration, occupancy, smoking status, view, and floor or wing designators parsed out of the description, then scored against the property's canonical room inventory. Confident matches merged automatically. Ambiguous ones stayed separate rather than guessing, because a wrong merge is a traveller arriving to a room they didn't book, while a missed merge is only a slightly longer results page. That asymmetry set the threshold — the system was tuned to under-merge.
System design
One room, many offers, one number that means something.
The core design decision was that the canonical room is the unit of the marketplace, not the supplier offer. Search results, comparison, and the booking flow all operate on resolved rooms, with supplier offers as attributes underneath — which is the inverse of how aggregated inventory usually surfaces, where each feed becomes its own row and the traveller does the reconciliation.
That structure is what let the marketplace make a claim it could stand behind. Showing the best rate for a room is only meaningful if the system knows which offers are competing for the same thing, and normalising to all-in cost is only meaningful if the comparison set is right. Both depend on resolution happening first.
It also contained the contractual complexity. Private and closed-user-group rates carry display restrictions that vary by supplier, and enforcing those at the offer level under a canonical room meant a gated rate could be selectively revealed to a signed-in member without restructuring the results page or leaking into public listings.
A price isn't a price until you know what it includes and what room it's for.
Reliability and rollout
The failure that matters happens after the traveller has decided.
Rate freshness was the operational risk. Cached availability makes search fast and makes checkout wrong, so rates were revalidated against the originating supplier immediately before payment, and a rate that had moved surfaced the change before capture rather than after. A booking that fails at the last step, or silently confirms at a different price, costs more trust than a slower search ever would.
Supplier behaviour drove the rest. Channels fail unevenly — one goes slow, one returns stale inventory, one rejects bookings it advertised — so each was monitored independently on latency, fill rate, and booking failure, and a degraded supplier was dropped from the fan-out rather than allowed to hold up every result. Search returned on a deadline with whatever had answered, because a page that renders in time with nine suppliers beats a complete page nobody waited for.
Everything ended at a human when it needed to. Complex itineraries, contract rates, and failed bookings handed off to the agent team with the full offer history attached, so a traveller calling in reached someone who could see exactly what they had been shown and why.
Built with
- React
- Next.js
- TypeScript
- Node.js
- Backend Development
- Hotel Booking API Integration
- PostgreSQL
- Database Engineering
- Search & Recommendation Systems
- Maps/Geospatial API Development
- Payment & Booking Systems
- Social/User-Generated Content Development