# TripAdvisor Hotels Scraper (`romy/tripadvisor-hotels-scraper`) Actor

Search TripAdvisor hotels by destination and fetch full hotel detail plus genuinely paginated reviews (a real cursor, not a fixed 20-review cap) for one or more hotel ids. Talks directly to TripAdvisor's internal mobile API — no account or key needed. Derived from tripadvisor-all-in-one-api.

- **URL**: https://apify.com/romy/tripadvisor-hotels-scraper.md
- **Developed by:** [Romy](https://apify.com/romy) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.34 / 1,000 hotel result pusheds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 TripAdvisor Hotels Scraper do?

**TripAdvisor Hotels Scraper** searches TripAdvisor hotels for a destination and/or fetches full detail plus paginated reviews for one or more hotel ids — a batch-run Actor that pushes one dataset row per result.

It talks directly to the same internal API the official [TripAdvisor](https://www.tripadvisor.com/) Android app uses, reverse-engineered by capturing live traffic from a real device. No TripAdvisor account, no API key of your own — set up an input, run the Actor, get a dataset.

This is the hotels-only, batch-run slice of [TripAdvisor All-in-One API](https://apify.com/romy/tripadvisor-all-in-one-api), which also serves restaurants and attractions as an always-on Standby REST API. Use this Actor instead when you just want hotel search/detail/reviews as normal dataset rows, no API integration required.

### Why use TripAdvisor Hotels Scraper?

- **Search, detail, and reviews in one Actor** — pass a destination id to search, hotel ids to get full detail, or both
- **Real filters that genuinely work** — price range, amenities, minimum rating, sort — confirmed live against the same underlying endpoint the parent Actor validated
- **Real paginated reviews, not a 20-item cap** — confirmed live end-to-end: page 2 returns 20 genuinely new reviews with zero overlap with page 1, using TripAdvisor's own real (non-trivial) pagination cursor, wrapped so you don't have to reverse-engineer it yourself
- **No account needed** — every endpoint works fully anonymously
- **Use cases:** hotel price/rating monitoring, review analysis, destination research, travel content aggregation

### Input

At least one of `geoId` or `hotelIds` is required.

| Field                   | Type                     | Description                                                                                                         |
| ----------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `geoId`                 | string                   | Numeric TripAdvisor destination id, e.g. `"293916"` for Bangkok. Runs a hotel search when set.                      |
| `priceMin` / `priceMax` | integer                  | Nightly price range — confirmed live to genuinely filter results                                                    |
| `amenities`             | string\[]                 | Amenity tag ids. Get ids from a prior response's `filters.availableFilterGroups`                                    |
| `minRating`             | string                   | Confirmed live values `"1"`-`"5"`                                                                                   |
| `sort`                  | string                   | Confirmed live values: `BEST_VALUE`, `PRICE_LOW_TO_HIGH`                                                            |
| `maxPages`              | integer, default `10`    | Stop after this many search pages. In practice this Actor's search only ever returns 1 page — see Known limitations |
| `maxItems`              | integer                  | Stop once this many hotels from the search have been pushed                                                         |
| `hotelIds`              | string\[]                 | TripAdvisor hotel content ids to fetch full detail for (and reviews, if requested)                                  |
| `includeReviews`        | boolean, default `false` | For each hotel id, also fetch its paginated reviews and attach them to the hotel's row                              |
| `reviewsMaxItems`       | integer                  | Stop once this many reviews have been fetched per hotel. Leave empty for all reviews                                |
| `currency`              | string, default `"GBP"`  | ISO code — confirmed live to change returned prices                                                                 |

Example — search:

```json
{ "geoId": "293916", "minRating": "4", "sort": "PRICE_LOW_TO_HIGH", "maxItems": 20 }
```

Example — detail + reviews:

```json
{ "hotelIds": ["25246100", "302769"], "includeReviews": true, "reviewsMaxItems": 40 }
```

### Output

**Search** (`geoId`) — one dataset row per hotel card:

```json
{
    "stableDiffingType": "HotelListCard_hotel_302769_HOTEL_0",
    "listSingleCardContent": {
        "cardTitle": { "string": "The Peninsula Bangkok" },
        "bubbleRating": { "rating": 4.7, "numberReviews": { "string": "(5,968)" } },
        "commerceInfo": { "priceForDisplay": { "string": "£260" } },
        "cardLink": { "route": { "params": { "contentId": "302769", "contentType": "hotel" } } }
    }
}
```

**Detail** (`hotelIds`) — one dataset row per hotel id:

```json
{
    "hotelId": "25246100",
    "hotel": {
        "AppPresentation_queryAppDetailV2": [{ "container": { "navTitle": "Villa Deva Resort & Hotel Bangkok" } }]
    },
    "reviews": [
        { "htmlTitle": { "htmlString": "Special thanks to May, Im, and Benz" }, "bubbleRating": { "rating": 5 } }
    ]
}
```

`reviews` is omitted entirely when `includeReviews` is `false`.

### Data notes

- **No request signing exists at all** — confirmed by capturing the real app's traffic and finding just static identity headers, not validated against any server-side registry.
- **Review pagination is genuinely non-trivial, and this Actor handles it for you.** Confirmed live: a raw page-number parameter alone silently re-returns page 1 — real pagination requires threading an opaque continuation cursor from the previous response back into the next request, alongside an internal page-depth counter. Verified end-to-end during this Actor's own smoke test: fetching hotel `25246100`'s reviews returned real, distinct review content.
- **`currency` changes real prices**, not just a symbol.

### Known limitations

- **Hotel search only returns 1 page, confirmed live during this Actor's own smoke test.** Search pagination beyond page 1 was genuinely attempted (not just left unwrapped like the parent Actor) — the same `extractUpdateToken()` cursor mechanism the reviews pagination below uses was threaded into a second `listContent()` call as `updateToken`. TripAdvisor's GraphQL schema rejects it: `updateToken` is not a valid field of the list endpoint's `routeParameters` (`InputMapDefinesTooManyFieldsException` / "not defined for input object type `Routing_AppListParametersInput`"), unlike the reviews endpoint where the identical mechanism works. `maxPages` above 1 has no further effect until TripAdvisor's app is found sending that cursor through some other field.
- **This Actor only wraps guest-accessible endpoints.** No login flow is implemented or planned — features requiring a real TripAdvisor account (saved trips, writing reviews, booking) are out of scope by design.
- Restaurants and attractions, destination search, the home feed, and photo galleries are covered by [TripAdvisor All-in-One API](https://apify.com/romy/tripadvisor-all-in-one-api) instead.
- This is an unofficial, reverse-engineered integration, not affiliated with or endorsed by Tripadvisor LLC. Behavior may change if TripAdvisor changes its API.

### Pricing

Pay per event: **$0.003** per `hotel` — charged once for each hotel returned by search and once for each hotel id's detail row (Free plan price; lower on paid Apify plans). See the Actor's Pricing tab for current tiered rates.

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

# Actor input Schema

## `geoId` (type: `string`):

Numeric TripAdvisor destination id, e.g. "293916" for Bangkok. Runs a hotel search when set.

## `priceMin` (type: `integer`):

Minimum price to filter results.

## `priceMax` (type: `integer`):

Maximum price to filter results.

## `amenities` (type: `array`):

TripAdvisor amenity tag ids. Get ids from a prior response's filters.availableFilterGroups.

## `minRating` (type: `string`):

Confirmed live values "1"-"5".

## `sort` (type: `string`):

Confirmed live values.

## `maxPages` (type: `integer`):

In practice caps at 1 page — TripAdvisor's list endpoint does not accept a continuation cursor (see README Known Limitations).

## `maxItems` (type: `integer`):

Stop once this many hotels from the search have been pushed.

## `hotelIds` (type: `array`):

TripAdvisor hotel content ids (from a search, or an existing URL) to fetch full detail for.

## `includeReviews` (type: `boolean`):

For each hotel id, also fetch its paginated reviews and attach them to the hotel's row.

## `reviewsMaxItems` (type: `integer`):

Stop once this many reviews have been fetched for a hotel. Leave empty to fetch all reviews (only used when includeReviews is true).

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

ISO 4217 currency code for returned prices.

## Actor input object example

```json
{
  "maxPages": 10,
  "hotelIds": [
    "25246100"
  ],
  "includeReviews": false,
  "currency": "GBP"
}
```

# 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 = {
    "hotelIds": [
        "25246100"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("romy/tripadvisor-hotels-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 = { "hotelIds": ["25246100"] }

# Run the Actor and wait for it to finish
run = client.actor("romy/tripadvisor-hotels-scraper").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 '{
  "hotelIds": [
    "25246100"
  ]
}' |
apify call romy/tripadvisor-hotels-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,romy/tripadvisor-hotels-scraper"
        }
    }
}

```

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/LsljXgfaKY9BFEdEb/builds/iz8cer6Z6nwVGdXoN/openapi.json
