# TripAdvisor Reviews (`superslowsloth/tripadvisor-reviews`) Actor

One flat row per TripAdvisor review for restaurants and attractions: rating, title, full text, trip type, stay date, reviewer and the owner reply.

- **URL**: https://apify.com/superslowsloth/tripadvisor-reviews.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** Travel, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 review scrapeds

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/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

## TripAdvisor Reviews

One flat row per review, for TripAdvisor **restaurants and attractions**.

Give it place URLs or TripAdvisor location ids and it returns the reviews as a
table: rating, title, the full review text, the trip type, the month of the
visit, who wrote it and where they are from, how many people found it helpful,
and the owner's public reply when there is one. Every row also carries the
place's id, name and URL, so several places can go into one dataset and still
be told apart.

### Hotels do not work, and here is the measurement

**Hotel properties are refused.** Restaurants and attractions are served
normally; asking for a hotel returns HTTP 403 from TripAdvisor's anti-bot
layer, on every configuration tried.

Measured on the Apify platform on 2026-08-25, all through the residential
proxy, against `d93589` (The Michelangelo New York) and `d302346`:

| Varied | Values tried | Result |
|---|---|---|
| TLS fingerprint | `chrome`, `chrome124`, `chrome131`, `chrome133a`, `safari18_0`, `firefox133` | 403 on all six |
| Exit address | 10 residential addresses per profile, rotated on each refusal | 403 on all |
| URL path prefix | `Hotel_Review`, `Restaurant_Review`, `Attraction_Review` for the same hotel id | 403 on all three |
| Language filter | with and without `?filterLang=` | 403 both ways |
| Session warm-up | with and without a prior request to establish cookies | 403 both ways |

The refusal follows the **location id**, not the URL shape: requesting a hotel's
id under the `Restaurant_Review` prefix - which works fine for actual
restaurants - is still refused. In the same runs, restaurant and attraction ids
returned full review payloads. That is what rules out the fingerprint, the
address pool and the URL builder as the cause, and points at TripAdvisor
gating hotel inventory specifically.

Hotel URLs are still accepted as input rather than rejected, so that the day
this changes the actor simply starts working. A run that is refused says so in
the log and in `failures`, and no review events are charged for it.

### Input

| Field | What it does |
|---|---|
| `startUrls` | Place pages to scrape. Paste the URL as it appears in your browser. |
| `locationIds` | TripAdvisor location ids - the digits after `-d` in a place URL, e.g. `93589`. Use instead of or alongside the URLs. |
| `maxReviews` | Reviews to collect **per place**, not per run. Three hotels at 100 means up to 300 rows. Default 100. |
| `language` | Two-letter code (`en`, `fr`, `de`, `ja`, `th`, …) to take only reviews in one language. Empty means every language. |
| `proxyConfiguration` | Must be Apify Proxy with the `RESIDENTIAL` group. See below. |

