Core concepts
The unified schema
Every platform normalizes to the same shapes: Property, Availability, Price, PriceCompare and Review. One data model to code against, four platforms behind it.
Property
Returned by search and listing. The composite id is stable and case-sensitive.
{
"id": "stays_vrbo_5663861ha", // composite: stays_<platform>_<platformListingId>, case-sensitive
"platform": "vrbo",
"platformListingId": "5663861ha", // verbatim from the source platform
"url": "https://www.vrbo.com/…",
"name": "Awesome home in Pirovac with swimming pool",
"propertyType": "house", // hotel | apartment | house | villa | cottage | other
"location": { "lat": 43.82, "lng": 15.58, "city": "Murter", "region": "Sibenik-Knin", "country": "HR" },
"starRating": null, // hotel class; null for short-term rentals
"guestRating": 9.1, "ratingScale": 10, "reviewCount": 142,
"maxOccupancy": 8, "bedrooms": 3, "bathrooms": 2,
"amenities": ["pool", "kitchen", "air_conditioning"],
"images": ["https://…"], // source URLs — ScoutingAPI never rehosts photos
"host": { "name": "…", "isSuperhost": false },
"price": { /* embedded Price when dates supplied; else absent */ }
}Composite id is verbatim & case-sensitive
id = stays_<platform>_<platformListingId>. The listing-id segment is preserved exactly from the source — never re-cased. Two ids differing only in case are different properties.Availability
One entry per requested listing; each day reports availability, min-nights and whether check-in / check-out / booking is allowed.
{
"platform": "airbnb",
"listingId": "42307961",
"dates": [
{ "date": "2026-07-13", "available": true, "minNights": 7, "checkIn": true, "checkOut": false, "bookable": true }
]
}Price
A real numeric quote for specific dates and occupancy. Fees are null when the source doesn’t itemize them.
{
"platform": "booking",
"listingId": "abramovic2",
"currency": "EUR",
"nightlyPrice": 303,
"totalPrice": 2122,
"fees": { "cleaning": null, "service": null, "taxes": null }, // null when the source doesn't itemize
"nights": 7,
"occupancy": { "adults": 2, "children": 2, "childAges": [8, 13] },
"source": "booking",
"url": "https://…"
}PriceCompare
One property, many OTAs — plus first-class min and median computed by ScoutingAPI across every offer’s total.
{
"property": "Hotel X, Sibenik",
"checkIn": "2026-07-13", "checkOut": "2026-07-20",
"currency": "EUR",
"min": 2122, // ScoutingAPI-computed: lowest offer totalPrice; null if no offers
"median": 2151, // ScoutingAPI-computed: median of offer totalPrices; null if no offers
"offers": [
{ "ota": "booking.com", "totalPrice": 2122, "currency": "EUR", "url": "…" },
{ "ota": "expedia", "totalPrice": 2180, "currency": "EUR", "url": "…" }
]
}Review
{
"platform": "tripadvisor",
"listingId": "d12345",
"reviewId": "r987",
"rating": 5, "ratingScale": 5,
"title": "Perfect family stay",
"text": "…",
"author": "Jane D.", "date": "2026-05-10",
"tripType": "family", "language": "en",
"ownerResponse": "Thank you!",
"liked": "…", "disliked": "…"
}Rating scales are echoed, never rescaled
ratingScale next to a rating. TripAdvisor, Airbnb and Vrbo are on a 5-point scale; Booking.com, Expedia and Hotels.com are on 10.Amenity taxonomy
Every adapter maps a platform’s raw amenity terms onto one controlled, snake_case vocabulary. The amenities[] filter on search accepts only these tokens (an unknown token → 400 invalid_amenity). The set is additive — tolerate unknown tokens.
| Token | Meaning |
|---|---|
pool | Swimming pool |
hot_tub | Hot tub / jacuzzi |
kitchen | Full kitchen |
wifi | Wireless internet |
air_conditioning | Air conditioning |
heating | Heating |
parking_free | Free parking |
parking_paid | Paid parking |
pet_friendly | Pets allowed |
washer | Clothes washer |
dryer | Clothes dryer |
ev_charger | EV charging |
gym | Gym / fitness |
sea_view | Sea / ocean view |
beachfront | Directly on the beach |
balcony | Balcony / terrace |
bbq | BBQ / grill |
workspace | Dedicated workspace |
crib | Crib / infant bed |
elevator | Elevator / lift |
wheelchair_accessible | Step-free access |
smoke_alarm | Smoke alarm |
breakfast | Breakfast available |
spa | Spa / wellness |
garden | Garden / green space |
fireplace | Indoor fireplace |
dishwasher | Dishwasher |
self_check_in | Self check-in |
non_smoking | Non-smoking |
family_friendly | Family friendly |
Airbnb amenity/pool filtering is post-filtered
amenity_post_filtered warning. Booking, Vrbo and Google constrain natively.Currency & localization
Pass-through, never FX
Default currency is USD. The effective currency is always echoed in meta.currency and in each price object. The currency parameter is a hint that triggers actor-side localization where supported; where it can’t localize, ScoutingAPI echoes the source currency unchanged and adds a currency_passthrough warning. No FX conversion is ever performed. On a mixed-currency fan-out, meta.currency echoes the requested currency and each item carries its own.
The unified schema types ship in @scoutingapi/schema and are re-exported by the SDK, so your types never drift from the API.