One row per property. The search modes produce the whole row; the enrichment modes produce the id, the stay and the block you asked for, so join those back on id.
Describes build 1.0.173 of rl1987/booking-api-scraper, deployed 2026-10-29. The column set last changed 2026-10-29. Earlier builds can return a different one.
Modes: search, rooms, details, surroundings
Dataset view: overview — ?view=overview on the items API, or the matching tab in the Console.
One row per property. The search modes produce the whole row; the enrichment modes produce the id, the stay and the block you asked for, so join those back on id.
38 columns, in the order the view shows them.
| # | Column | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | scrapedAt | Scraped at | string, nullable | When the response that produced this row came back - the PRICED response where the row has a price, not when the row was pushed, which is batched and later by seconds to minutes. ISO-8601 UTC, to milliseconds. Note Booking’s own date strings in the same row are NOT ISO (freeCancellationUntil is “2026-10-28 23:59:59 +0000”), so do not point one parser at both. Prices are fetched live - nothing in this Actor is cached or reused - so this is the age of the price, and a dataset read a week later does not refresh it. | 2026-10-27T12:53:42.118Z |
| 2 | scrapedAtUnix | Scraped at (epoch) | integer, nullable | The same instant as scrapedAt, in epoch SECONDS, for consumers that diff timestamps arithmetically. Two keys rather than one selectable format, so a column’s type never moves with a run setting and two datasets always join. | 1793105622 |
| 3 | id | ID | integer | number, nullable | Booking’s own numeric property id, stable across runs and the key to join enrichment rows back onto a search row. | 9176203 |
| 4 | name | Name | string, nullable | Property name on a property row; the attraction name on an attraction row. | AIRINN Vilnius Airport Hotel RENOVATED 2025 |
| 5 | accommodationType | Type | string, nullable | Booking’s own label for the property type. | Hotels, Apartments |
| 6 | accommodationTypeId | Type ID | integer | number, nullable | Booking’s numeric type id; the same vocabulary the accommodationTypes input takes. | 204, 201 |
| 7 | starRating | Stars | integer | number, nullable | Stars, 1-5, or null. It does not say whether the stars are official. Booking awards some properties an official classification and lets others self-estimate one; the mobile surface returns symbol “STARS” for both, so a 3 here may be official or self-assessed. Null is common and legitimate - hostels and many apartments carry no rating. | 3 |
| 8 | reviewScore | Review score | number, nullable | Booking’s guest score, 0-10. | 9.3 |
| 9 | reviewScoreWord | Score word | string, nullable | Booking’s own label for the score. | Fabulous |
| 10 | reviewsCount | Reviews | integer | number, nullable | How many guest reviews the score is based on. | 9657 |
| 11 | address | Address | string, nullable | Street line as the mobile API returns it. The product’s own street address, when Booking publishes one: filled on 5 of 12 rows measured. A tour or transfer usually has no fixed address and returns null; a ticketed venue usually has one. | |
| 12 | city | City | string, nullable | City name. | Vilnius |
| 13 | district | District | string, nullable | Booking’s district for the property. Only populated when Include details is on - it is filled by a backfill from the details surface, not by search. | Fort Kochi |
| 14 | countryCode | Country | string, nullable | Two-letter country code, lowercase. | lt |
| 15 | latitude | Latitude | number, nullable | The property’s own latitude on a property row. On an ATTRACTION row this is the destination’s centroid, not the attraction’s - every attraction in a city returns the same pair. On an attractionDetails row this is the PRODUCT’s own latitude, filled on 6 of 12 rows measured - a tour often has none. The destination centroid is cityLatitude, always present. Coerced to a number: the upstream sends it as a string on some products. | 54.6341 |
| 16 | longitude | Longitude | number, nullable | As latitude: the property’s own on a property row, the destination’s centroid on an attraction row. | 25.2858 |
| 17 | checkin | Check-in | string, nullable | Stay start, yyyy-mm-dd, echoed from the input. | 2026-10-31 |
| 18 | checkout | Check-out | string, nullable | Stay end, yyyy-mm-dd, echoed from the input. | 2026-11-01 |
| 19 | currency | Currency | string, nullable | ISO 4217 code every money figure on the row is in. | EUR |
| 20 | provenance | Provenance | object, nullable | What produced this row’s price: the proxy exit it was fetched through, the generation of that exit, the currency you asked for and the endpoint that priced it. The point of sale follows the proxy exit country, not any input - bookerCountry was removed in this build because nothing read it. | |
| 21 | price | Price | object | number, nullable | On a property row, the price object: base, book/total, per_night and extra_charges. On a rate or attraction row, a plain number. Read total_with_charges to match what the buyer is shown - it is right on every path, which total is not. Where Booking excludes taxes from its display price, total understates: measured on a search row, display EUR 246.28 with EUR 82.50 of excluded charges beside it. Where the taxes are already inside gross_amount, the two are the same number and both match the screen: measured on a room-list rate, MYR 151 on the app’s price sheet against MYR 151.305 on the wire. discount_percent is not Booking’s badge: it is computed from Booking’s own figures and moves with the point of sale, so use it to rank savings within a run rather than to reproduce what a browser shows. | |
| 22 | availability | Availability | object, nullable | Why a property has no price, when it has none: sold out, minimum stay not met (with the nights required), closed, or an unknown id. A property row always comes back even when it cannot be priced. | |
| 23 | freeCancellation | Free cancellation | boolean, nullable | Whether a free-cancellation rate exists for the stay (property row) or the attraction is freely cancellable (attraction row). | |
| 24 | dealBadges | Deal badges | array, nullable | Booking’s own promotional badges for the stay. | |
| 25 | isPreferred | Preferred | boolean, nullable | Booking’s Preferred Partner flag. | |
| 26 | isPreferredPlus | Preferred Plus | boolean, nullable | Booking’s Preferred Plus flag. | |
| 27 | url | URL | string, nullable | Web page for the property or attraction. Populated on search, hotel_ids, rooms and details rows; absent on reviews, reviewScores and surroundings rows, which carry only the id, the stay and what you asked for - join those back on id. | |
| 28 | deep_link_url | Deep link | string, nullable | A booking:// URI that opens the property in the Booking app. Not an http URL, so it will not open in a browser. | |
| 29 | mainPhotoUrl | Photo | string, nullable | One hi-res photo, on every search row, at no extra request or charge. The full gallery is details.photos and needs mode: “details”. | |
| 30 | products | Products | array, nullable | One entry per bookable rate: price, board, cancellation, payment terms, occupancy and how many are left at that price. Needs Include rooms. price.currency says which currency price.total and its siblings are in - read it rather than assuming the row’s, because the two can differ: the total falls back to Booking’s gross_amount_hotel_currency when the ordinary amount is absent, and that one is the property’s own. Four nested keys are read wrong often enough to name here. policies.payment.noPrepayment means “nothing due today”, not “nothing due ever” - true both for settle-at-the-property rates and for ones that take the full amount on a date before arrival; read prepaymentPolicy to tell them apart. amountDueNow is null, not 0, when the rate states no payment stage (198 of 688 sampled blocks), so reading null as 0 turns every one into a false “pays nothing at booking”. dueDate is not the cancellation deadline - it is when payment falls due, and across 48 comparable rates it is always the earlier of the two. policies.meal_plan.plan only describes what is included: offers[] carries the meals that can be bought with the rate (status included / available_for_fee, with the price and its own currency), breakfastAvailableForFee flags the common case, and summary is Booking’s own wording, localised and rounded. | |
| 31 | rooms | Rooms | array, nullable | One entry per room type with availability on your dates - not the property’s whole catalogue, which is usually larger. Occupancy, bed layout, bedrooms, bathrooms, size, photos and child policy. bedCount counts beds, not sleeping places: a bunk counts once and sleeps two, so a “14-Bed Private Room” can report 7. Use maxOccupancy for capacity, and note that is the largest occupancy Booking offers your party rather than the room’s own. areas[] says which room each bed is in for apartment-like units (bedroom, living room, each with its own beds, sleeps and en-suite flag), with hasLivingRoom beside bedroomCount; null on ordinary hotel rooms, which publish no layout. areas[] places beds, it does not inventory rooms: two entries of the same type can describe one room (measured against Booking’s own room summary, counting them overstates living rooms on 2 of 14 units), which is why hasLivingRoom is a flag and not a count. areas[].beds[] and bedLayouts are the same beds from different angles, so summing across both double-counts - bedCount stays the one number to trust. | |
| 32 | paymentTerms | Payment terms | object, nullable | Property-level requirements to book: model, cardRequired, cvcRequired, addressRequired, noCardNeeded, hostedPaymentPageOnly, requestToBookRequired. requestToBookRequired is about INSTANT booking, and it is null on most properties. Booking runs two models: Instant Booking, confirmed on the spot, and Request to Book, where the host has 24 hours to accept and the guest 24 more to finish, with the price held and the card pre-authorised rather than charged. Instant is the default, so the flag only appears on the vacation-rental segment where Request to Book is offered at all, and null there means the question does not apply rather than data missing. | |
| 33 | childAndBedPolicy | Cot & extra bed policy | object, nullable | The property’s cot and extra-bed rules by age band. Needs Include rooms. | |
| 34 | refundabilityPremium | Refundability premium | object, nullable | What a refundable rate costs over the cheapest non-refundable one at the same property. Needs Include rooms. | |
| 35 | details | Details | object, nullable | Static property content: description, fine print, check-in/out times, property type, district, languages, facilities, photos, host and trader information. Needs Include details, and is the whole content of mode: “details”. | |
| 36 | reviews | Reviews | array, nullable | Guest reviews: author, score, pros, cons, date. Needs Include reviews, and the count is capped by reviewsMax in mode reviews or maxReviews on search. | |
| 37 | reviewSubscores | Subscores | array, nullable | Per-category scores - staff, cleanliness, location, value, comfort, facilities, wifi. One request per property however many reviews you fetch. | |
| 38 | reviewSummary | Summary | object, nullable | Booking’s aggregated review summary, including its AI summary where one exists. |
Some columns above carry an object, or a list of objects, with documented members of their own. Each table below lists the members documented at ONE level; a member that is itself an object names the table it continues in, and [] in a path means a list.
row
├─ provenance
├─ price
├─ products[]
│ ├─ policies
│ │ ├─ meal_plan
│ │ │ └─ offers[]
│ │ ├─ payment
│ │ └─ deposit
│ └─ price
├─ rooms[]
│ └─ areas[]
│ └─ beds[]
├─ childAndBedPolicy
│ └─ ageBands[]
└─ details
└─ damageDeposit
provenance — Provenance
Parent chain: row → provenance
What produced this row’s price: the proxy exit it was fetched through, the generation of that exit, the currency you asked for and the endpoint that priced it. The point of sale follows the proxy exit country, not any input - bookerCountry was removed in this build because nothing read it.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | exitCountry | Exit country | string, nullable | ISO country of the proxy exit this row was fetched through, sampled once per proxy generation from Apify’s own request echo. This is the market that priced the row: on the hotel_ids path taxes come back inside the display price from an EU exit and excluded through a US one. Null when the run’s pool rotates per request (a residential proxy with no session), when the probe failed, or past the per-run probe budget - null means not established, not “no market”. Pin country-XX or a session in the proxy configuration to make it exact for every row. | US, GB |
| 2 | proxyGeneration | Proxy generation | integer, nullable | Which exit fetched this row, counted from 0. The retry ladder draws a fresh exit when one fails - six generations in 30 seconds on one measured run - so two rows of one dataset can honestly carry different tax treatment. RUN_SUMMARY.request.exitCountries lists every generation and its country. | 0 |
| 3 | currencyRequested | Currency requested | string, nullable | The currency you asked for, beside the honoured currency. Not recoverable from the row otherwise: HOTEL resolves to the property’s own code, which the row then states as its own currency - so a row quoting MYR could have been asked for as HOTEL or as MYR. | EUR, HOTEL |
| 4 | surface | Surface | string, nullable | The endpoint that produced this row: search (the destination search), roomList (the per-property rate list) or hotelPage (the by-id path). The surfaces disagree in documented ways - roomList carries the structured meal price and hotelPage does not - so a buyer comparing two rows needs to know which answered. | roomList, hotelPage |
price — Price
Parent chain: row → price
On a property row, the price object: base, book/total, per_night and extra_charges. On a rate or attraction row, a plain number. Read total_with_charges to match what the buyer is shown - it is right on every path, which total is not. Where Booking excludes taxes from its display price, total understates: measured on a search row, display EUR 246.28 with EUR 82.50 of excluded charges beside it. Where the taxes are already inside gross_amount, the two are the same number and both match the screen: measured on a room-list rate, MYR 151 on the app’s price sheet against MYR 151.305 on the wire. discount_percent is not Booking’s badge: it is computed from Booking’s own figures and moves with the point of sale, so use it to rank savings within a run rather than to reproduce what a browser shows.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | base | Base | number, nullable | Net rate before any listed charge. | |
| 2 | book | Book | number, nullable | What you pay for the stay, as Booking displays it. | |
| 3 | total | Total | number, nullable | Display price BEFORE the listed extra charges. | |
| 4 | total_with_charges | Total with charges | number, nullable | The all-in amount: total plus everything Booking reports as charged on top. This is the figure that matches the app. | |
| 5 | per_night | Per night | number, nullable | Total divided by nights. | |
| 6 | before_discount | Before discount | number, nullable | Booking’s struck-through price. | |
| 7 | before_discount_with_charges | Before discount, all-in | number, nullable | The struck-through price on the same basis as total_with_charges. | |
| 8 | discount | Discount | number, nullable | Total saving against before_discount. | |
| 9 | discount_percent | Discount % | number, nullable | discount / before_discount from Booking’s own figures. Not the badge the web page shows - the point of sale and the comparator both move it. Use it to rank savings within a run. | |
| 10 | mobile_discount | Mobile discount | number, nullable | The mobile-only cut itself. | |
| 11 | without_mobile_discount | Without mobile discount | number, nullable | The same rate without the mobile-only cut - what a desktop browser shows. | |
| 12 | discounts | Discount lines | array, nullable | Every discount with its code, name and amount. | |
| 13 | extra_charges | Extra charges | object, nullable | Taxes and fees. details[] itemises each one; excludedTotal / includedTotal / unknownTotal / depositTotal roll them up. chargesCompleteness says whether the itemisation is all of it: under “incalculable” the all-in figure is a floor, not a total. inclusion and mode are two different vocabularies and both contain “conditional” - inclusion is whether the guest owes it (included, excluded, conditional, deposit; anything else becomes unknown, with Booking’s own token kept in inclusionRaw), while mode is how it is computed (percentage, incalculable, not_applicable, variable_amount, conditional, per_stay, per_person_per_stay, per_night, per_person_per_night, per_item, per_item_per_period - passed through raw). A line reading “conditional” means nothing until you know which field it came from. There is no conditional bucket and no conditionalTotal - removed 2026-10-28 after it proved empty on every surface this Actor reads: 0 of 4,934 roomList charge lines and 0 of 405 search lines. The VALUE survives where it is true - a charge line’s own inclusion still reads conditional where Booking says so - but the always-empty array and always-zero rollup are gone from every level. If such a line ever arrives it groups with excluded, which is conservative (the guest may owe it) and never with unknown, which would assert Booking said nothing. depositTotal stays, and the asymmetry is the whole reason: grouping a conditional line into excluded is true, while grouping a refundable deposit there would overstate the bill. Measured empty too - 0 of 4,934, including 615 lines from 50 vacation rentals and entire homes across five markets, the segment a damage deposit comes from - but kept, because ARB found the app really does pass DEPOSIT at a call site (queue item 149) and renders it in the checkout price summary, which is reached only after a room is selected. A deposit line, if one ever arrives, is the damage deposit - the same figure as details.damageDeposit reached by a second route (the app maps BpPriceAdditionalInfo.damageDeposit onto the DEPOSIT inclusion), not a further charge to add to it. |
products[] — Rate
Parent chain: row → products[]
One bookable rate. Only the members documented at this level are listed below — a rate carries the rest of its columns (price, occupancy, deal, room, fit) as described in the products row of the table above.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | policies | Policies | object, nullable · in products[].policies | The rate’s commercial terms: cancellation, meal plan, payment timing, prepayment and deposit. | |
| 2 | price | Rate price | object, nullable · in products[].price | What this rate costs for the whole requested stay - the party and the nights you asked for, already expanded, not a per-night or per-person figure. It states its own currency rather than borrowing the row’s (#86), because it does not always match: the total falls back to Booking’s gross_amount_hotel_currency when the ordinary amount is absent, and that field is the PROPERTY’s currency. |
products[].policies — Policies
Parent chain: row → products[] → policies
The rate’s commercial terms: cancellation, meal plan, payment timing, prepayment and deposit.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | meal_plan | Meal plan | object, nullable · in products[].policies.meal_plan | What the rate includes, and what else can be bought with it. | |
| 2 | payment | Payment policy | object, nullable · in products[].policies.payment | When the rate is paid, which is timing rather than amount. | |
| 3 | deposit | Deposit (prepayment) policy | object, nullable · in products[].policies.deposit | The pay-now / pay-later flag, NOT a damage deposit - despite Booking calling the field deposit_required on the wire. True means prepayment is taken at booking. A real damage deposit is details.damageDeposit, and the two are unrelated: one is when the rate is paid, the other is money left at the desk and refunded. noPaymentDueAtBooking and payment.types answer different questions - the first is whether anything is taken now, the second is when the rate is settled - so a rate can require no payment now and still be pay_now for the balance. |
products[].policies.meal_plan — Meal plan
Parent chain: row → products[] → policies → meal_plan
What the rate includes, and what else can be bought with it.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | meals | Meals included | array | The meals the rate includes, as a list of breakfast / lunch / dinner. Empty when it includes none. | |
| 2 | plan | Plan | string | Board INCLUDED in the rate: breakfast_included, half_board, full_board, all_inclusive, or no_plan. no_plan does not mean no breakfast - it means none is included, and 40% of blocks measured sell one for a fee. The vocabulary is deliberately unchanged, so a filter written against no_plan does not change answer; breakfastAvailableForFee is the field that corrects the record. | no_plan, breakfast_included |
| 3 | breakfastAvailableForFee | Breakfast for a fee | boolean | Whether breakfast can be bought with this rate but is not included in it. True on 145 of 366 blocks measured, every one of which plan calls no_plan. False is an answer rather than a gap: the payload names the rates that offer no meal at all. | |
| 4 | offers | Offers | array, nullable · in products[].policies.meal_plan.offers[] | One entry per meal the rate can carry, included or for a fee. Null on a rate with no meal option at all, rather than an empty array on every such row. | |
| 5 | summary | Summary | string, nullable | Booking’s own wording for the meal terms, verbatim: localised by language and converted to the run’s currency, ROUNDED (“Breakfast EUR 6” for a 120 ZAR breakfast). It is the only place the per-person-per-night basis is stated, and it is display text - offers[].price is the number to compute on. Null on a rate with no meal option. | Continental breakfast costs € 18.90 per person per night. |
products[].policies.meal_plan.offers[] — Meal offer
Parent chain: row → products[] → policies → meal_plan → offers[]
One meal this rate can carry, and on what terms.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | meal | Meal | string, nullable | breakfast, lunch or dinner. Derived from the offer icon, never from its title, because the title is localised. Null for a single lunch-or-dinner entry, where the icon does not say which and nothing else does either - label carries Booking’s own words for it. | breakfast |
| 2 | status | Status | string, nullable | included when the rate covers the meal, available_for_fee when it can be bought on top. Decided by the presence of a price and the rate’s own board flags, not by the prose. Null in the unobserved case where neither says. | available_for_fee, included |
| 3 | price | Price | number, nullable | What the meal costs on top of the rate, as the property quotes it: a unit price per person per night, not a stay total (A/B’d invariant across 2 and 4 adults, 2 and 4 nights). Null when the meal is included, and null on the hotel_ids path, which publishes the offer but not the amount. | 18.9 |
| 4 | currency | Currency | string, nullable | The currency price is quoted in - THE PROPERTY’S OWN, which can differ from the row’s currency. A Cape Town property returns 120 ZAR on a run priced in EUR, so pairing the amount with the row’s currency overstates it twentyfold. | EUR, ZAR |
| 5 | style | Style | string, nullable | The cuisine or service list Booking puts in brackets after the title, passed through verbatim and localised. | Continental, Buffet |
| 6 | label | Label | string, nullable | Booking’s own title for the offer, verbatim and localised. Null on the hotel_ids path, which carries no structured offer to title. | Breakfast available (Continental, Buffet) |
products[].policies.payment — Payment policy
Parent chain: row → products[] → policies → payment
When the rate is paid, which is timing rather than amount.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | types | Payment type | string, nullable | One of pay_now, pay_later or pay_at_property. | |
| 2 | choosable | Guest chooses | boolean, nullable | True where the guest picks the timing, false where the rate requires it. |
products[].policies.deposit — Deposit (prepayment) policy
Parent chain: row → products[] → policies → deposit
The pay-now / pay-later flag, NOT a damage deposit - despite Booking calling the field deposit_required on the wire. True means prepayment is taken at booking. A real damage deposit is details.damageDeposit, and the two are unrelated: one is when the rate is paid, the other is money left at the desk and refunded. noPaymentDueAtBooking and payment.types answer different questions - the first is whether anything is taken now, the second is when the rate is settled - so a rate can require no payment now and still be pay_now for the balance.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | prepaymentAtBooking | Prepayment at booking | boolean, nullable | True where Booking takes payment when the rate is booked. | |
| 2 | noPaymentDueAtBooking | Nothing due at booking | boolean, nullable | The yes/no a consumer should read. Means nothing due today, not nothing due ever. | |
| 3 | depositRequiredRaw | Deposit required, raw | string | integer, nullable | Booking’s own deposit_required value, verbatim. | |
| 4 | termsId | Terms id | integer | string, nullable | An opaque id with no consumer in Booking’s own app either; passed through rather than interpreted. |
products[].price — Rate price
Parent chain: row → products[] → price
What this rate costs for the whole requested stay - the party and the nights you asked for, already expanded, not a per-night or per-person figure. It states its own currency rather than borrowing the row’s (#86), because it does not always match: the total falls back to Booking’s gross_amount_hotel_currency when the ordinary amount is absent, and that field is the PROPERTY’s currency.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | currency | Currency | string, nullable | The currency every amount in this object is in, read from the same money object Booking supplied the total in. Usually the row’s currency, but not guaranteed to be - compare the two rather than assuming. Null where Booking labelled no currency, which is honest rather than a guess. | |
| 2 | total | Total | number, nullable | The all-stay price Booking displays for this rate. Same number as book. | |
| 3 | book | Bookable price | number, nullable | The display price, under the name the older output used. Same number as total. | |
| 4 | base | Net | number, nullable | Before any tax or fee Booking itemises - net_amount on the wire. | |
| 5 | per_night | Per night | number, nullable | Derived here by dividing the stay total by the nights: Booking’s own averagePricePerNight comes back zero-filled. | |
| 6 | before_discount | Before discount | number, nullable | The undiscounted rate. Present only on a discounted rate. | |
| 7 | discount | Discount | number, nullable | The deal or campaign cut. Present only on a discounted rate. | |
| 8 | mobile_discount | Mobile discount | number, nullable | The mobile-only part of the cut. Present only when there is one. | |
| 9 | without_mobile_discount | Without the mobile cut | number, nullable | What a desktop browser is shown. Present only when a mobile cut applies. |
rooms[] — Room type
Parent chain: row → rooms[]
One room type with availability on your dates. Only the members documented at this level are listed below — a room type carries the rest of its columns (occupancy, bed layout, bathrooms, size, photos, child policy) as described in the rooms row of the table above.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | areas | Areas | array, nullable · in rooms[].areas[] | Which area of the unit each bed is in, one entry per area. Null on an ordinary hotel room, which publishes no layout. It PLACES BEDS rather than inventorying rooms: two entries of one type can describe a single room, so count beds here and not areas. The same beds as bedLayouts from another angle, so summing across both double-counts - bedCount stays the one number to trust. | |
| 2 | hasLivingRoom | Has a living room | boolean, nullable | Whether any area is a living room. A flag and not a count: counting Living Room entries overstated against Booking’s own room summary on 2 of 14 units measured, while presence agreed on 14 of 14. Null when the unit publishes no layout. |
rooms[].areas[] — Area of a unit
Parent chain: row → rooms[] → areas[]
One area of a multi-room unit, with the beds in it.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | type | Area type | string, nullable | Normalised area type: bedroom or living_room. Booking owns this vocabulary, so a value we have not seen is slugged from its own string (Dining Room -> dining_room) and keeps its beds rather than being dropped. | bedroom, living_room |
| 2 | typeLabel | Area label | string, nullable | Booking’s own name for the area, localised by language and server-provided. | Bedroom, Living room |
| 3 | sleeps | Sleeps | integer, nullable | How many people this area sleeps. 0 is a real answer and is published as 0 - a living room with no bed sleeps nobody. | 2 |
| 4 | ensuiteBathroom | En-suite bathroom | boolean, nullable | Whether this area has its own bathroom. 29% of areas measured do. | |
| 5 | beds | Beds in this area | array · in rooms[].areas[].beds[] | The beds in this area. Empty on an area that holds none. |
rooms[].areas[].beds[] — Bed in an area
Parent chain: row → rooms[] → areas[] → beds[]
One bed kind in this area, with how many of it.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | type | Bed type | string | Normalised bed type: twin, double, king, bunk_bed, sofa_bed, queen, futon or double_bunk_bed. An id outside Booking’s 1-8 range comes through as bed_type_ | queen, sofa_bed |
| 2 | typeId | Bed type id | integer | string, nullable | Booking’s own numeric bed type, passed through unchanged - the key to trust when type reads bed_type_ | 6 |
| 3 | count | Beds of this type | integer | How many of this bed the area holds. | 1 |
| 4 | name | Bed name | string, nullable | Booking’s own name for the bed type, localised by language. Sometimes absent, which is why typeId is the reliable key. | Queen bed(s) |
| 5 | label | Bed label | string, nullable | Booking’s own phrasing with the count included, localised. | 1 queen bed |
| 6 | size | Bed width | string, nullable | The width band as Booking states it, metric, localised. | 151-180 cm wide |
| 7 | sizeImperial | Bed width (imperial) | string, nullable | The same band in inches. Both are emitted so a consumer picks rather than converts. | 60-70 inches wide |
childAndBedPolicy — Child and bed policy
Parent chain: row → childAndBedPolicy
Booking’s children and extra-bed rules for the property.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | ageBands | Age bands | array, nullable · in childAndBedPolicy.ageBands[] | Per-age-range cot and extra-bed rules. Anything priced here is paid at the property and sits outside price.total_with_charges. |
childAndBedPolicy.ageBands[] — Age band
Parent chain: row → childAndBedPolicy → ageBands[]
One age range and what it costs.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | minAge | Minimum age | integer, nullable | Youngest age in the band. | |
| 2 | maxAge | Maximum age | integer, nullable | Oldest age in the band. | |
| 3 | cot | Cot | object, nullable | Cot availability and price for this band, where Booking states one. | |
| 4 | extraBed | Extra bed | object, nullable | Extra-bed availability and price for this band. |
details — Static property details
Parent chain: row → details
Static, stay-independent property content. Needs includeDetails (or mode details).
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | damageDeposit | Damage deposit | object, nullable · in details.damageDeposit | A refundable deposit taken at the property. In the PROPERTY’s currency, not the run’s - a Kuala Lumpur property returns 50 MYR on a run requesting EUR, because a deposit is taken at the desk in local money. It is not part of price.total_with_charges, and it is the only place a damage deposit is reported. |
details.damageDeposit — Damage deposit
Parent chain: row → details → damageDeposit
A refundable deposit taken at the property. In the PROPERTY’s currency, not the run’s - a Kuala Lumpur property returns 50 MYR on a run requesting EUR, because a deposit is taken at the desk in local money. It is not part of price.total_with_charges, and it is the only place a damage deposit is reported.
| # | Field | Label | Type | Description | Example |
|---|---|---|---|---|---|
| 1 | policyType | Policy type | string, nullable | DAMAGE_DEPOSIT for real money the guest puts down, or DAMAGE_PROGRAMME for Booking’s damage-protection scheme, which is not. | |
| 2 | policyTypeRaw | Policy type, raw | string, nullable | Booking’s own token, kept where it is outside the known set. | |
| 3 | isDamageDeposit | Is a damage deposit | boolean, nullable | True only for DAMAGE_DEPOSIT, so the two cases cannot be confused by a consumer reading the flag alone. | |
| 4 | amount | Amount | number, nullable | The deposit amount. | |
| 5 | currency | Currency | string, nullable | The property’s currency, which may differ from the row’s. |
Captured from a live run. Lists are cut to two entries and deep nesting is elided — the shape is real, the bulk is not.
{
"id": 176226,
"currency": "EUR",
"checkin": "2026-10-31",
"checkout": "2026-11-01",
"datesAdjusted": false,
"deep_link_url": "booking://hotel/176226?affiliate_id=337862&checkin=2026-10-31&checkout=2026-11-01&mcid=10",
"price": {
"book": 70.54,
"total": 70.54,
"per_night": 70.54,
"base": 70.54,
"extra_charges": {
"excluded": 10.46,
"included": 0,
"excludedTotal": 10.46,
"includedTotal": 0,
"depositTotal": 0,
"unknownTotal": 0,
"details": [
"{... 10 keys}",
"{... 10 keys}"
]
},
"total_with_charges": 81,
"isMobileRate": false,
"priced_block": {
"block": "17622630_89064837_1_0_0",
"room": 17622630,
"source": "roomList"
}
},
"url": "https://www.booking.com/hotel/lt/airlnn-vilnius.html?aid=337862&checkin=2026-10-31&checkout=2026-11-01&no_rooms=1&group_adults=1&selected_currency=EUR",
"name": "AIRINN Vilnius Airport Hotel RENOVATED 2025",
"address": "Rodūnios kelias 8, Naujininkai, LT-02187 Vilnius, Lithuania",
"city": "Vilnius",
"countryCode": "lt",
"latitude": 54.6421857815128,
"longitude": 25.279001891613,
"starRating": 3,
"availability": {
"status": "available",
"requestedNights": 1,
"minStayNights": null,
"datesAdjusted": false,
"soldOutMessage": null,
"alternatives": []
},
"products": [
{
"id": "17622630_89064837_1_0_0",
"room": 17622630,
"roomTypeId": 8,
"roomTypeCode": "TWIN",
"pricedPerBed": false,
"roomName": "Standard Twin Room",
"children": [],
"deal": null,
"isSmoking": false,
"number_available_at_this_price": 20,
"number_of_adults": 1,
"fitsParty": true,
"fitStatus": 2,
"occupancy": "{... 5 keys}",
"maxOccupancy": 1,
"bedroomCount": null,
"bathroomCount": null,
"privateBathroom": true,
"policies": "{... 4 keys}",
"price": "{... 6 keys}"
},
{
"id": "17622630_170669047_1_0_0",
"room": 17622630,
"roomTypeId": 8,
"roomTypeCode": "TWIN",
"pricedPerBed": false,
"roomName": "Standard Twin Room - single occupancy",
"children": [],
"deal": null,
"isSmoking": false,
"number_available_at_this_price": 20,
"number_of_adults": 1,
"fitsParty": true,
"fitStatus": 2,
"occupancy": "{... 5 keys}",
"maxOccupancy": 1,
"bedroomCount": null,
"bathroomCount": null,
"privateBathroom": true,
"policies": "{... 4 keys}",
"price": "{... 6 keys}"
},
"... 42 more"
],
"paymentTerms": {
"model": "hybrid2.0",
"cardRequired": true,
"cvcRequired": false,
"addressRequired": false,
"noCardNeeded": false,
"hostedPaymentPageOnly": false,
"requestToBookRequired": null
},
"rooms": [
{
"id": 17622630,
"roomTypeId": 8,
"roomTypeCode": "TWIN",
"pricedPerBed": false,
"name": "Standard Twin Room",
"maxOccupancy": 1,
"fitsParty": true,
"bedroomCount": null,
"bathroomCount": null,
"privateBathroom": true,
"beds": [
"{... 8 keys}"
],
"bedCount": 2,
"hasSofaBed": false,
"sofaBedInEveryConfiguration": false,
"bedConfigurationCount": 1,
"bedsSelectable": false,
"bedLayouts": [
"{... 3 keys}"
],
"areas": null,
"hasLivingRoom": null,
"surfaceM2": 22,
"photos": [
"https://cf.bstatic.com/xdata/images/hotel/max500/694846956.jpg?k=ebb71354aab6975b6d6cde6a92d5de729082dfef1a6980f24a13447131572aee&o=",
"https://cf.bstatic.com/xdata/images/hotel/max500/694845674.jpg?k=f6c7f0b29b6256301c37c6b8626880dbdfba2b8767ea68b502a808e3d6b64d0f&o=",
"... 3 more"
],
"childrenPolicy": "{... 2 keys}",
"isSmoking": false,
"roomsLeftMessage": null,
"productCount": 4,
"minPrice": 70.54,
"minPriceFitting": 70.54,
"currency": "EUR"
},
{
"id": 17622635,
"roomTypeId": 10,
"roomTypeCode": "SINGLE",
"pricedPerBed": false,
"name": "Standard Single Room",
"maxOccupancy": 1,
"fitsParty": true,
"bedroomCount": null,
"bathroomCount": null,
"privateBathroom": true,
"beds": [
"{... 8 keys}"
],
"bedCount": 1,
"hasSofaBed": false,
"sofaBedInEveryConfiguration": false,
"bedConfigurationCount": 1,
"bedsSelectable": false,
"bedLayouts": [
"{... 3 keys}"
],
"areas": null,
"hasLivingRoom": null,
"surfaceM2": 18,
"photos": [
"https://cf.bstatic.com/xdata/images/hotel/max500/694849697.jpg?k=44be3ea06ede0d9033076040f0ebbd735cc9882bdc7e51b4a41d526842ad15b6&o=",
"https://cf.bstatic.com/xdata/images/hotel/max500/694849726.jpg?k=ed49e72c3f77ea36ae7e2420dda49cf9578e424b067d12c1000c1604b38bab07&o=",
"... 3 more"
],
"childrenPolicy": "{... 2 keys}",
"isSmoking": false,
"roomsLeftMessage": null,
"productCount": 4,
"minPrice": 71.43,
"minPriceFitting": 71.43,
"currency": "EUR"
},
"... 9 more"
],
"accommodationType": "Hotels",
"accommodationTypeId": 204,
"reviewScore": 9.3,
"reviewScoreWord": "Wonderful",
"reviewsCount": 9657,
"freeCancellation": false,
"mainPhotoUrl": "https://cf.bstatic.com/xdata/images/hotel/max1024x768/695378560.webp?k=d4eefc35d46eab621629466922faf394b9275864e3ab3a5269b3f2b126bf0cf6&o=",
"isPreferred": false,
"isPreferredPlus": false,
"details": {
"name": "AIRINN Vilnius Airport Hotel RENOVATED 2025",
"accommodationType": "Hotels",
"accommodationTypeId": 204,
"starRating": 3,
"reviewScore": 9.3,
"reviewScoreWord": "Wonderful",
"reviewsCount": 9657,
"address": "Rodūnios kelias 8",
"city": "Vilnius",
"district": "Naujininkai",
"countryCode": "lt",
"latitude": 54.642185781512765,
"longitude": 25.279001891613007,
"freeCancellation": false,
"mainPhotoUrl": "https://cf.bstatic.com/xdata/images/hotel/max1024x768/695378560.webp?k=d4eefc35d46eab621629466922faf394b9275864e3ab3a5269b3f2b126bf0cf6&o=",
"isPreferred": false,
"isPreferredPlus": false,
"ufi": -2620663,
"photos": [
"https://cf.bstatic.com/xdata/images/hotel/max1024x768/694772468.webp?k=1d9cecb93b332d2cfc8d3988c7fea95deb9cbced5d77174fb8a7948d8e7e54f3&o=",
"https://cf.bstatic.com/xdata/images/hotel/max1024x768/695378560.webp?k=d4eefc35d46eab621629466922faf394b9275864e3ab3a5269b3f2b126bf0cf6&o="
],
"facilities": [
"Parking",
"Restaurant",
"... 100 more"
],
"timezone": "Europe/Vilnius",
"familyFacilities": [
"Soundproof rooms",
"Family rooms"
],
"languagesSpoken": [
"ru",
"lt",
"... 1 more"
],
"isFamilyFriendly": false,
"wifiReviewScore": 9.2,
"breakfastReviewScore": 0,
"hasKitchen": false,
"hasSeatingArea": false,
"hasRefundableRates": false,
"hasNonRefundableRates": true,
"kitchenFacilities": [
"Electric kettle"
],
"distanceFromCenter": 4.998,
"description": "AIRINN Vilnius Airport Hotel RENOVATED 2025 is located on the grounds of Vilnius International Airport, just 164 feet from the main terminal, offering a view...",
"summary": "The 3-star hotel features a child-friendly buffet, family rooms, and a pram for convenience. Families appreciate the connection to the airport and the shuttl...",
"checkInTime": "14:00",
"checkinAgeRestriction": "There's no age requirement for check-in",
"petsAllowed": "YES",
"petsChargeMode": "CHARGES_MAY_APPLY",
"acceptedPaymentMethods": [
"{... 3 keys}",
"{... 3 keys}",
"... 2 more"
],
"cashAccepted": true,
"cardAccepted": true,
"paymentMethodsExclusive": false,
"bookingManagedPayAtProperty": true,
"checkOutTime": "12:00",
"propertyType": "Hotels",
"propertyTypeId": 204,
"popularFacilities": [
"Parking",
"Internet",
"... 7 more"
],
"formattedAddress": "Rodūnios kelias 8, Naujininkai, LT-02187 Vilnius, Lithuania",
"postalCode": "LT-02187",
"districts": [
"Naujininkai"
],
"isCentrallyLocated": false,
"hostContact": {
"isTrader": true,
"legalEntityId": 12140480,
"companyLegalName": "AirInn Vilnius, UAB",
"firstName": null,
"middleName": null,
"lastName": null,
"email": "[email protected]",
"phone": "+37069813909",
"registrationNumber": "302631825",
"tradeRegisterName": "Registrų centras",
"subjectType": "BUSINESS",
"address": "{... 6 keys}",
"licenseNumbers": [],
"licensePhrases": []
},
"distanceUnit": "km"
},
"childAndBedPolicy": {
"allowChildren": true,
"ageBands": [
"{... 5 keys}",
"{... 5 keys}"
],
"cotAvailable": true,
"extraBedAvailable": true,
"notes": [
"Additional fees are not calculated automatically in the total cost and will have to be paid for separately during your stay.",
"The maximum number of cribs, extra beds, and children allowed in existing beds can vary depending on the option booked.",
"... 2 more"
],
"childrenNotes": [
"Children of all ages are welcome.",
"Children 4 and above will be charged as adults at this property.",
"... 1 more"
],
"summary": "Children of all ages are allowed.\n1 child (up to and including 3 years old) stays for free when using available cribs.\nUp to 5 children (up to and including ..."
}
}