# Booking.com Room Offers & Cancellation Policies (`apt_marble/booking-room-offers-scraper`) Actor

Get every property's cheapest bookable offer for your exact dates and party: stay price, price per night, taxes, board, room setup — plus the cancellation terms. Free-cancellation deadline, hours of flexibility before check-in, refundable true/false, and market-wide refundability shares.

- **URL**: https://apify.com/apt\_marble/booking-room-offers-scraper.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (community)
- **Categories:** Travel, Automation
- **Stats:** 2 total users, 1 monthly users, 60.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.50 / 1,000 row saveds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Booking.com Room Offers & Cancellation Policies

What a property will actually charge for your dates — and whether you can cancel
it for free. Name the places you care about, set the stay and the party, and this
actor returns one row per property: its cheapest bookable offer for exactly that
stay, the price for the whole stay and per night, the taxes and charges that sit
on top, the board included, the room's bed and bathroom count, and the
cancellation terms — the free-cancellation deadline as a real timestamp, how many
hours before check-in that deadline falls, and a plain true/false for whether the
offer is refundable at all. Built for revenue managers watching a comp set's live
rates, travel brands and booking sites tracking price and policy
competitiveness, and analysts who need a dated, comparable snapshot of what a
market is charging.

It is a cheapest-offer scanner, not a room-inventory dump. Booking.com shows one
to two offers per property for any given stay, so that is what you get — read
**Limits** below before you buy.

### What you can do with it

- **Watch a comp set's live rates for one specific stay.** Every row is stamped
  with the check-in, check-out, nights, lead time and party it was priced for, so
  two runs are directly comparable and nothing has to be reconstructed later.
- **Measure how flexible a market really is.** Turn on the market summary and
  each destination gets a row with the share of properties offering free
  cancellation and the share needing no prepayment — for a live Austin weekend
  that was 587 of 646 properties refundable and 188 with no prepayment.
- **Pull only the flexible inventory.** Switch on "Free cancellation only" and
  effectively every row comes back with a real cancellation deadline, which is
  the fastest way to build a shortlist a corporate travel policy will accept.
- **Compare the true cost, not the headline price.** Where Booking.com publishes
  charges that sit outside the quoted price, you get them as their own column
  plus a tax-inclusive total, so a cheap headline that turns expensive at
  checkout is visible immediately.
- **Build a price and policy series over time.** Run it on a schedule against the
  same destinations, dates and currency and you have a dated history of what a
  market charged and how refundable it was, per property.
- **Screen a whole market down to a shortlist.** Filter by star rating, guest
  review band, board basis, no-prepayment, genuine discount depth, or any of
  Booking.com's own facets, and only the rows that survive are saved — so you are
  never billed for rows you would have thrown away.

### What you get

Two kinds of row land in the same dataset, told apart by `recordType`.

An offer row — one per property, cheapest matching offer first:

```json
{
  "recordType": "offer",
  "hotelId": 1344239,
  "hotelName": "Austin Motor Inn",
  "hotelUrl": "https://www.booking.com/hotel/us/austin-motor-inn.en-us.html",
  "latitude": 30.30662,
  "longitude": -97.73915,
  "address": "5000 North Interregional Highway",
  "city": "Austin",
  "countryCode": "us",
  "starRating": 2,
  "reviewScore": 6.4,
  "reviewsCount": 412,
  "accommodationTypeId": 205,
  "offerRank": 1,
  "roomId": "134423917",
  "occupancy": 2,
  "mealPlanId": 0,
  "mealPlanType": null,
  "mealPlanText": null,
  "finalPrice": 87.98,
  "currency": "USD",
  "originalPrice": 87.98,
  "discountAmount": null,
  "discountPercent": null,
  "pricePerNight": 43.99,
  "taxesAndCharges": 13.2,
  "totalWithTaxes": 101.18,
  "chargesNote": "+$13 taxes and charges",
  "freeCancellationUntil": "2026-09-09T05:00:00Z",
  "isRefundable": true,
  "hoursOfFreeCancellationBeforeCheckin": 19,
  "scarcityMessage": null,
  "scarcityTag": null,
  "roomName": "Double Room",
  "bedrooms": 1,
  "beds": 1,
  "bathrooms": 1,
  "kitchens": 0,
  "livingRooms": 0,
  "variant": "base",
  "checkIn": "2026-09-10",
  "checkOut": "2026-09-12",
  "nights": 2,
  "leadTimeDays": 42,
  "adults": 2,
  "rooms": 1,
  "children": 0,
  "destination": "Austin",
  "destinationType": "CITY",
  "scrapedAt": "2026-07-30T09:14:02.181Z",
  "actorRunId": "y7Kq2mVbN4tRp8sD"
}
```

