# Tripadvisor Hotel Reputation Monitor (`ryanclinton/tripadvisor-hotel-reputation-monitor`) Actor

Monitors a portfolio of hotels on Tripadvisor and returns a ranked attention queue: which property needs attention, what changed since the last run, the reviews behind it, and the team best placed to investigate. Subrating trends, response gaps, comp-set context.

- **URL**: https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor.md
- **Developed by:** [Ryan Clinton](https://apify.com/ryanclinton) (community)
- **Categories:** Travel, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$350.00 / 1,000 property analyseds

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

## Tripadvisor Hotel Reputation Monitor

![Know which hotel needs attention: a ranked queue of what changed, the supporting reviews, and the team best placed to investigate](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/hero.png)

**Know which hotel needs attention, and what the reviews are saying.**

Monitor Tripadvisor reputation across a hotel portfolio and get a ranked queue of material
changes. Each attention item shows what moved, the recent and baseline periods compared, the
reviews supporting it, how strong that evidence is, and the team best placed to investigate.

A scraper returns records. A monitor returns changes.

### Ready-to-run examples

Each one is preconfigured for a job. Open it, swap in your own properties, run it.

- [Track hotel reviews across multiple properties](https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor/examples/monitor-hotel-portfolio) — the scheduled portfolio monitor this actor is built for
- [Audit a hotel's reputation with evidence](https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor/examples/audit-hotel-reputation) — a one-off deep look at specific properties
- [Compare a hotel against its competitors](https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor/examples/hotel-comp-set-benchmark) — comp-set benchmarking with pinned competitors
- [Get Tripadvisor hotel URLs for a city](https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor/examples/find-hotel-urls-by-city) — turn a city list page into property URLs
- [Export Tripadvisor hotel reviews](https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor/examples/export-hotel-reviews) — review rows for your own warehouse

[See all examples](https://apify.com/ryanclinton/tripadvisor-hotel-reputation-monitor/examples)

### Best for

- Hotel groups and management companies monitoring multiple properties
- Hospitality reputation agencies producing recurring client reports
- Asset managers and acquisition teams reviewing property performance

Designed around portfolios of roughly 5 to 200 properties on a recurring schedule.

### Example result

![Ranked attention queue: five properties with the reason each needs attention and its priority](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/attention-queue.png)

**1. Riverside Hotel Midtown, high priority**

Cleanliness complaints rose from **9% to 23%** of reviews against the baseline window.

- Supporting reviews: 9
- Evidence freshness: high
- Sample confidence: moderate
- Lifecycle: worsening
- Suggested owner: housekeeping

The review identifiers, comparison values and reason codes behind that item are in the dataset.

**On ordering:** priority scoring is deterministic and reproducible, but the weights behind it are
provisionally calibrated. Treat the ranking as a prioritisation aid rather than a settled
severity scale until validation is complete. Every attention record carries `calibrationStatus`
so this is visible in the data, not just here.

### How monitoring works

![Watchlist digest: 40 properties tracked, 6 attention items, 2 new this run, 3 worsening, 1 closed on evidence](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/watchlist-summary.png)

The first successful run establishes a baseline. Later runs collect new evidence, compare it
against the stored recent and baseline windows, and report material changes. Every property is
also refreshed periodically even when its rounded headline rating has not moved, because a
recent review evidence can deteriorate before the rounded headline rating moves.

Several successful observations are needed before trajectory and persistence mean much. Six is
the initial product guideline and may be recalibrated.

The actor does not claim to retrieve every historical review. Each property record reports what
was collected, why collection stopped, how much of the recent window it covered, and whether
freshness could be independently verified.

### What you get from one run

- **A ranked attention queue.** One item per consolidated property issue, keyed on property,
  operational theme and active window. A property with two unrelated problems produces two items;
  several correlated changes on one theme produce one.
- **Change detection across runs.** What changed in the observed evidence since the last
  comparable observation.
- **Evidence behind every claim.** Each signal names the metric, the direction, the recent and
  baseline values, how many reviews matched, how many are recent, and the review identifiers.
- **Per-dimension movement, where the source exposes it.** Where Tripadvisor exposes usable
  review subratings, they are tracked separately from the headline rating. A dimension can fall
  materially while the headline holds, and that is the case a rating histogram hides.
- **Management-response measurement, where it is readable.** Where management responses and their
  timestamps are reliably available, response coverage and response delay are reported, including
  coverage specifically on one and two star reviews. Where they are not, the fields are marked
  unavailable with the reason rather than estimated.
- **Amenity expectation mismatches.** Advertised features read against what reviewers reported, keeping
  "advertised", "reviewers report problems" and "not independently verified" as three separate
  statements.
- **Comp-set context.** A property against named competitors, or against nearby properties that
  clear a real comparability test rather than merely being close by.
- **Alert events.** Attention items that cross an alert condition produce a compact payload with
  a deterministic key, so a retried run does not page your team twice for one event.
- **An honest coverage contract.** Nothing is called complete unless it was measured as complete.

### Modes

| Mode | What it does |
|---|---|
| **Monitor** (default) | Tracks a portfolio over time and returns what changed since the last run. This is what the actor is built for. |
| **Audit** | A one-off evidence-based review of specific properties, applying the deepest configured collection policy to every property. |
| **Comp-set** | One property measured against named competitors and, optionally, nearby candidates that clear a comparability test. |
| **Discover** | Turns a hotel list page for a city or area into property URLs you can paste into the other modes. |
| **Reviews** | A review-forward output shape for migrating an existing pipeline. Deliberately secondary. |

### Quick start

The URLs below are **placeholders**. Replace the `d1234567` identifiers with real Tripadvisor
hotel page URLs from your own portfolio.

Monitoring a portfolio, with your own property identifiers attached:

```json
{
  "mode": "monitor",
  "watchlistName": "Northeast portfolio",
  "propertyUrls": [
    "https://www.tripadvisor.com/Hotel_Review-g60763-d1234567-Reviews-Property_Name-New_York_City_New_York.html",
    "https://www.tripadvisor.com/Hotel_Review-g60745-d7654321-Reviews-Another_Property-Boston_Massachusetts.html"
  ],
  "propertyMetadata": [
    { "propertyId": "1234567", "internalPropertyId": "NYC-01", "region": "Northeast", "brand": "Riverside" },
    { "propertyId": "7654321", "internalPropertyId": "BOS-02", "region": "Northeast", "brand": "Riverside" }
  ]
}
```

`propertyMetadata` is an array of objects. Each property takes the first entry that matches it,
tested on `propertyId` first and then on `url` after the URL is canonicalised, so an entry
carrying the numeric id wins over one carrying only a URL. An entry that matches nothing in the
run is simply unused. Those values ride on every record and every alert in their own namespace,
so output joins to your operating structure without a lookup and never overwrites anything read
from the source.

Then schedule it, and schedule it early. Review dates support an initial retrospective baseline,
but past observations of the source, and the run-to-run changes between them, cannot be
reconstructed after the fact.

A whole city instead of a hand-listed set:

```json
{
  "mode": "monitor",
  "watchlistName": "Manhattan market watch",
  "discoveryUrls": ["https://www.tripadvisor.com/Hotels-g60763-New_York_City_New_York-Hotels.html"],
  "maxProperties": 25
}
```

**Membership from a list page is recomputed each run.** Whatever the list page returns that run
is what gets monitored, so a reordered or changed list, or a `maxProperties` limit that bites,
will move properties in and out of the portfolio between runs. Watchlist history stays continuous
because identity is pinned by overlap, but an individual property that drops out and returns has
a gap in its own baseline. For a stable portfolio, discover once with `discover` mode and then
monitor the explicit property URLs it returns. Use list-page monitoring when you actually want a
market view rather than a fixed set of properties.

One property against its competitors:

```json
{
  "mode": "compset",
  "focalPropertyUrl": "https://www.tripadvisor.com/Hotel_Review-g60763-d1234567-Reviews-Property_Name-New_York_City_New_York.html",
  "competitorUrls": [
    "https://www.tripadvisor.com/Hotel_Review-g60763-d2345678-Reviews-Competitor_One-New_York_City_New_York.html"
  ],
  "includeGeoCompSetCandidates": true
}
```

A competitor you list is pinned. It stays in the comparison across runs even when the automatic
selection would drop it, so a comparison does not reshuffle underneath you.

The input form comes prefilled with a small demonstration: one public hotel list page and a
three-property limit, so a first run exercises the whole pipeline cheaply. Replace those values
with your own before setting up a recurring watchlist. Running with a completely empty input
falls back to the same demonstration.

### Use a general-purpose Tripadvisor scraper when

![Rows in, shortlist out: 40,000 reviews from a generic scraper against 6 properties to investigate, each with its reason](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/before-after.png)

You need the lowest cost per raw record, every reachable historical review, restaurants or
attractions, or a general dataset to analyse yourself. Those actors are very good at that job,
and a bulk exporter is the cheaper way to get raw rows.

Use this one when you need:

- Change detection across scheduled runs
- Portfolio-level attention prioritisation
- Evidence-backed complaint themes
- Stable comp-set comparisons
- Operational routing to a responsible team
- Explicit freshness, coverage and confidence on every conclusion

### What you pay for

**$0.35 per property analysed.** One charge, for a property that went through review collection
and analysis: reviews gathered, themes measured, changes compared against its stored baseline,
and any attention item produced with the evidence behind it.

Nothing else is charged. A property that only received the quick details check is not billed,
because deciding which properties deserve the expensive collection is this actor's job rather
than something to charge you for. Neither are review rows, since the collection that produced
them is already covered by the analysis. Nor are deferred properties, failures, or the run
summary. Apify bills its own platform compute separately, as it does for every actor.

In practice a 40-property portfolio on a weekly schedule, where roughly half the properties come
due for collection each run, costs about $7 per run. Use `maxHydratedProperties` to put a hard
ceiling on it.

### Input parameters

| Parameter | Type | Notes |
|---|---|---|
| `mode` | select | monitor, audit, compset, discover, reviews. Inferred when omitted, see below. |
| `propertyUrls` | array | Tripadvisor hotel page URLs. Up to 500 entries. |
| `discoveryUrls` | array | Hotel list page URLs for a city or area. Up to 50. |
| `watchlistName` | string | A label shown on every record and alert. Renaming never resets history. |
| `focalPropertyUrl` | string | Comp-set mode: the property everything is compared against. |
| `competitorUrls` | array | Pinned competitors. Up to 30. |
| `includeGeoCompSetCandidates` | boolean | Also score already-collected nearby properties for comparability. |
| `compSetRadiusKm` | integer | One input to comparability, never the deciding one. Default 3. |
| `collectionPolicy` | select | Overrides the mode's collection depth. Leave empty to let the mode choose. |
| `recentWindowDays` | integer | The window that counts as recent. Default 90. |
| `baselineWindowDays` | integer | How far back the comparison baseline may reach. Default 365. |
| `targetReviewSample` | integer | Per-property collection target. Collection also stops on its other conditions. |
| `maxStalenessDaysOverride` | integer | Force a periodic deep collection at least this often. Leave empty to derive it from each property's observed review velocity. |
| `controlSampleShare` | number | Share of properties given a deep collection each run regardless of apparent change, as a check on the selection itself. Default 0.05. Set 0 to disable. |
| `analysisLanguage` | select | English in this version. |
| `includeReviewRecords` | boolean | Add one row per collected review. The mode decides when omitted. |
| `includeReviewerDisplayName` | boolean | Off by default. See Responsible use. |
| `propertyMetadata` | array | Your own identifiers, attached to every record. |
| `watchlistId` | string | Advanced. See below. |
| `resetScope` | select | Destructive. See below. |
| `maxProperties` | integer | Effective default 50. The input form prefills 3 so a first run is cheap; that prefill is a form value, not the default. Schema maximum 500. |
| `maxHydratedProperties` | integer | How many properties receive review collection in one run. The mode chooses when omitted. |
| `maxRunMinutes` | integer | Wall-clock budget. Default 60. |
| `confirmReset` | boolean | Required for `resetScope` to take effect. |
| `proxyConfiguration` | object | See Connection below. |

#### Mode inference

When `mode` is omitted it is resolved in this order, and the first match wins:

1. A focal property **and** at least one competitor URL, `compset`
2. Otherwise, any property URLs, `monitor`
3. Otherwise, any hotel list pages, `discover`
4. Otherwise, `monitor`

So a run carrying both property URLs and a focal property without competitors resolves to
`monitor`, and one carrying both hotel list pages and property URLs resolves to `monitor` with
the list pages still used for discovery. Set `mode` explicitly whenever you want something other
than that.

#### Connection

The run uses the Apify proxy you configure, defaulting to a residential group in the United
States, which is the configuration the collection was measured against. Residential access
depends on your Apify plan. If the proxy configuration cannot be created the run continues
without a proxy rather than failing, which on a restricted plan is likely to mean more properties
come back as retrieval failures. Country choice can affect the locale a page is served in, so a
portfolio outside the United States may be worth pointing at a closer country.

#### Portfolio size

The schema accepts up to 500 properties in one run, and reaching a run limit defers properties
rather than dropping them. The actor is designed for portfolios of roughly 5 to 200 properties;
500 is the configured maximum, not a benchmarked throughput figure. Use `maxProperties`,
`maxHydratedProperties` and `maxRunMinutes` to bound a run, and expect deferrals above the range
it was designed for.

#### `watchlistId`

Normally omit it on every run, including the first. The actor resolves a stable identifier for
the watchlist and reports it on every record and in the run summary.

Identity is pinned by the properties, not by the name. The first run registers its properties
against a new identifier; later runs recognise the watchlist from any property already registered
to it and adopt newly added properties into that same identifier. So renaming the watchlist,
adding properties, or removing them all keep one continuous history.

The consequence to know about: **overlap means identity.** Two portfolios that share even one
property resolve to the same watchlist and therefore the same history. If you monitor two
portfolios that genuinely overlap, give at least one of them its own explicit `watchlistId` so
they stay separate. For the same reason, when splitting one portfolio into two, assign a new
explicit `watchlistId` to at least one of the halves. Leaving both unset makes both halves
continue the parent's history.

Supply an explicit `watchlistId` only when deliberately continuing, separating, or merging a
history. Two watchlists given the same explicit id share one history.

#### `resetScope`

Destructive, and deliberately explicit. **A reset scope on its own does nothing.** It takes
effect only when `confirmReset` is also set to true; without that the run proceeds normally with
your history intact and records the unconfirmed request in the run summary. Whatever is reset is
also reported there, because a silent history wipe is indistinguishable from a monitoring fault.
Applies to the properties in the current run.

| Value | Effect |
|---|---|
| `none` | Keep all history. The default. |
| `propertyBaselines` | Forget previous observations. Change detection restarts from this run. |
| `signalHistory` | Forget open signals and their lifecycle. |
| `compSet` | Forget stored comparison members. |
| `reviewBoundary` | Collect reviews from scratch on the next run. |
| `entireWatchlist` | Forget everything stored for these properties. |

A reset cannot be undone from inside the actor, and re-running does not rebuild what was
discarded. That is why it requires `confirmReset` alongside the scope.

### Output

![Sample dataset output: ranked properties with priority, the reason and the routed owning team](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/output-table.png)

One dataset with typed rows. Read `recordType` first, then the fields belonging to that kind.

| Record type | What it is |
|---|---|
| `attention` | The queue. One item per consolidated property issue, ranked. |
| `property` | One row per property: ratings, subrating trajectory, response metrics, freshness, coverage, position. |
| `signal` | Every detected change with its evidence, confidence and reason codes. Suppressed signals are included, with the reason. |
| `review` | Individual reviews, when you switch them on. |
| `compset` | Who a property is compared against, and why each member is in or out. |
| `failure` | Source retrieval that did not succeed, with its type and diagnostics. |
| `runSummary` | What the run covered, what it deferred, and where its conclusions stop. |

Eight views are configured: Attention queue first, then Properties, Signals, New reviews,
Reviews, Comp-set, Failures, Run summary.

**Deferral is not failure.** A property that hit a run limit stays a `property` record with
`processingStatus: "deferred"`, the reason, and when it becomes eligible again. `failure` records
are for source retrieval that did not succeed. A 100-property run with six retrieval failures
still emits the full set of records for the 94 that succeeded, plus six explicit failure records,
never one opaque failed run.

**Emission order.** Property, signal and review records are written as each property finishes, so
the dataset fills during the run. The attention queue is ranked across the whole portfolio, so
final ranked attention records are written after portfolio scoring completes.

Suppressed signals are emitted alongside active ones so a suppression is auditable rather than
invisible. On a large portfolio they can be a meaningful share of the rows; filter by
`recordType` and the `suppressed` flag, or use the Signals view.

#### An attention record, shortened

```json
{
  "recordType": "attention",
  "orderingRank": 1,
  "propertyName": "Riverside Hotel Midtown",
  "internalPropertyId": "NYC-01",
  "priority": "high",
  "theme": "cleanliness",
  "lifecycleTransition": "worsening",
  "strongestReason": "Cleanliness share of reviews rose 14 percentage points, from 9% to 23%.",
  "suggestedOwner": "housekeeping",
  "priorityComponents": {
    "severity": 0.85, "acceleration": 0.62, "confidence": 0.7,
    "recency": 1, "persistence": 0.2, "portfolioOutlier": 0.33
  },
  "calibrationStatus": "provisional",
  "confidence": {
    "overall": "moderate", "classification": "moderate",
    "sample": "moderate", "freshness": "high", "baseline": "moderate"
  },
  "evidence": { "matchingReviews": 9, "recentMatching": 6, "sampleShare": 0.23, "reviewIds": ["ta_..."] },
  "supportingSignalIds": ["sig_1234567_issueAccelerating_cleanliness"],
  "alertEvent": { "eventKey": "evt_...", "eventType": "signalWorsened", "priority": "high" }
}
```

Confidence is decomposed rather than collapsed into one band, because a single band hides whether
a signal is thin on evidence or thin on collection.

![Why one property reached the top of the queue: theme share, direction, subrating, evidence count, confidence and portfolio position, ending in the routed team](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/decision-trace.png)

### Alerts and integrations

An attention item that crosses an alert condition carries an alert event: a compact payload with
the property, your own property id, the priority, the theme, the confidence and the evidence
count. Items that do not cross a condition carry `alertEvent: null`, so a persistent issue does
not re-page your team every run.

The conditions are fixed in this version:

| Event | Fires when |
|---|---|
| `newCriticalSignal` | A new issue appears at high or critical priority |
| `signalWorsened` | An open issue worsens, or one previously closed reappears |
| `signalImproved` | An open issue improves materially |
| `signalClosedByEvidence` | An issue closes on evidence, not on absence |
| `ratingThresholdCrossed` | The headline rating moves across a threshold |
| `propertyBecamePortfolioOutlier` | A property becomes a clear outlier against the rest of the portfolio |
| `freshnessLagging` | Collection for a property falls behind its own review velocity |
| `repeatedCollectionFailure` | A property fails retrieval across several consecutive runs |

`eventKey` is derived from the watchlist, the property, the theme, the lifecycle transition and
the observation version. It contains no run id and no timestamp, so a retried run over the same
effective observation produces the same key. The observation version advances only when a
property's stored state actually advances, not when a run is repeated. Deduplicate on `eventKey`
and your team is paged once per event.

Wire it up with a scheduled run plus an Apify webhook or integration into Slack, email, a
warehouse or your own endpoint. The full set is also written to the run's key-value store under
`ALERT_EVENTS`, with the attention snapshot under `ATTENTION_QUEUE` and the run summary under
`SUMMARY`.

### Using the API

Replace the input below with your own properties.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run = client.actor("ryanclinton/tripadvisor-hotel-reputation-monitor").call(run_input={
    "mode": "monitor",
    "watchlistName": "Northeast portfolio",
    "propertyUrls": ["https://www.tripadvisor.com/Hotel_Review-g60763-d1234567-Reviews-Property_Name-New_York_City_New_York.html"],
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("recordType") == "attention":
        print(item["orderingRank"], item["propertyName"], item["priority"], item["strongestReason"])
```

#### JavaScript

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });

const run = await client.actor('ryanclinton/tripadvisor-hotel-reputation-monitor').call({
    mode: 'monitor',
    watchlistName: 'Northeast portfolio',
    propertyUrls: ['https://www.tripadvisor.com/Hotel_Review-g60763-d1234567-Reviews-Property_Name-New_York_City_New_York.html'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const queue = items.filter((i) => i.recordType === 'attention');
console.log(queue.map((i) => `${i.orderingRank}. ${i.propertyName} (${i.priority})`));
```

Alert events for the run:

```javascript
const alerts = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('ALERT_EVENTS');
```

### How reliability works

![The stack from Tripadvisor hotel pages through identity, selective collection, theme analysis, change detection, evidence and consolidation to a ranked attention queue](https://apifyforge.com/readme-assets/ryanclinton-tripadvisor-hotel-reputation-monitor/intelligence-layers.png)

- **Identity before anything else.** Properties are keyed on their stable numeric Tripadvisor id,
  not the name in the URL, so a rename or a URL change does not split one property's history in
  two. Two URLs for the same property are one property, and you are told so.
- **Selective collection with a floor.** Review collection is expensive, so it goes to properties
  that changed, that carry an open high-priority signal, or that are due a periodic deep
  collection. A reproducibly selected control share gets one each run regardless, as a
  check on the selection itself. The selection is derived from the property and the run index,
  not from a random draw, so a rerun makes the same choices.
- **Adaptive collection with a stated stop reason.** Collection stops on whichever condition fires
  first, and the condition that fired is reported on the property record, so a thin sample is
  always explainable.
- **Memory only advances when the collection earned it.** If a property's collection did not pass
  its checks, the previous baseline is kept rather than moved. A boundary advanced on bad data
  corrupts every later run's "what is new", permanently and silently.
- **A partial collection never closes a signal.** Improvement needs evidence, and absence in a
  degraded collection is not evidence.
- **Absence is never reported as absence of a problem.** You will see "no matching reviews
  observed", never "no problems".
- **Recalculations are visible.** Every pinned version behind the numbers is on the run summary,
  and changing a setting that alters interpretation marks the run as not directly comparable to
  the previous one.

### Limitations

- Hotels and lodging only in this version. Restaurants, attractions and vacation rentals are out
  of scope.
- Review text analysis runs on English. Reviews in other languages still count towards ratings,
  subratings, volume and response metrics, and the excluded share is reported per property and
  lowers the confidence attached to every signal for that property.
- Coverage of the full public review history is never claimed. Historical and recent-window
  coverage are reported separately, because the first will always be small on a property with
  thousands of reviews while the second can be complete.
- Subrating tracking and management-response measurement depend on the source exposing usable
  values. Where it does not, those fields are reported unavailable with the reason rather than
  estimated.
- Where a review has no stable identifier at the source, a content change is reported as possible
  rather than confirmed, and a review that stops being visible is only reported after several
  successful collections. The actor never infers why a review disappeared.
- Attention priority weights are provisional until calibrated against a labelled validation set.
  Every attention row carries `calibrationStatus` saying so.
- The first run of a new watchlist produces a baseline rather than change detection.
- Comp-set position depends on how many comparable properties were actually collected. With a
  small comp set the position is reported as insufficient evidence rather than guessed.
- The output is decision support. It tells you what deserves investigation and why. It does not
  make the management decision, forecast rates or revenue, or predict what a rating will do next.

### What this actor will not do

It does not book or modify reservations, quote live rates or availability, forecast rates or
revenue, decide whether a review is genuine, identify or profile reviewers, or classify a
property or an area as unsafe. It may report that reviewers raised a safety concern. It will not
tell you a property is unsafe.

Reviews are evidence of what reviewers reported. They are not proof that an operational fact
occurred exactly as described, and the actor does not present them as such.

### Responsible use

It accesses publicly available pages without logging in, without solving verification puzzles,
and without using anyone's credentials. It takes no action inside Tripadvisor.

**Reviewer privacy.** Reviewer profile links, contribution counts and stated home locations are
not parsed into output or into persistent analytical state, in any configuration. A reviewer
display name is read only when you switch `includeReviewerDisplayName` on. Raw page markup,
screenshots and browser traces are not persisted: the run's stored artifacts are the dataset
records and the three key-value summaries, nothing else.

**Legality.** Whether any particular collection is lawful depends on your jurisdiction, the
source's terms, the data collected and your intended use. This actor is designed to read public
pages without login and to minimise reviewer identity data. Take your own advice on your own use
case.

### Troubleshooting

**The first run shows no changes.** Expected. The first run establishes a baseline; change
detection starts from the second.

**A property came back deferred.** A run limit was reached. It kept its previous baseline, and
the record carries the reason and when it becomes eligible again. Raise `maxProperties`,
`maxHydratedProperties` or `maxRunMinutes`, or split the portfolio across scheduled runs.

**Response metrics say unavailable.** Management-response timestamps were not readable for that
property. The reason is on the record. Coverage is still reported where responses themselves are
visible.

**Renaming the watchlist did not reset anything.** Correct. History is keyed on an id resolved
from the properties, not the name.

**A signal is marked suppressed.** It did not clear its evidence threshold, or the run was not
comparable to its baseline. The reason codes are on the record.

### FAQ

**Is this a Tripadvisor scraper?** It reads Tripadvisor, but it is built to return changes, not
records. If you want a bulk export of raw reviews, a general-purpose scraper is the cheaper and
better-suited tool.

**How often should I run it?** Weekly suits most portfolios. Daily is reasonable on high-volume
properties. The actor tracks each property's own review velocity and forces a periodic deep
collection regardless of apparent change.

**Can I get the raw reviews too?** Yes, set `includeReviewRecords` to true in any mode.

**Why do attention items say provisional?** The priority weights have not yet been calibrated
against a labelled validation set. The ranking is deterministic and reproducible; the weights
themselves may be revised, and every row says so.

**Does it detect fake reviews?** No, and it does not try. Judging authenticity is not something
this data supports.

**What happens if a page cannot be retrieved?** You get an explicit `failure` record with the
type and diagnostics. Other properties are unaffected, and no baseline is advanced for the
property that failed.

### Not affiliated with Tripadvisor

This actor is an independent tool that reads publicly available pages. It is not affiliated with,
endorsed by, or connected to Tripadvisor.

# Actor input Schema

## `mode` (type: `string`):

Monitor tracks a portfolio over time and returns only what changed since the last run, which is what the actor is built for. Audit runs a one-off evidence-based review of specific properties. Comp-set compares one property against named competitors and nearby candidates. Discover turns a hotel list page into property URLs you can paste into the other modes. Reviews returns a review-forward output shape for migrating an existing pipeline.

## `propertyUrls` (type: `array`):

Tripadvisor hotel page URLs to monitor or audit, in the form https://www.tripadvisor.com/Hotel\_Review-g60763-d1234567-Reviews-Property\_Name-City.html. Properties are identified by their numeric Tripadvisor id rather than the name in the URL, so a rename or a URL change does not split a property's history in two.

## `discoveryUrls` (type: `array`):

Tripadvisor hotel list page URLs for a city or area, in the form https://www.tripadvisor.com/Hotels-g60763-New\_York\_City\_New\_York-Hotels.html. Every property found on the page is added to the run, up to your property limit. Use this when you want a location rather than a hand-listed set of properties.

## `watchlistName` (type: `string`):

A label for this portfolio, shown on every record and every alert. History is stored against a stable internal id derived from the properties themselves, so renaming a watchlist never resets its history and two watchlists with the same name never collide.

## `focalPropertyUrl` (type: `string`):

Comp-set mode: the Tripadvisor hotel page URL of the property everything else is compared against.

## `competitorUrls` (type: `array`):

Tripadvisor hotel page URLs of the competitors you want in the comparison. A competitor you list here is pinned: it stays in the comp set across runs even when the automatic selection would drop it, so the comparison does not reshuffle underneath you.

## `includeGeoCompSetCandidates` (type: `boolean`):

Also consider the other properties collected in this run as comp-set candidates, scored on how comparable they actually are. Suggestions are returned separately from confirmed comparables so nothing is silently added to your comparison.

## `compSetRadiusKm` (type: `integer`):

How far from the focal property a candidate can be and still be considered comparable. Distance is one input among several, never the deciding one.

## `maxCompSetCandidates` (type: `integer`):

Upper limit on how many nearby properties are scored for comparability in a single run.

## `collectionPolicy` (type: `string`):

Leave this empty and the mode picks for you: monitor collects only what is new since the last run, audit collects a diagnostic sample, comp-set covers the recent window, discover collects details only, and reviews collects to a target sample. Set it explicitly when you want to override that.

## `recentWindowDays` (type: `integer`):

The window that counts as recent for change detection, theme shares and the freshness contract. Shorter windows react faster and carry thinner samples, and the confidence reported with every signal reflects that.

## `baselineWindowDays` (type: `integer`):

How far back the comparison baseline is allowed to reach when previous observations exist.

## `targetReviewSample` (type: `integer`):

How many reviews a full collection aims to gather per property. Leave empty to let the mode choose. Collection also stops on its other conditions, so the number actually collected can be lower and the reason is reported per property.

## `maxStalenessDaysOverride` (type: `integer`):

Every property is fully collected at least this often even when nothing on its summary appears to have moved, because a property can deteriorate while its rounded rating holds. Leave empty and the interval is set from each property's own observed review velocity.

## `controlSampleShare` (type: `number`):

Share of properties that receive a full collection each run regardless of whether anything appeared to change, as a check on the selection itself. Set to 0 to switch the control sample off.

## `analysisLanguage` (type: `string`):

Review text analysis is supported for English in this version. Reviews in other languages still count towards ratings, subratings, volume and response metrics, and the excluded share is reported per property and lowers the confidence attached to every signal.

## `translationPreference` (type: `string`):

Whether to work with reviews as the page presents them, or to prefer the original text where both are shown. This is recorded on every property so a difference between two runs is explainable rather than mysterious.

## `includeReviewRecords` (type: `boolean`):

Add one row per collected review alongside the property and decision rows. Leave empty and the mode decides: the review-forward migration mode includes them, the decision modes do not. Turn it on in any mode when you want the underlying rows in your warehouse.

## `includeReviewerDisplayName` (type: `boolean`):

Off by default. When on, review rows carry the display name shown on the review. No other reviewer detail is ever collected or returned: no profile links, no contribution counts and no home location, whatever this is set to.

## `propertyMetadata` (type: `array`):

Optional list of objects that attach your own identifiers to each property, for example \[{"propertyId":"1234567","internalPropertyId":"NYC-01","region":"Northeast","owner":"Jane Doe","brand":"Riverside"}]. Match on propertyId or url. These values are carried on every record and every alert in their own namespace, so the output joins to your operating structure without a lookup and never overwrites anything read from the source.

## `watchlistId` (type: `string`):

Advanced. Pin the internal id that history is stored against. Leave empty and it is resolved automatically from the properties, which is what keeps history intact when you rename a watchlist or add and remove properties.

## `resetScope` (type: `string`):

Deliberately discard part of the stored history before this run. What was reset is reported in the run summary, because a silent history wipe is indistinguishable from a monitoring fault. Leave on None unless you specifically want to start a baseline again.

## `confirmReset` (type: `boolean`):

Required for a reset to actually happen. Discarded history cannot be rebuilt by re-running, so a reset scope on its own does nothing: leave this off and the run proceeds normally with your history intact, and the run summary records that an unconfirmed reset was requested. Switch it on only when you intend to discard the history you selected above.

## `maxProperties` (type: `integer`):

Upper limit on properties handled in one run. Anything above the limit is still returned, marked as deferred with the reason and when it becomes eligible again, so a portfolio never silently loses members.

## `maxHydratedProperties` (type: `integer`):

Upper limit on how many properties receive the expensive review collection in one run. Leave empty and the mode chooses. Properties are selected by what actually changed, what has an open high-priority signal, and what is due for its periodic full collection.

## `maxRunMinutes` (type: `integer`):

Wall-clock budget for the run. On reaching it the run stops collecting, emits everything already produced, and leaves untouched properties on their previous baselines.

## `maxReviewInteractionsPerProperty` (type: `integer`):

Upper bound on how far collection will go on a single property. Collection is adaptive and usually stops well before this on its own conditions, which are reported per property.

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

Accepted for migrating an existing pipeline. Property URLs and hotel list pages are separated by URL kind rather than assumed, and every alias applied is reported back in the run summary so the migration is auditable.

## `maxReviewsPerProperty` (type: `integer`):

Accepted for migrating an existing pipeline. Applied as the per-property collection target. Collection also stops on its other conditions, so the number returned can be lower and the reason is reported per property.

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

Accepted for migrating an existing pipeline. Used as the language for review text analysis. Review text analysis is supported for English in this version, and any other value is reported as a behavioural difference rather than silently accepted.

## `locationFullName` (type: `string`):

Accepted for migrating an existing pipeline. A location name on its own does not resolve to a hotel list page here, so supply the list page URL in Hotel list pages instead. Setting this field records the difference in the run summary rather than guessing a location.

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

Proxy used to reach Tripadvisor. Residential proxy in the United States is the default and is what the collection was measured against. Each retry is issued a fresh exit, which is the whole point of retrying.

## Actor input object example

```json
{
  "mode": "monitor",
  "discoveryUrls": [
    "https://www.tripadvisor.com/Hotels-g60763-New_York_City_New_York-Hotels.html"
  ],
  "watchlistName": "My hotel portfolio",
  "includeGeoCompSetCandidates": false,
  "compSetRadiusKm": 3,
  "maxCompSetCandidates": 8,
  "recentWindowDays": 90,
  "baselineWindowDays": 365,
  "controlSampleShare": 0.05,
  "analysisLanguage": "en",
  "translationPreference": "sourceDisplayed",
  "includeReviewerDisplayName": false,
  "resetScope": "none",
  "confirmReset": false,
  "maxProperties": 3,
  "maxRunMinutes": 60,
  "maxReviewInteractionsPerProperty": 14,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `attentionQueue` (type: `string`):

The default read: which property needs attention, what changed, and who should act.

## `properties` (type: `string`):

One row per property with ratings, subrating movement, response metrics, freshness and coverage.

## `signals` (type: `string`):

Every detected change with its evidence, decomposed confidence and reason codes.

## `newReviews` (type: `string`):

Reviews seen for the first time this run.

## `reviews` (type: `string`):

Review-forward shape for migrating an existing pipeline.

## `compSet` (type: `string`):

Who each property is compared against, and why each member is in or out.

## `failures` (type: `string`):

Properties that could not be collected, each with its failure type and diagnostics.

## `runSummaryView` (type: `string`):

What the run covered, what it deferred, and where its conclusions stop.

## `summary` (type: `string`):

The run summary as a single stored record.

## `attentionArtifact` (type: `string`):

Compact ranked snapshot of the attention queue for a five-second read.

## `alertEvents` (type: `string`):

Compact alert payloads for webhooks, each with a deterministic key to deduplicate on.

## `allRecords` (type: `string`):

Every row this run produced, in the order it was produced.

# 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 = {
    "discoveryUrls": [
        "https://www.tripadvisor.com/Hotels-g60763-New_York_City_New_York-Hotels.html"
    ],
    "watchlistName": "My hotel portfolio",
    "maxProperties": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryanclinton/tripadvisor-hotel-reputation-monitor").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 = {
    "discoveryUrls": ["https://www.tripadvisor.com/Hotels-g60763-New_York_City_New_York-Hotels.html"],
    "watchlistName": "My hotel portfolio",
    "maxProperties": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("ryanclinton/tripadvisor-hotel-reputation-monitor").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 '{
  "discoveryUrls": [
    "https://www.tripadvisor.com/Hotels-g60763-New_York_City_New_York-Hotels.html"
  ],
  "watchlistName": "My hotel portfolio",
  "maxProperties": 3
}' |
apify call ryanclinton/tripadvisor-hotel-reputation-monitor --silent --output-dataset

```

## MCP server setup

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

```

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/PZOkZxkwqbJb1EWmg/builds/auGZfUOUs9OvOTd6w/openapi.json
