# Booking.com All-in-One API (`romy/booking-all-in-one-api`) Actor

Unofficial always-on REST API for live Booking.com data: hotel search (real price/amenity filters + sort), hotel detail, paginated reviews, flights, car rentals, airport taxi, and attractions — search and full detail. Real multi-region pricing, no account needed.

- **URL**: https://apify.com/romy/booking-all-in-one-api.md
- **Developed by:** [Romy](https://apify.com/romy) (community)
- **Categories:** Travel
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $39.00 / 1,000 hotels

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

### What does Booking.com All-in-One API do?

**Booking.com All-in-One API** is a REST endpoint for live Booking.com data — destination search, hotel search with real price/amenity filters and sort, full hotel detail, paginated reviews with topic filters, plus flights, car rentals, airport taxi, and attractions search. All from a single always-on API powered by [Apify Standby](https://docs.apify.com/platform/actors/development/programming-interface/standby).

It talks directly to the same internal API the official [Booking.com](https://www.booking.com/) Android app uses, reverse-engineered by capturing live traffic from a real device. Auth uses a standard, publicly-documented token-exchange mechanism — not a proprietary signing scheme — generated fresh server-side. No Booking.com account, no API key of your own, and not limited to one region — call the endpoint, get JSON back.

### Why use Booking.com All-in-One API?

- **Real filters and sort that genuinely work** — price range and sort order confirmed live by comparing results before/after: a narrower price range returns fewer, correctly-bounded properties, not an ignored parameter
- **Real multi-region pricing** — confirmed live: an identical search with different currency codes (USD/EUR/IDR tested) returns genuinely different prices for the same rooms, not a symbol swap
- **Real paginated reviews** — confirmed live end-to-end: requesting the next page of reviews returns entirely new review ids with zero overlap with the previous page, using Booking.com's own real offset-based pagination
- **A dedicated review-topic filter endpoint** — the same topic/category breakdown (with counts) the app's own review-filter screen shows
- **Four more verticals beyond hotels** — flights, car rentals, airport taxi, and attractions are each a genuinely separate backend, confirmed live by opening every tab in the app and capturing real traffic — every one returns real, current prices matching the app's own screen
- **Always-on** — Standby mode means no cold start, responds in milliseconds
- **No account needed** — every endpoint works fully anonymously
- **Use cases:** travel content aggregation, price monitoring across currencies, review analysis, destination research bots

### Endpoints

| Method | Path | Description |
| ------ | ---- | ----------- |
| `GET` | `/search` | Destination autocomplete |
| `GET` | `/home` | Home tab feed |
| `GET` | `/hotels` | Hotel search for a destination, with real filters/sort |
| `GET` | `/hotels/{id}` | Full hotel detail |
| `GET` | `/hotels/{id}/reviews` | Paginated hotel reviews |
| `GET` | `/hotels/{id}/reviews/filters` | Review-topic filter schema for one hotel |
| `GET` | `/flights/search-locations` | Flight-specific airport/city autocomplete |
| `GET` | `/flights` | Round-trip or one-way flight search |
| `GET` | `/flights/detail` | Full flight detail — baggage policy, fare rules |
| `GET` | `/car-rentals/search-locations` | Car rental pickup-location autocomplete |
| `GET` | `/car-rentals` | Car rental search |
| `GET` | `/car-rentals/detail` | Full car detail — specs, inclusions, cancellation terms |
| `GET` | `/taxi/search-locations` | Airport taxi location resolution (Google Place ID) |
| `GET` | `/taxi/rates` | Airport taxi prices for a route |
| `GET` | `/attractions` | Things-to-do search |
| `GET` | `/attractions/{slug}` | Full attraction detail |
| `GET` | `/attractions/{id}/reviews` | Paginated attraction reviews |
| `GET` | `/attractions/{id}/reviews/filters` | Review filter/sort schema for one attraction |

### How to use Booking.com All-in-One API

1. Open this Actor's Standby API URL (shown on the Actor's page, under the API tab).
2. Call any endpoint — for example:
   ```bash
   curl "https://romy--booking-all-in-one-api.apify.actor/search?query=Bangkok"
   curl "https://romy--booking-all-in-one-api.apify.actor/home"
   curl "https://romy--booking-all-in-one-api.apify.actor/hotels?dest_id=-3414440&dest_name=Bangkok"
   curl "https://romy--booking-all-in-one-api.apify.actor/hotels?dest_id=-3414440&dest_name=Bangkok&price_min=50&price_max=150&currency=USD"
   curl "https://romy--booking-all-in-one-api.apify.actor/hotels/27229"
   curl "https://romy--booking-all-in-one-api.apify.actor/hotels/27229/reviews?rows=25&offset=25"
   curl "https://romy--booking-all-in-one-api.apify.actor/hotels/27229/reviews/filters"
   curl "https://romy--booking-all-in-one-api.apify.actor/flights/search-locations?q=Bangkok"
   curl "https://romy--booking-all-in-one-api.apify.actor/flights?from=BKK.AIRPORT&to=SIN.CITY&depart=2026-09-19&return=2026-09-26&currency=USD"
   # /flights/detail needs a token field copied from a /flights result — e.g. flightOffers[0].token
   curl "https://romy--booking-all-in-one-api.apify.actor/flights/detail?token=<token from a /flights result>"
   curl "https://romy--booking-all-in-one-api.apify.actor/car-rentals/search-locations?term=Bangkok"
   curl "https://romy--booking-all-in-one-api.apify.actor/car-rentals?location=Bangkok&lat=13.7563&lng=100.5018&pickup=2026-09-24T10:00:00.000Z&dropoff=2026-09-27T10:00:00.000Z&currency=USD"
   # /car-rentals/detail needs search_key + vehicle_id copied from a /car-rentals result
   curl "https://romy--booking-all-in-one-api.apify.actor/car-rentals/detail?search_key=<search_context.searchKey from a /car-rentals result>&vehicle_id=<content.metadata.vehicleId from the same result>"
   curl "https://romy--booking-all-in-one-api.apify.actor/taxi/search-locations?q=Sukhumvit&lat=13.6934&lng=100.751"
   curl "https://romy--booking-all-in-one-api.apify.actor/taxi/rates?pickup=Suvarnabhumi%20Airport&pickup_place_id=ChIJTydCFXdnHTERB3oVT1UZDRI&dropoff=Sukhumvit%20Soi%2011&dropoff_place_id=ChIJ5yhiwOae4jARWAUxwZD2wE4&pickup_datetime=2026-09-19T10:00:00"
   curl "https://romy--booking-all-in-one-api.apify.actor/attractions?ufi=-3414440"
   curl "https://romy--booking-all-in-one-api.apify.actor/attractions/prvajppfwe7k-the-newest-luxury-5-star-bangkok-chao-phraya-dinner-cruise"
   curl "https://romy--booking-all-in-one-api.apify.actor/attractions/PRvAjpPfwE7K/reviews?rating=5"
   curl "https://romy--booking-all-in-one-api.apify.actor/attractions/PRvAjpPfwE7K/reviews/filters"
   ```
   Every call needs your Apify API token, either as `Authorization: Bearer <token>` or `?token=<token>` — the Actor's page API tab has a ready-to-copy version with your token filled in.
3. Read the JSON response — no setup required.

### Parameters

**`GET /search`** — `query` required (free-text destination name).

**`GET /home`** — no required parameters.

**`GET /hotels`** — `dest_id` required (from `/search`).

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `dest_id` | string | `-3414440` | From `/search` |
| `dest_name` | string | `Bangkok` | |
| `checkin` / `checkout` | string (date) | `2026-09-08` | `YYYY-MM-DD`, default tomorrow/day-after |
| `adults` | int, default `2` | `2` | |
| `rooms` | int, default `1` | `1` | |
| `price_min` / `price_max` | int | `50` / `150` | Confirmed live to genuinely narrow results |
| `sort` | string | `best_value` | Confirmed live values: `popularity`, `best_value`, `price` |
| `currency` | string | `USD` | Confirmed live to change returned prices — works for any region, not just one |

**`GET /hotels/{id}`** — `id` required (a hotel id from `/hotels`). `currency` optional.

**`GET /hotels/{id}/reviews`** — `id` required.

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `offset` | int, default `0` | `25` | Confirmed live: real pagination, zero id overlap between offsets |
| `rows` | int, default `25` | `25` | Reviews per page |
| `rating` | string | | Filter to one review-score band |

**`GET /hotels/{id}/reviews/filters`** — `id` required. Returns the review-topic breakdown (with counts) for that hotel.

**`GET /flights/search-locations`** — `q` required (free-text). `type` optional (`from`/`to`) — narrows suggestions by leg.

**`GET /flights`** — `from`, `to`, `depart` required.

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `from` / `to` | string | `BKK.AIRPORT` / `SIN.CITY` | From `/flights/search-locations` |
| `depart` | string (date) | `2026-09-19` | `YYYY-MM-DD` |
| `return` | string (date) | `2026-09-26` | Omit for a one-way search |
| `adults` | int, default `1` | `1` | |
| `cabin_class` | string, default `ECONOMY` | `ECONOMY` | `ECONOMY` | `PREMIUM_ECONOMY` | `BUSINESS` | `FIRST` |
| `sort` | string, default `BEST` | `CHEAPEST` | `BEST` | `CHEAPEST` | `FASTEST` |
| `page` | int, default `1` | `1` | |
| `currency` | string | `USD` | Confirmed live to change returned prices — a real ISO code, not just a display label |

**`GET /flights/detail`** — `token` required, the opaque `token` field from a `/flights` result (also duplicated as `flightDeals[].offerToken`). Returns full baggage policy and fare rules. Its currency is already baked into the token from whichever `/flights` search produced it — there's no separate `currency` param here.

**`GET /car-rentals/search-locations`** — `term` required (free-text).

**`GET /car-rentals`** — `location`, `lat`, `lng`, `pickup`, `dropoff` required.

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `location` | string | `Bangkok` | From `/car-rentals/search-locations` |
| `lat` / `lng` | number | `13.7563` / `100.5018` | Pickup coordinates |
| `pickup` / `dropoff` | string (ISO 8601) | `2026-09-24T10:00:00.000Z` | Pickup/drop-off datetime |
| `driver_age` | int, default `30` | `30` | |
| `currency` | string, default `HOTEL` | `USD` | Confirmed live to change returned prices |

**`GET /car-rentals/detail`** — `search_key` and `vehicle_id` both required, both copied as-is from a `/car-rentals` result (`search_key` is that response's own `search_context.searchKey`; `vehicle_id` is one result card's `content.metadata.vehicleId`). Returns full specs, what's included, and cancellation terms. Confirmed live: the same `search_key` works for every `vehicle_id` from that same search. `currency` optional, default `HOTEL`, confirmed live to change returned prices.

**`GET /taxi/search-locations`** — `q` required (free-text). `lat`/`lng` optional, bias suggestions toward a reference point.

**`GET /taxi/rates`** — `pickup`, `pickup_place_id`, `dropoff`, `dropoff_place_id`, `pickup_datetime` required.

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `pickup` / `dropoff` | string | `Suvarnabhumi Airport` | Free-text label |
| `pickup_place_id` / `dropoff_place_id` | string | `ChIJTydCFXdnHTERB3oVT1UZDRI` | Google Place ID, from `/taxi/search-locations` |
| `pickup_datetime` | string | `2026-09-19T10:00:00` | A time too close to now can legitimately return "no suppliers available" — a real availability constraint, not a bug |
| `passengers` | int, default `1` | `2` | |
| `currency` | string, default `HOTEL` | `USD` | |

**`GET /attractions`** — `ufi` required (from `/search`, same id space as hotels' `dest_id`).

**`GET /attractions/{slug}`** — `slug` required, the FULL slug string from an `/attractions` search result (e.g. `prvajppfwe7k-the-newest-...`). Confirmed live: the short `id` field alone does not resolve here (404) — the full slug is genuinely required.

**`GET /attractions/{id}/reviews`** — `id` required, but here it's the SHORT `id` field from a search result (e.g. `PRvAjpPfwE7K`), not the slug used for detail above — the two endpoints genuinely need different identifiers from the same search result.

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `page` | int, default `1` | `2` | Real pagination — confirmed live, zero id overlap between pages |
| `limit` | int, default `20` | `10` | Reviews per page |
| `rating` | string | `5` | An option id from `/attractions/{id}/reviews/filters`'s "score" filter group — confirmed live to genuinely narrow results, not just echo the parameter back |

**`GET /attractions/{id}/reviews/filters`** — `id` required, same short id as above.

| Param | Type | Example | Meaning |
| ----- | ---- | ------- | ------- |
| `ufi` | int | `-3414440` | Destination id |
| `page` | int, default `1` | `1` | |
| `limit` | int, default `20` | `20` | |

### Output

```json
{
    "success": true,
    "list": {
        "total": 20,
        "properties": [
            { "id": 27229, "name": "Ambassador Hotel Bangkok" }
        ],
        "raw": { "...": "full server-driven-UI response, for anything not already pulled into properties[]" }
    }
}
```

### Data notes

- **Auth is a standard token-exchange flow, not a proprietary scheme.** A one-time anonymous request (self-generated device id only, no account) returns a short-lived access token — confirmed live and standalone-reproducible with a brand-new device id, no prior registration. This Actor renews by requesting a fresh token shortly before the cached one expires, generated entirely server-side.
- **Real filters and sort, confirmed live.** A price range narrows returned properties to that bound (compared results before/after); sort order genuinely reorders results.
- **Multi-region pricing confirmed live.** The exact same search with different currency codes returns genuinely different prices for the same rooms — this Actor isn't limited to one region's pricing.
- **`currency` is also a real, caller-controlled parameter on `/flights`, `/car-rentals`, and `/car-rentals/detail`**, confirmed live with the same before/after price comparison used for hotels — e.g. the exact same flight search returned `176 USD` vs `3,359,469 IDR`, and the same car rental returned `US$78` vs `Rp 1,386,012`. `/flights/detail`'s currency is inherited from whichever `/flights` search produced its token, not independently settable. **Attractions has no equivalent** — three different ways of passing a currency override (as a search-input field, as a context field, and as a query-string parameter matching the pattern used elsewhere on the same host) were each tried live and every one was rejected by the API's own input validation, so prices there reflect a fixed account-level currency this Actor cannot override. Documented here rather than silently omitted.
- **The search/list response is a nested component tree, not a flat array** — this Actor walks it and returns a clean `properties[]` array of the structured fields (id, name, price, rating) callers actually want, alongside the full raw tree for anything not yet extracted.
- **Room photos are already included in the hotel-detail response** — no separate photo-gallery call needed for the room-level photo set the app itself shows on the detail screen.
- **Review pagination is real offset-based paging, confirmed live end-to-end** — requesting the next `offset` returns entirely new review ids, zero overlap with the previous page.
- **The review-topic filter schema is a genuinely separate endpoint**, confirmed live — it isn't bundled into the reviews or search responses.
- **Flights, car rentals, airport taxi, and attractions are each a genuinely separate backend from hotels**, confirmed live by opening every tab in the app and capturing real traffic — not a re-skin of the hotel search. All four were confirmed standalone-reproducible with no session cookies at all, using only the same access-token headers hotels already need (flights and car rentals do use their own separate app-level credential from hotels; airport taxi and attractions reuse the hotel one, since both ride on the same underlying host).
- **Flight search filter/sort options and the attractions *search* filter/sort schema both come embedded in their own search response** (`includeAllFilterValues`/`extractFilterOptions`), same self-describing pattern as the hotel vertical's price-range filter — no separate schema endpoint needed for either. This is distinct from attraction *review* filters (below), which do need a separate call.
- **Airport taxi needs a two-step flow, same as the app itself**: resolve pickup/drop-off to a Google Place ID via `/taxi/search-locations` first, then pass both ids to `/taxi/rates`. Confirmed live: prices matched the app's own screen exactly for a real Suvarnabhumi Airport → Sukhumvit search.
- **Attraction detail and reviews are genuinely separate operations from search, each needing a different identifier from the same search result.** `/attractions/{slug}` needs the full slug string; `/attractions/{id}/reviews` and its filters need the short `id` field instead — confirmed live that the short id alone does not resolve as a slug (404), so both fields genuinely have to be passed through from a search result as-is, not derived from each other.
- **Attraction review pagination is real page/pageSize paging, confirmed live end-to-end** — 20 reviews per page, zero id overlap between pages.
- **The attraction review filter schema is a genuinely separate endpoint from both the reviews and the search-result filters**, confirmed live — and its rating filter genuinely narrows results (verified: a 5-star filter and a 1-star filter returned completely different review ids and ratings), not just an echoed-back parameter.
- **`/flights/detail` and `/car-rentals/detail` both need an opaque identifier copied as-is from their respective search result, not constructed client-side.** `/flights/detail`'s `token` comes straight from a `/flights` result's own `token` field; `/car-rentals/detail` needs both `search_key` (the search response's own `search_context.searchKey`) and `vehicle_id` (a result card's `content.metadata.vehicleId`). Confirmed live for car rentals: the same `search_key` works for every `vehicle_id` from that same search, so no per-vehicle re-derivation is needed.
- **Attraction review pagination is real page/pageSize paging, confirmed live end-to-end** — 20 reviews per page, zero id overlap between pages.
- **The attraction review filter schema is a genuinely separate endpoint from both the reviews and the search-result filters**, confirmed live — and its rating filter genuinely narrows results (verified: a 5-star filter and a 1-star filter returned completely different review ids and ratings), not just an echoed-back parameter.

### Pricing

Pay-per-event, billed on each successful call. See the Actor's Pricing tab for current rates.

### Known limitations

- **This Actor only wraps guest-accessible endpoints.** No login flow is implemented or planned — features requiring a real Booking.com account (saved properties, writing reviews, booking) are out of scope by design.
- \~~Restaurants and attractions aren't covered. Booking.com's mobile app is hotel-focused — its car rental/flights/taxi tabs are separate integrations outside this Actor's scope, and no dedicated restaurant/attraction browsing surface was found in the app.~~ **Correction: found in a later pass.** An earlier version of this README reported flights/car rentals/taxi/attractions as out of scope after the first build only covered hotels. All four turned out to be genuinely real, separate backends — see the Endpoints table above. Restaurant browsing specifically still wasn't found as a distinct surface in the app.
- This is an unofficial, reverse-engineered integration, not affiliated with or endorsed by Booking.com B.V. Behavior may change if Booking.com changes its API.

Found a bug or have a feature request? Use the Issues tab on this Actor's page.

# Actor input Schema

## Actor input object example

```json
{}
```

# Actor output Schema

## `api` (type: `string`):

This Actor doesn't write to a dataset — every response is returned directly over HTTP by its Standby web server. See the README / web server OpenAPI schema (webServerSchema) for the full endpoint list and response shapes (GET /search).

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("romy/booking-all-in-one-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("romy/booking-all-in-one-api").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{}' |
apify call romy/booking-all-in-one-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,romy/booking-all-in-one-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

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