And, while the market summary is left on, one row per destination:

```json
{
  "recordType": "marketRefundability",
  "destination": "Austin",
  "destinationType": "CITY",
  "checkIn": "2026-09-10",
  "checkOut": "2026-09-12",
  "nights": 2,
  "totalProperties": 646,
  "freeCancellationCount": 587,
  "freeCancellationSharePct": 90.87,
  "noPrepaymentCount": 188,
  "noPrepaymentSharePct": 29.1,
  "availableOnlyCount": 646,
  "currency": "USD",
  "scrapedAt": "2026-07-30T09:14:02.181Z",
  "actorRunId": "y7Kq2mVbN4tRp8sD"
}
```

### Input reference

#### Where to look

Offers only exist inside a market's live results, so start from a place rather
than from a single property.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `destinations` | array of strings | `["Austin"]` (max 100) | Where to look for offers, one entry per line: a city (`Austin`), a region or country (`Tuscany`, `Portugal`), a district, a landmark, an airport name, or a `latitude,longitude` pair such as `30.2672,-97.7431`. An entry Booking.com cannot resolve is named in the log and fails only itself. |
| `destinationType` | select | `CITY` | What kind of place the entries are: city, region or state, country, district or neighbourhood, airport, landmark, or coordinates. A `latitude,longitude` pair is always treated as coordinates whatever you pick. |

#### Stay and guests

Every price and every cancellation deadline belongs to one specific stay, so
these settings change the whole output.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `checkIn` | date | *(empty)* | First night of the stay, as `YYYY-MM-DD`. Leave both dates blank and you get a rolling two-night stay 30 days from the run date, which keeps a scheduled run useful without anyone editing it. Booking.com shows no availability for dates in the past. |
| `checkOut` | date | *(empty)* | Departure day, as `YYYY-MM-DD`. Must be at least one night after check-in; if it is not, two nights are used and the log says so. |
| `adults` | integer | `2` (max 30) | Adults per room. Offers are priced for exactly the party you ask for, so changing this changes every price. |
| `rooms` | integer | `1` (max 30) | How many rooms the party needs. |
| `children` | integer | `0` (max 10) | Children in the party. Add their ages below — Booking.com prices children by age, and without ages the offers may not be the ones you would be quoted. |
| `childrenAges` | array of strings | `[]` (max 10) | One age (0–17) per child, one per line. Filling this in also sets the number of children. |

#### Offer filters

Applied while the market is read, so the rows you are billed for are the rows
you wanted.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `refundableOnly` | boolean | `false` | Keep only offers that can be cancelled free of charge up to a deadline. Turning this on lifts the share of rows carrying a cancellation deadline to effectively all of them. |
| `noPrepaymentOnly` | boolean | `false` | Keep only offers you do not have to pay for up front. |
| `mealPlanFilter` | select | `any` | Board basis: any, breakfast included, breakfast and dinner included, or kitchen facilities. The choice is matched against the labels the destination itself publishes; when a destination does not publish that option, the log says so and offers come back without the board filter rather than empty. |
| `minStars` | integer | `0` (max 5) | Keep only properties with at least this official star rating. `0` keeps everything, including properties with no star rating at all. |
| `minReviewScore` | select | `any` | Keep only properties whose guest review score is at least this good: any, 6+, 7+, 8+ or 9+. The bands are the ones Booking.com itself publishes for that destination. |
| `extraFilters` | array of strings | `[]` (max 12) | Advanced. Any additional Booking.com filter ids, one per line, in the form `group=value` — for example `hotelfacility=107` for a pool or `ht_id=204` for hotels only. Malformed entries are dropped and named in the log. |
| `minDiscountPercent` | integer | `0` (max 100) | Keep only offers whose price is genuinely below the property's own pre-discount price by at least this much. Most offers carry no discount at all, so anything above `0` is a strict filter. |

