# Hotel Reviews Monitor - New Agoda, Trip.com, Kurzurlaub Reviews (`neverempty/hotel-reviews-monitor`) Actor

For hotel revenue managers and review-alert tools: only the new guest reviews from Agoda, Trip.com and Kurzurlaub.de, with rating, date, text, reviewer, room and the hotel's reply. One property took 2-29 s in production runs (2026-09-23). For daily or hourly schedules; nothing new = one free row.

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

## Pricing

from $3.50 / 1,000 new review returneds

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Hotel Reviews Monitor - New Agoda, Trip.com, Kurzurlaub Reviews

For hotel revenue managers and review-alert tools that check each property daily: the reviews not returned before, from Agoda, Kurzurlaub.de and Trip.com (the about 10 on its hotel page), with rating, date, text, reviewer, room or package, category scores and the hotel's reply. In production runs on 2026-09-23, one property per run took 2 to 29 seconds and a check with nothing new returned one free row. Put one property on an hourly or daily schedule and you get a row only when a guest writes something, with no start date to track and no diffing on your side.

Unofficial. Public data only.

Export as JSON, CSV or Excel, or read the dataset from the API after each scheduled run.

### What you get

- **Only new reviews.** Every run remembers which reviews it has returned for each property. The next run returns only the ones it has not returned before. You never type a start date.
- **One small check per property.** Built to be run often: one property per run, usually one request and a few seconds (Agoda hotels with reviews in many languages: up to about 40 requests and 30 seconds). Put each property on its own schedule, or list up to 20 in one run.
- **Three sites, one row format.** Agoda (including the Booking.com and Priceline.com reviews Agoda displays), Trip.com and Kurzurlaub.de come back with the same columns, and `source` and `reviewSite` say where each review is from.
- **A clear answer every time.** A property with nothing new, no reviews, a page that does not exist, a refused request or a spending limit each comes back as a free row that says why. A scheduled run never ends silently.

### Measured on 2026-09-23 (Apify production, one property per run)

| Site | Property | First check | Later check with nothing new | Requests per check | Platform cost per run |
|---|---|---|---|---|---|
| Kurzurlaub.de | Schloss Lieser (187 reviews) | 4.5 - 6.7 s, 10 rows (9.0 s for 25) | 1.6 - 4.1 s, 1 free row | 1 (+1 when the page URL is new) | $0.00009 - $0.00048 |
| Trip.com | 5L Hotel, Beijing (2,324 reviews) | 4.6 - 6.7 s, 10 rows | 3.0 - 10.6 s, 1 free row | 1 | $0.00005 - $0.00031 |
| Agoda | Marine Hotel, Dublin (1,389 reviews incl. Booking.com) | 18.6 - 20.8 s, 10 rows | 4.7 - 14.0 s, 1 free row | 1 - 40 | $0.00005 - $0.00049 |

First checks of Imperial Hotel Tokyo and Sheraton Grand Taipei on Agoda took 29 s and 22 s; Kurzurlaub.de and Trip.com first checks of five other hotels took 3-5 s. Run times include the container start. Agoda needs more requests for hotels whose reviews are spread over many languages (see "How new reviews are found").

### Input