```json
{
  "startUrls": [
    { "url": "https://www.tripadvisor.com/Hotel_Review-g60763-d93589-Reviews-The_Michelangelo_New_York-New_York_City_New_York.html" }
  ],
  "maxReviews": 100,
  "language": "en",
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

TripAdvisor identifies a place by the `-d` number and rewrites the rest of the
URL itself, so a link to a place that has since been renamed still resolves,
and a bare location id is enough on its own.

### Output

One row per review:

| Field | Notes |
|---|---|
| `place_id`, `place_name`, `place_url`, `place_type` | The place. `place_type` is TripAdvisor's own classification, e.g. `EATERY`, `ACCOMMODATION`. |
| `review_id`, `review_url` | Stable identifiers for the review itself. |
| `title`, `text` | The headline and the full prose, newlines intact. |
| `rating` | 1-5. |
| `published_date`, `created_date` | ISO dates. They differ when a review sat in moderation. |
| `stay_date` | When the reviewer says they visited - not when they wrote it. |
| `trip_type` | `FAMILY`, `COUPLES`, `BUSINESS`, `FRIENDS`, `SOLO`, or null. |
| `language` | The language the review was written in. |
| `reviewer_id`, `reviewer_name`, `reviewer_location`, `reviewer_contributions` | Who wrote it. `reviewer_location` is the free-text home town they set. |
| `helpful_votes` | How many readers marked it helpful. |
| `owner_response_text`, `owner_response_date` | The business's public reply. |

#### Nulls mean "not stated", never zero

A field is `null` when TripAdvisor did not supply it, and it is never defaulted
to `0` or `""` to fill the column. `helpful_votes` is the one worth being
careful with: `0` means TripAdvisor reported zero votes, while `null` means the
field was absent. Writing a zero for an absent value would read as a
measurement, which is the one thing it is not.

Reviewers frequently leave `reviewer_location` unset and TripAdvisor often has
no `trip_type` for attraction reviews, so expect nulls in both.

### The residential proxy is required, not optional

TripAdvisor is fronted by DataDome, which scores the exit address as well as
the TLS fingerprint of the client. Measured on 2026-08-25: a plain HTTP client
and a fingerprint-matched one - impersonating twelve different Chrome, Safari,
Firefox and Edge builds - were all answered with HTTP 403 and a challenge page
from an ordinary address. Datacenter proxies are refused the same way.

The default input uses `RESIDENTIAL`, which is the configuration this actor is
built and priced around. Running it without a proxy produces a run that charges
the start fee and returns nothing, so the actor logs a warning when it finds
no proxy configured.

When a page is refused, the run treats it as temporary, takes a new exit
address and tries again, rather than giving up on the place.

### Occasionally a whole run is refused

TripAdvisor's anti-bot layer scores the exit address, and the residential pool
contains addresses it has already scored. Usually one of the first couple of
addresses is accepted; sometimes every address a run draws is refused and the
run returns nothing.

Measured over seven consecutive identical runs on 2026-08-25: six returned the
full 60 rows on the first or second address, one exhausted all ten attempts on
each of its three places and returned zero.

What that means for a buyer:

- **You are not charged per review for reviews you did not get.** A refused run
  charges the actor-start event and nothing else, and it says why in the log.
- **Re-running is the fix**, and it usually succeeds immediately, because the
  addresses are drawn afresh.
- Each place gets ten attempts with a fresh exit address and a backoff between
  them, so a run that is going to fail takes a couple of minutes to say so.

### Pricing

Pay per event: a small charge when a run starts, then one charge per review
row. A run that legitimately finds nothing still covers its fixed cost; a run
that finds 400 reviews is billed for 400 rows.

### Limits worth knowing before you buy

- `maxReviews` rounds up to a whole page, because TripAdvisor serves reviews 10
  to 15 at a time.
- A place's review count in the output can be smaller than the number
  TripAdvisor advertises when a `language` filter is set - the filter is applied
  by TripAdvisor, not by us.
- Reviews are read from the data TripAdvisor's own page is built from rather
  than from the rendered markup, which is more stable than scraping the visible
  HTML but is still a private detail of their site. If the actor starts
  returning nothing, that is the thing that changed.

# Actor input Schema

## `startUrls` (type: `array`):

Place pages to scrape reviews from. Restaurants and attractions work; hotel properties are currently refused by TripAdvisor with HTTP 403 whatever we do, and the README records the measurement. Paste the URL of the place as it appears in your browser, for example https://www.tripadvisor.com/Hotel\_Review-g60763-d93589-Reviews-The\_Michelangelo\_New\_York-New\_York\_City\_New\_York.html. TripAdvisor identifies a place by the -d number in the URL and rewrites the rest itself, so a URL that has since been renamed still resolves.

## `locationIds` (type: `array`):

TripAdvisor location ids, if you already have them - the digits after -d in a place URL, such as 93589. Use this instead of, or alongside, the URL list. Reviews from every place named in either field land in the same dataset, and each row carries place\_id so you can tell them apart.

## `maxReviews` (type: `integer`):

How many reviews to collect for each place, not for the run as a whole - three hotels at 100 means up to 300 rows. TripAdvisor serves 10 to 15 reviews per page, so this rounds up to a whole page.

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

Restrict results to reviews written in one language, as a two-letter code such as en, fr, de, es, it, ja or th. Leave empty to take every language TripAdvisor holds for the place. Each row also carries the language it was written in.

## `browserProfile` (type: `string`):

Advanced. Which browser's TLS fingerprint to present, for example chrome, chrome124, chrome131, safari18\_0 or firefox133. TripAdvisor's anti-bot layer decides partly on this, and which profile it currently accepts changes over time. Leave empty unless a scrape has started being refused.

## `proxyConfiguration` (type: `object`):

TripAdvisor is fronted by DataDome, which refuses unproxied and datacenter addresses with HTTP 403 before any page is served. A residential proxy is required, not optional - the default below is the working configuration and should be left alone.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.tripadvisor.com/Attraction_Review-g187147-d188151-Reviews-Eiffel_Tower-Paris_Ile_de_France.html"
    }
  ],
  "maxReviews": 100,
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `reviews` (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 = {
    "startUrls": [
        {
            "url": "https://www.tripadvisor.com/Attraction_Review-g187147-d188151-Reviews-Eiffel_Tower-Paris_Ile_de_France.html"
        }
    ],
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/tripadvisor-reviews").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 = {
    "startUrls": [{ "url": "https://www.tripadvisor.com/Attraction_Review-g187147-d188151-Reviews-Eiffel_Tower-Paris_Ile_de_France.html" }],
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/tripadvisor-reviews").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 '{
  "startUrls": [
    {
      "url": "https://www.tripadvisor.com/Attraction_Review-g187147-d188151-Reviews-Eiffel_Tower-Paris_Ile_de_France.html"
    }
  ],
  "language": "en"
}' |
apify call superslowsloth/tripadvisor-reviews --silent --output-dataset

```

## MCP server setup

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

```

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/7HgJxQdQvn8mPIOtN/builds/UcZbFuhCUxILBkhW7/openapi.json