#### Coverage and extras

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `offerVariants` | array of strings | `[]` (max 6) | The one honest way to see more than a property's single cheapest offer. Each entry is a Booking.com filter id — for example `fc=2` for free cancellation or `mealplan=1` for breakfast included — and the destination is read again under that one constraint, returning each property's **best offer within that constraint**. Rows are merged and identical offers removed, and every row records which variant produced it. Each entry roughly doubles the work of the run. An entry that repeats a filter you already switched on above would return the very same market a second time, so it is dropped and named in the log rather than saving — and charging you for — every offer twice. |
| `sortBy` | select | `price` | Cheapest first, or Booking.com's own ranking. Cheapest first keeps a market comparable between runs and surfaces materially more refundable inventory; Booking.com's own ranking mixes in far more non-refundable offers. |
| `maxPropertiesPerDestination` | integer | `500` (max 1000) | How many properties to read per destination, per variant. Booking.com itself stops showing more than about 1,000 properties for any single search, so that is the ceiling. |
| `emitMarketRefundabilityRow` | boolean | `true` | Saves one extra row per destination with the share of properties offering free cancellation, the share needing no prepayment, and how many properties the destination held in total. |

#### Run settings

Sensible defaults — most runs never need to touch these.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `maxRequests` | integer | `800` (max 8000) | A hard ceiling on how much reading one run may do. The projected size of the job is printed in the log before anything starts, and the run finishes cleanly with whatever it collected once the ceiling is reached. |
| Destinations at a time | integer | `4` (max 8) | How many destinations are worked through in parallel. Higher finishes a long list sooner; `4` is a good balance. |
| `currency` | string | `USD` | Three-letter code every price is returned in, for example `USD`, `EUR` or `GBP`. Amounts are taken exactly as Booking.com quotes them and nothing is ever converted, so keep this the same between runs or a price series cannot be compared. |
| `language` | string | `en-us` | Booking.com language code for property names and labels, for example `en-us`, `en-gb`, `de` or `fr`. |
| `proxyCountry` | string | *(empty)* | Country to appear to browse from, as a two-letter code such as `us`, `gb` or `de`. Booking.com tailors availability and promotions by country, so this can change which offers appear. |

### Output fields

#### Offer rows (`recordType: "offer"`)