| Field | What it does |
|---|---|
| `propertyUrls` | Hotel page URLs from Agoda (`https://www.agoda.com/marine-hotel/hotel/dublin-ie.html`), Trip.com (`https://www.trip.com/hotels/beijing-hotel-detail-375126/`) or Kurzurlaub.de (`https://www.kurzurlaub.de/hotel/mosel_saar/lieser/hotel-schloss-lieser.html`, or one of the hotel's package pages). Up to 20 per run. |
| `maxReviewsFirstRun` | The first time a property is checked, return this many of its newest reviews and only remember the rest. 0 = return none and start watching from now. Empty = 10. A first check reads at most 50 reviews on Kurzurlaub.de and about 10 on Trip.com, so a larger number returns no more there. Later runs return every new review. |
| `includeOtherSites` | Agoda only. On or empty = also return the Booking.com and Priceline.com reviews Agoda shows. Off = only reviews written on Agoda. Switching it starts a new watch for Agoda properties (the next run is a first check again). |
| `watchName` | Optional. Runs with the same watch name share what has already been returned. Give two schedules that watch the same property different names so each gets every new review. |
| `resetMonitoringState` | Forget what this watch remembered for these properties at the start of this run, so this run is a first check again. Turn it off again for scheduled runs. |

### Output

```json
{
  "source": "trip.com",
  "status": "ok",
  "propertyId": "375126",
  "propertyName": "5L Hotel",
  "propertyUrl": "https://www.trip.com/hotels/beijing-hotel-detail-375126/5l-hotel/",
  "inputUrl": "https://www.trip.com/hotels/beijing-hotel-detail-375126/5l-hotel/",
  "reviewId": "2106132610",
  "reviewSite": "Trip.com",
  "rating": 5.5,
  "ratingScale": 10,
  "ratingText": null,
  "reviewDate": "2026-09-17",
  "reviewDateTime": "2026-09-17T15:21:25",
  "stayDate": "2026-09",
  "title": null,
  "text": "The room was very old. TV wasn't working well. There was no telephone in the room, so I could only reached front desk by using my cell phone to call. Toilet bowl flushing system was faulty.",
  "positives": null,
  "negatives": null,
  "language": "en",
  "translatedTitle": null,
  "translatedText": null,
  "translatedLanguage": null,
  "reviewerName": "YSTT",
  "reviewerCountry": "Singapore",
  "travelerType": "Traveling with friends",
  "roomType": "Apartment Of Twin Beds",
  "nightsStayed": null,
  "packageName": null,
  "categoryRatings": { "location": 8, "amenities": 4, "service": 6, "cleanliness": 4 },
  "helpfulVotes": 0,
  "hotelResponse": "Dear Guest, thank you for sharing your feedback with us. We’re truly sorry that your stay did not meet your expectations. ...",
  "hotelResponseDate": "2026-09-17",
  "photoUrls": [],
  "positionOnPage": 1,
  "isFirstCheck": true,
  "propertyReviewCount": 2324,
  "propertyRating": 9,
  "propertyRatingScale": 10,
  "checkedAt": "2026-09-23T11:03:45.616Z"
}
```

- `rating` is on the scale in `ratingScale`: 10 for Agoda and Trip.com, 6 for Kurzurlaub.de (its own 1-6 points).
- `reviewDate` is the date the site gives for the review. `stayDate` is the stay (Agoda: check-in day; Trip.com: month only).
- `isFirstCheck` is true for rows from a property's first check (its newest reviews, not necessarily new ones) and false for reviews that are new since the watch started.
- `propertyRating` is not filled for Agoda (it would cost an extra request on every run).
- `positionOnPage` is only filled for Trip.com.
- Columns a site does not have are `null`, never guessed: Kurzurlaub.de has no review title, reviewer country or hotel reply; Trip.com has no title or pros and cons; Booking.com reviews shown on Agoda often have only pros and cons, no text.

Free rows have a `status` other than `ok` and a `note` that says why: `no-new-reviews`, `watch-started` (first check with `maxReviewsFirstRun` 0), `no-reviews`, `not-found`, `blocked`, `unreadable`, `bad-input`, `budget-reached`.

### How new reviews are found

- **Kurzurlaub.de** lists a hotel's reviews newest first, 10 per page. A check reads page 1 and goes on only while every review on the page is new. Kurzurlaub.de reviews have no ID of their own, so the ID is made from the hotel, date, reviewer first name and package; two reviews by the same first name on the same day for the same package count as one.
- **Agoda** sorts newest first, but within each language, so a new Spanish review can sit far down the list. A check reads the first 100 reviews of each site and then the newest 20 of each language that did not fit. Agoda also serves two slightly different copies of its review list to different visitors; a review that shows up for the first time but is dated more than 7 days before the watch started is remembered and not returned (the free row says how many).
- **Trip.com** shows about 10 reviews on the hotel page in its own "Most relevant" order, not newest first, and this Actor reads only that page. A review is returned when it first appears among them (`positionOnPage` says where), and the same 7-day rule applies. New Trip.com reviews that never reach those 10 are not returned.
- The first check returns up to `maxReviewsFirstRun` of the newest reviews. If a first check could not finish (for example it reached the spending limit), the next run finishes it and also returns every review dated from 2 days before that first check on.
- A check reads a limited number of pages (Kurzurlaub.de 5 pages; Agoda 3 pages of 20 per language). If every review on the last page is new, older new reviews beyond it may never be returned, and a free `unreadable` row says so; check busy properties more often.
- Hostelworld is not supported: its review service refuses Apify's network, and its property page shows only the 3 latest reviews without IDs.

### Scheduling tips

- One property per run keeps each run to seconds and makes each property's result its own dataset.
- Do not put the same property in two overlapping schedules with the same `watchName`. Apify's key-value store has no atomic update, so two runs finishing at the same moment can return the same new review twice. Different `watchName`s never interfere.
- The run start fee is charged even when nothing is new; that is what pays for the check.

### Pricing

Pay per event:

- **Run start: $0.02 per run** (Bronze $0.0182, Silver $0.0164, Gold $0.0146), charged once before the first request, whether or not a new review is found, and also when the site refuses the request. A run whose input cannot be used, or whose maximum total charge has no room for the start fee plus one review row, requests nothing and is charged nothing. A run restarted or moved by Apify is charged once.
- **New review returned: $5.00 per 1,000** (Bronze $4.50, Silver $4.00, Gold $3.50). Only rows with `status` `ok` are charged; every free row explains itself.

A daily check of one property with nothing new costs $0.02. A check that finds 3 new reviews costs $0.02 + $0.015.

### Limits

- Up to 20 properties per run.
- No proxy is used and site checks are never bypassed. A refused request (HTTP 403 or 429, or a check page) stops that site for the rest of the run with a free `blocked` row; nothing is remembered for that property, so the next run reads it again.
- Reviewer names are the display names the sites show publicly. Email addresses and long phone numbers written inside review text are masked.

# Actor input Schema

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

Hotel page URLs from Agoda (https://www.agoda.com/marine-hotel/hotel/dublin-ie.html), Trip.com (https://www.trip.com/hotels/beijing-hotel-detail-375126/) or Kurzurlaub.de (https://www.kurzurlaub.de/hotel/mosel\_saar/lieser/hotel-schloss-lieser.html). Up to 20 per run; one property per run keeps each run to a few seconds. Each run returns only the reviews that are new since the last run for that property.

## `maxReviewsFirstRun` (type: `integer`):

The first time a property is checked, return this many of its newest reviews and only remember the rest. 0 = return none and start watching from now. Empty = 10. A first check reads at most 50 reviews on Kurzurlaub.de and about 10 on Trip.com, so a larger number returns no more there. Later runs return every new review.

## `includeOtherSites` (type: `boolean`):

Agoda also shows reviews it takes from Booking.com and Priceline.com. On (or empty) = return them too, with reviewSite saying where each one comes from. Off = only reviews written on Agoda. Switching it starts a new watch for Agoda properties (the next run is a first check again).

## `watchName` (type: `string`):

Optional. Runs with the same watch name share what has already been returned. Give different names to two schedules that watch the same property for different purposes, so each gets every new review. Letters, digits, dot, dash and underscore.

## `resetMonitoringState` (type: `boolean`):

Forget what this watch remembered for these properties at the start of this run, so this run is a first check again. Turn it off again for scheduled runs, or every run starts over and returns the same reviews again.

## Actor input object example

```json
{
  "propertyUrls": [
    "https://www.agoda.com/marine-hotel/hotel/dublin-ie.html"
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

One row per new review: site, property ID and name, review ID, rating and scale, review date, title and text, language and translation, reviewer name and country, traveler type, room or package, category ratings, the hotel's reply, and whether the row comes from the first check. A property with no new review, no reviews, a page that does not exist, a refused request or a run that hit its maximum charge comes back as a free row that says why.

# 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 = {
    "propertyUrls": [
        "https://www.agoda.com/marine-hotel/hotel/dublin-ie.html"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/hotel-reviews-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 = { "propertyUrls": ["https://www.agoda.com/marine-hotel/hotel/dublin-ie.html"] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/hotel-reviews-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 '{
  "propertyUrls": [
    "https://www.agoda.com/marine-hotel/hotel/dublin-ie.html"
  ]
}' |
apify call neverempty/hotel-reviews-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/hotel-reviews-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/48F7cnnkEat8S6gEP/builds/39tg3Gslg7gYRG2iD/openapi.json