| Field | Type | Description |
| --- | --- | --- |
| `hotelId` | integer | Booking.com's numeric id for the property. The stable key to join on across runs. |
| `hotelName` | string | Property name as Booking.com publishes it. |
| `hotelUrl` | string | Link to the property page, assembled from the property's own details. A convenience field — it opens for the great majority of properties but is not the link Booking.com published. |
| `latitude` / `longitude` | number | Position of the property. |
| `address` | string | Street address, where published. |
| `city` | string | City the property sits in — often more precise than the destination you asked for. |
| `countryCode` | string | Two-letter country code, lower-cased. |
| `starRating` | number | Official star rating. Empty for properties that have none. |
| `reviewScore` | number | Guest review score out of 10. Empty when Booking.com withholds it, usually because there are too few reviews. |
| `reviewsCount` | integer | How many guest reviews the property has. |
| `accommodationTypeId` | integer | Booking.com's own code for the kind of accommodation (hotel, motel, apartment, guest house and so on). |
| `offerRank` | integer | `1` for the property's cheapest matching offer, `2` for the runner-up when a second one is shown. |
| `roomId` | string | Identifier of the room being offered, as text so it joins cleanly against other datasets. |
| `occupancy` | integer | How many guests the offer is priced for. |
| `mealPlanId` | integer | Booking.com's board code for the offer; `0` means no board included. |
| `mealPlanType` | string | Board type as Booking.com labels it, when it labels one. |
| `mealPlanText` | string | Human-readable board description, for example "Breakfast included". |
| `finalPrice` | number | What the offer costs for the whole stay, in `currency`. This is the number to compare. |
| `currency` | string | Currency every amount on the row is quoted in. Never converted. |
| `originalPrice` | number | The pre-discount price for the same stay. Equal to `finalPrice` whenever there is no discount, which is the common case. |
| `discountAmount` | number | How much is off, and only when the pre-discount price is genuinely higher. Empty otherwise — a 0% discount is never reported as a finding. |
| `discountPercent` | number | The same discount as a percentage, on the same terms. |
| `pricePerNight` | number | `finalPrice` divided by the nights in the stay, for comparing stays of different lengths. |
| `taxesAndCharges` | number | Charges Booking.com publishes as sitting **outside** the quoted price. A non-zero value means `finalPrice` does not include them. Booking.com publishes one such figure per property and it belongs to that property's cheapest offer, so it is reported on that row only — never copied onto a runner-up whose price is different. |
| `totalWithTaxes` | number | `finalPrice` plus those charges. Empty when no charge figure is published for that offer, rather than silently repeating the offer price as if it were tax-inclusive or borrowing another offer's tax. |
| `chargesNote` | string | Booking.com's own wording about taxes and charges for this property. |
| `freeCancellationUntil` | string | The moment free cancellation expires, as a full timestamp. **Empty means the offer is non-refundable** — it is not a missing value. |
| `isRefundable` | boolean | `true` when the offer has a free-cancellation deadline, `false` when it has none. |
| `hoursOfFreeCancellationBeforeCheckin` | number | How many hours before the start of the check-in day the free-cancellation deadline falls — the number that tells you how flexible an offer actually is. Typically 19–43 hours. |
| `scarcityMessage` | string | Booking.com's "only X left" style urgency note, when it shows one. A rare bonus signal, empty on almost every offer. |
| `scarcityTag` | string | The tag behind that message, when there is one. |
| `roomName` | string | Name of the room or unit being offered. |
| `bedrooms` / `beds` / `bathrooms` / `kitchens` / `livingRooms` | integer | Configuration of the unit matched to your party. |
| `variant` | string | `base` for the main pass, or the extra-pass filter id this row came from. |
| `checkIn` / `checkOut` | string | The stay this price and this deadline belong to. |
| `nights` | integer | Length of the stay. |
| `leadTimeDays` | integer | Days between the run date and check-in — the axis to plot a price curve against. |
| `adults` / `rooms` / `children` | integer | The party the offer was priced for. |
| `destination` | string | The destination entry that produced this row. |
| `destinationType` | string | The kind of place that entry was treated as. |
| `scrapedAt` | string | When this row was collected. |
| `actorRunId` | string | The run that produced this row. |

#### Market summary rows (`recordType: "marketRefundability"`)

| Field | Type | Description |
| --- | --- | --- |
| `destination` / `destinationType` | string | The market this summary describes. |
| `checkIn` / `checkOut` / `nights` | string, integer | The stay it was measured for. |
| `totalProperties` | integer | How many properties Booking.com reported for that market and stay. A live snapshot — see Limits. |
| `freeCancellationCount` | integer | Properties in that market offering free cancellation. |
| `freeCancellationSharePct` | number | The same figure as a share of `totalProperties`. |
| `noPrepaymentCount` | integer | Properties needing no payment up front. |
| `noPrepaymentSharePct` | number | The same figure as a share of `totalProperties`. |
| `availableOnlyCount` | integer | Properties Booking.com counts as actually available for the stay. |
| `currency` | string | Currency the run was pinned to. |
| `scrapedAt` | string | When the summary was taken. |
| `actorRunId` | string | The run that produced this row. |

### Pricing

Pay per event — you are billed for what you keep, not for how long a run takes.

| What triggers a charge | Price |
| --- | --- |
| Any row saved to the dataset — an offer row or a market summary row | $0.0005 each, i.e. **$0.50 per 1,000 rows** |

That is the whole price list. Pricing here is deliberately single-tier: offer
rows arrive dozens to hundreds at a time, so no row costs meaningfully more to
produce than any other and a second charge would be a markup with nothing behind
it. Rows a filter removes — the wrong board, too few stars, a discount below your
floor — are never saved and therefore never billed, and neither are repeats of an
offer you already have from an earlier variant.

Worked example: **5,000 offers is about $2.50.** A single 500-property market with
the summary row on is roughly $0.25; a 20-city sweep at 500 properties each,
about $5.

### Limits and what this actor cannot do

- **This is the cheapest matching offer per property, not a full room list.**
  Booking.com's market results carry one to two offers for a property on your
  dates, and nothing it publishes lists a property's whole inventory, so no actor
  can honestly give you every room and rate for a hotel. `offerVariants` is the
  real workaround: each extra pass returns each property's best offer *within one
  constraint you choose*, which widens coverage without pretending to be complete.
- **Offers match the occupancy you asked for.** Two adults and one room returns
  what two adults in one room would be quoted — not the property's cheapest room
  in the abstract. Change the party and every price changes.
- **A blank free-cancellation date means non-refundable.** That is the verified
  meaning, not a gap. Booking.com also publishes an unlabelled policy code
  alongside each offer, but its values have no published meaning, so this actor
  does not ship it dressed up as a policy name. Whether a free-cancellation
  deadline exists is the honest refundability signal, and it is the one you get.
- **Which offers you see depends on the ordering.** Cheapest first surfaces
  materially more refundable inventory than Booking.com's own ranking does. Both
  are truthful views of the same market; neither is the complete one.
- **Booking.com shows at most about 1,000 properties per destination search.**
  A market bigger than that cannot be enumerated in one destination entry — split
  it into districts, coordinates or neighbouring places to see further.
- **Booking.com's own property total moves between identical searches.** The same
  Austin query returned 632, 646 and 647 within minutes, so `totalProperties` and
  the market shares are a snapshot, not an audited figure.
- **A property with no availability for your dates is absent entirely.** It has
  no offer to report, so there is no row. A named single hotel can therefore only
  be priced when it actually turns up in its market's results for your stay.
- **Discounts are rare and are only reported when real.** On live samples the
  pre-discount price equalled the price being charged on every offer, so a
  discount floor above `0` is a genuinely strict filter that can empty a market.
- **Scarcity badges appear rarely.** Treat them as a bonus signal, never as a
  guarantee that a room is about to sell out.
- **Tax-inclusive totals depend on what Booking.com publishes.** Where no charge
  figure is published, the tax-inclusive total is left empty rather than assuming
  the quoted price already includes tax.
- **Prices are never converted.** Every amount is quoted exactly as Booking.com
  gave it in the currency you pinned; a market that quotes in something else is
  reported as it came, not silently converted.
- **Speed depends on the size of the job and on Booking.com's own response
  times.** No fixed completion time is promised for any run size.
- **Booking.com's terms prohibit automated access.** You are responsible for
  using this data lawfully and in line with the source site's terms.

### FAQ

**Do I need a Booking.com account?**
No. None at all.

**Does it need a login, a password or any personal details?**
No. There is nothing to sign in to and nothing to configure — pick your
destinations and dates and run it.

**Can I get every room and rate a hotel offers?**
No, and no tool reading Booking.com's public pages honestly can. A market's
results carry one to two offers per property, so what you get is the cheapest
offer that matches your party, with its cancellation terms. If you need more
breadth per property, add entries to `offerVariants`: each one re-reads the
market under a single constraint — free cancellation, no prepayment, breakfast
included — and returns each property's best offer under it.

**Can I price one specific hotel?**
Only through its market. Search a hotel by itself and Booking.com returns the
right property with no rates attached at all, which is why this actor works from
places rather than from single properties. Run its city with a tight filter — its
star band, or a review-score floor — and pick it out of the results by
`hotelId`.

**Can I schedule it?**
Yes, and it is built for it. Leave both dates blank and every run scans a rolling
two-night stay 30 days out, so a daily schedule gives you a clean lead-time price
curve with no edits. Keep the currency, the party and the destinations fixed
between runs, then join on `hotelId` plus `checkIn` to see what moved.

**Is the data complete?**
For the stay you asked about, within two honest bounds: a destination shows at
most about 1,000 properties, and each property contributes its cheapest matching
offer rather than its full room list. Everything inside those bounds is Booking.com's
own live data for your exact dates, party and currency — nothing is estimated,
interpolated or converted.

**Why did a destination come back with no offers?**
Either nothing in it is bookable for those dates and that party, or your filters
were strict enough to empty it — a board basis the market does not publish, a
review-score band it does not offer, or a discount floor no offer meets. The log
names which of those happened for every destination.

# Actor input Schema

## `destinations` (type: `array`):

Where to look for offers. Enter a city (Austin), a region or country (Tuscany, Portugal), a district, a landmark or airport name, or a latitude,longitude pair (30.2672,-97.7431). One entry per line, up to 100.

## `destinationType` (type: `string`):

What kind of place the entries are. A latitude,longitude pair is always treated as coordinates whatever you choose here.

## `checkIn` (type: `string`):

First night of the stay, as YYYY-MM-DD. Leave both dates blank for a rolling two-night stay 30 days from the run date, which keeps scheduled runs useful without editing. Booking.com shows no availability for dates in the past.

## `checkOut` (type: `string`):

Departure day, as YYYY-MM-DD. Must be at least one night after check-in; if it is not, two nights are used.

## `adults` (type: `integer`):

Adults per room. Offers are priced for exactly the party you ask for, so changing this changes every price.

## `rooms` (type: `integer`):

How many rooms the party needs.

## `children` (type: `integer`):

Children in the party. Add their ages below — Booking.com prices children by age, and without ages the offers may not be the ones you would be quoted.

## `childrenAges` (type: `array`):

One age (0-17) per child, one per line. When you fill this in it also sets the number of children.

## `refundableOnly` (type: `boolean`):

Keep only offers that can be cancelled free of charge up to a deadline. Turning this on lifts the share of rows carrying a cancellation deadline to effectively all of them.

## `noPrepaymentOnly` (type: `boolean`):

Keep only offers you do not have to pay for up front.

## `mealPlanFilter` (type: `string`):

Keep only offers with the board you want. The option is matched against the labels the destination itself publishes; when a destination does not offer it, the run says so and returns offers without that filter.

## `minStars` (type: `integer`):

Keep only properties with at least this official star rating. 0 keeps everything, including properties with no star rating.

## `minReviewScore` (type: `string`):

Keep only properties whose guest review score is at least this good. The bands are the ones Booking.com itself publishes for the destination.

## `extraFilters` (type: `array`):

Advanced. Any additional Booking.com filter ids, one per line, in the form group=value — for example hotelfacility=107 (pool) or ht\_id=204 (hotels only). Up to 12.

## `minDiscountPercent` (type: `integer`):

Keep only offers whose price is genuinely below the property's own pre-discount price by at least this much. Most offers carry no discount at all, so anything above 0 is a strict filter.

## `offerVariants` (type: `array`):

Booking.com shows one to two offers per property for a given search. To see more of a property's inventory, add filter ids here (for example fc=2 for free cancellation, mealplan=1 for breakfast included) and the destination is read again under each one, returning that property's best offer within that constraint. Each entry roughly doubles the run's work. Up to 6.

## `sortBy` (type: `string`):

Price order keeps a market comparable between runs and surfaces more refundable inventory. Booking.com's own ranking mixes in materially more non-refundable offers.

## `maxPropertiesPerDestination` (type: `integer`):

How many properties to read per destination, per pass. Booking.com itself stops showing more than about 1,000 properties for any single search, so that is the ceiling.

## `emitMarketRefundabilityRow` (type: `boolean`):

Saves one extra row per destination with the share of properties offering free cancellation, the share needing no prepayment, and how many properties the destination had in total.

## `maxRequests` (type: `integer`):

A hard ceiling on how much reading the run may do. The projected amount is printed before work starts and the run stops cleanly when the ceiling is reached.

## `maxConcurrency` (type: `integer`):

How many destinations to work through in parallel. Higher is faster on long lists; 4 is a good balance.

## `currency` (type: `string`):

Three-letter currency code every price is returned in, for example USD, EUR or GBP. Keep it the same between runs or a price series cannot be compared.

## `language` (type: `string`):

Booking.com language code for names and labels, for example en-us, de or fr.

## `proxyCountry` (type: `string`):

Country to appear to browse from, as a two-letter code such as us, gb or de. Leave blank for the default. Booking.com tailors availability and promotions by country, so this can change which offers appear.

## Actor input object example

```json
{
  "destinations": [
    "Austin"
  ],
  "destinationType": "CITY",
  "checkIn": "",
  "checkOut": "",
  "adults": 2,
  "rooms": 1,
  "children": 0,
  "childrenAges": [],
  "refundableOnly": false,
  "noPrepaymentOnly": false,
  "mealPlanFilter": "any",
  "minStars": 0,
  "minReviewScore": "any",
  "extraFilters": [],
  "minDiscountPercent": 0,
  "offerVariants": [],
  "sortBy": "price",
  "maxPropertiesPerDestination": 100,
  "emitMarketRefundabilityRow": true,
  "maxRequests": 800,
  "maxConcurrency": 4,
  "currency": "USD",
  "language": "en-us",
  "proxyCountry": ""
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "destinations": [
        "Austin"
    ],
    "destinationType": "CITY",
    "adults": 2,
    "rooms": 1,
    "children": 0,
    "refundableOnly": false,
    "noPrepaymentOnly": false,
    "mealPlanFilter": "any",
    "minStars": 0,
    "minReviewScore": "any",
    "minDiscountPercent": 0,
    "sortBy": "price",
    "maxPropertiesPerDestination": 100,
    "emitMarketRefundabilityRow": true,
    "maxRequests": 800,
    "maxConcurrency": 4,
    "currency": "USD",
    "language": "en-us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/booking-room-offers-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "destinations": ["Austin"],
    "destinationType": "CITY",
    "adults": 2,
    "rooms": 1,
    "children": 0,
    "refundableOnly": False,
    "noPrepaymentOnly": False,
    "mealPlanFilter": "any",
    "minStars": 0,
    "minReviewScore": "any",
    "minDiscountPercent": 0,
    "sortBy": "price",
    "maxPropertiesPerDestination": 100,
    "emitMarketRefundabilityRow": True,
    "maxRequests": 800,
    "maxConcurrency": 4,
    "currency": "USD",
    "language": "en-us",
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/booking-room-offers-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "destinations": [
    "Austin"
  ],
  "destinationType": "CITY",
  "adults": 2,
  "rooms": 1,
  "children": 0,
  "refundableOnly": false,
  "noPrepaymentOnly": false,
  "mealPlanFilter": "any",
  "minStars": 0,
  "minReviewScore": "any",
  "minDiscountPercent": 0,
  "sortBy": "price",
  "maxPropertiesPerDestination": 100,
  "emitMarketRefundabilityRow": true,
  "maxRequests": 800,
  "maxConcurrency": 4,
  "currency": "USD",
  "language": "en-us"
}' |
apify call apt_marble/booking-room-offers-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=apt_marble/booking-room-offers-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hlp6g2FSLb2fJFGkL/builds/FgZJ9sKH32UlcuncD/openapi.json
