# HolidayCheck Hotel Reviews Scraper (`fetchfoundry-labs/holidaycheck-reviews-scraper`) Actor

Extracts the full guest review history for any hotel on HolidayCheck (.de/.at/.ch) by hotel URL, including per-topic sentiment and rating facets.

- **URL**: https://apify.com/fetchfoundry-labs/holidaycheck-reviews-scraper.md
- **Developed by:** [Fetch Foundry](https://apify.com/fetchfoundry-labs) (community)
- **Categories:** Travel
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

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

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

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

## HolidayCheck Hotel Reviews Scraper

Extracts the full guest review history for any hotel listed on HolidayCheck (holidaycheck.de, .at, or .ch) — just give it the hotel page link.

### Why use this actor

- No account, no login, and no API key needed — works on public hotel pages only.
- Returns **every** guest review up to your chosen limit, not just the first page: review title, the guest's own recommendation, travel date, party type, room type, tour operator, and whether the stay was a verified booking.
- Includes HolidayCheck's structured detail you can't get from a plain review list: **per-topic sentiment with the exact quotes behind it** (location, parking, food, cleanliness, service…) and the guest's individual answers to HolidayCheck's rating questionnaire.
- Each hotel also gets a summary row with the star rating, average score, recommendation rate, address and contact details, plus the full rating histogram and traveller-type/season/age-group breakdowns.
- Stable JSON that drops straight into a spreadsheet, database, or analysis pipeline.
- Every hotel you submit gets at least one output row — including a clear diagnostic reason on the rare rows that couldn't be fetched, so nothing silently disappears from your results.

### How it works

1. You give the actor one or more HolidayCheck hotel page links (or bare hotel IDs).
2. For each hotel, it reads the hotel profile and then pages through that hotel's review history until it reaches your limit or runs out of reviews.
3. Results are streamed to your dataset as they finish, with automatic retries when HolidayCheck is temporarily unresponsive.
4. You export the finished dataset as JSON, CSV, or Excel straight from the Apify Console — no scrapers, browsers, or blocks to babysit.

### Input

```json
{
  "hotelUrls": [
    "https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6"
  ],
  "maxReviewsPerHotel": 300,
  "maxConcurrency": 3,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

| Field | Type | Description |
|---|---|---|
| `hotelUrls` | array of strings | HolidayCheck hotel page URLs (.de, .at or .ch). Both the reviews tab (`/hr/...`) and the hotel-info tab (`/hi/...`) work, and a bare hotel ID such as `454268e5-f1bb-304e-a92d-d4c994ae43c6` is also accepted. |
| `maxReviewsPerHotel` | integer | Stop after this many reviews per hotel. HolidayCheck serves 10 reviews per page, so a hotel with 1,000+ reviews needs 100+ page reads — hence the default cap. Set to `0` to fetch every review. Default `300`. |
| `maxConcurrency` | integer | How many hotels to process at the same time. Default `3`. |
| `proxyConfiguration` | object | Proxy settings. Off by default; Residential is recommended for large production runs. |

### Output

Each hotel produces one **summary** row plus one **review** row per guest review. Real examples from a live run:

**Summary row** (`recordType: "SUMMARY"`) — truncated:

```json
{
  "_input": "https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6",
  "_source": "S1-fluxible-state",
  "_scrapedAt": "2026-08-10T18:14:28Z",
  "recordType": "SUMMARY",
  "reviewUrl": "https://www.holidaycheck.de/hr/454268e5-f1bb-304e-a92d-d4c994ae43c6",
  "totalReviewCount": 1089,
  "indexedReviewsCount": 71,
  "hotelId": "454268e5-f1bb-304e-a92d-d4c994ae43c6",
  "name": "centrovital Hotel Berlin",
  "stars": 4.5,
  "rating": 4.975282211088373,
  "recommendation": 0.8662317836962519,
  "reviewCount": 1089,
  "pictureCount": 444,
  "phone": "+49 30 818750",
  "email": "info@centrovital-berlin.de",
  "address": "Brauereihof 6\r\n13585\r\nBerlin\r\nBerlin\r\nDeutschland",
  "geo": { "type": "Point", "coordinates": [13.2108027, 52.5467968] },
  "reviewFacets": {
    "season": { "spring": { "count": 236 }, "summer": { "count": 301 }, "autumn": { "count": 289 }, "winter": { "count": 263 } },
    "traveledWith": { "COUPLE": { "count": 571 }, "SINGLE": { "count": 288 }, "FAMILY": { "count": 133 }, "FRIENDS": { "count": 95 } },
    "ratingRounded": { "1": { "count": 10 }, "2": { "count": 18 }, "3": { "count": 42 }, "4": { "count": 105 }, "5": { "count": 446 }, "6": { "count": 468 } }
  }
}
```

**Review row** (`recordType: "REVIEW"`) — truncated:

```json
{
  "_input": "https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6",
  "_source": "S1-fluxible-state",
  "_scrapedAt": "2026-08-10T18:14:28Z",
  "recordType": "REVIEW",
  "id": "495fa2b2-4da6-442a-a3da-99c11aba7ec2",
  "title": "Gutes Konzept, aber mittlerweile verbesserungswürdig",
  "traveledWith": "COUPLE",
  "user": {
    "id": "ab40ab36-6411-3183-be4d-dec0d334f972",
    "firstName": "Uwe",
    "ageGroup": "FROM_61_TO_65",
    "hasPublicProfile": true,
    "hotelReviewCount": 260
  },
  "entryDate": 1780910793000,
  "travelDate": 1780272000000,
  "hotel": { "id": "454268e5-f1bb-304e-a92d-d4c994ae43c6", "name": "centrovital Hotel Berlin" },
  "originalLocale": "de",
  "recommendation": true,
  "proofedReservation": false,
  "additional": { "costPerformance": "ACCEPTABLE", "roomType": null, "roomView": null },
  "topics": [
    {
      "id": "LOCATION_AND_SURROUNDING",
      "name": "Lage und Umgebung",
      "sentiment": 1,
      "quotes": [
        "Es handelt sich um ein ans ich schönes Hotel an den Spandauer Seen gelegen auf einem alten Brauerei - Gelände.",
        "Die öffentlichen Verkehrsmittel fahren fast direkt vor der Tür und man ist auch schnell an der U-Bahn."
      ]
    },
    { "id": "PARKING", "name": "Parken", "sentiment": -1, "quotes": ["Parkplätze sind jedoc …"] },
    "… 16 more"
  ],
  "contributions": {
    "ROOM_SIZE": { "value": "1", "text": "Zimmergröße", "inputType": "RATING", "rating": { "sentiment": 1 } },
    "PUBLIC_TRANSPORT": { "value": "YES", "text": "ÖPNV-Erreichbar", "inputType": "CHOICES" },
    "SERVICE_OPINION": { "value": "Personal war freundlich und hilfsbereit, jedoch …", "text": "Erfahrungen mit dem Service", "inputType": "TEXT" }
  },
  "hasMedia": true,
  "mediaList": ["… 13 items"]
}
```

When the input isn't a recognisable HolidayCheck hotel link, the actor still emits one row for that input with a clear reason:

```json
{
  "_input": "https://example.com/nope",
  "_source": "none",
  "_scrapedAt": "2026-08-10T18:14:28Z",
  "recordType": "ERROR",
  "_error": "invalid_input",
  "_errorDetail": "Expected a holidaycheck.de/.at/.ch hotel URL containing a hotel UUID (e.g. https://www.holidaycheck.de/hr/bewertungen-<slug>/<uuid>) or a bare hotel UUID."
}
```

| Field | Type | Description |
|---|---|---|
| `_input` | string | The hotel URL or ID you submitted. |
| `_source` | string | Which internal strategy produced this row. `"none"` on a failed row. |
| `_scrapedAt` | string | UTC timestamp of when this row was produced. |
| `recordType` | string | `"SUMMARY"`, `"REVIEW"`, or `"ERROR"`. |
| `totalReviewCount` | integer | How many reviews this hotel has in total (summary row). |
| `reviewFacets` | object | Rating histogram plus season / traveller-type / age-group / room-type breakdowns (summary row). |
| `id` | string | HolidayCheck's stable ID for this review — safe to de-duplicate on. |
| `title` | string | The review headline written by the guest. |
| `recommendation` | boolean | Whether the guest recommends the hotel. |
| `proofedReservation` | boolean | Whether HolidayCheck verified the stay against a real booking. |
| `travelDate` / `entryDate` | integer | When the guest travelled / posted, as Unix milliseconds. |
| `topics` | array | Per-topic sentiment (`1`, `0`, `-1`) with the exact quotes from the review that produced it. |
| `contributions` | object | The guest's individual answers to HolidayCheck's rating questionnaire, keyed by question. |
| `mediaList` | array | Photos the guest uploaded with the review. |
| `_error` | string | Present only on rows that couldn't be fetched, e.g. `invalid_input`, `fetch_failed`. |
| `_errorDetail` | string | Human-readable detail for the error, present only alongside `_error`. |
| *(all other fields)* | — | On a successful row, every field HolidayCheck publishes for that hotel/review is passed through unchanged. |

### Notes / limits

- HolidayCheck is a German-language site: review text, titles, and topic names come back in German (the review's own language is reported in `originalLocale`).
- Reviews are served 10 per page, so a hotel with 1,000+ reviews is a long run. Keep `maxReviewsPerHotel` at a sensible cap unless you really need the whole history.
- Reviewer names are first names only — that is all HolidayCheck publishes.
- The same hotel ID works on all three country sites (.de/.at/.ch) and returns the same reviews; the actor keeps whichever host you submitted.
- A `partial_*` error row means pagination stopped partway through a hotel after already returning some real reviews — you still get what was fetched, plus a diagnostic explaining why it stopped early.

### Other actors in this collection

| Actor | What it returns |
|---|---|
| Priceline Hotel Reviews Scraper | Guest reviews for hotels listed on Priceline.com |
| Agoda Reviews Scraper | Guest reviews for hotels listed on Agoda.com |
| Booking.com Reviews Scraper | Guest reviews for hotels listed on Booking.com |
| Trip.com Reviews Scraper | Guest reviews for hotels listed on Trip.com |
| Orbitz Hotel Reviews Scraper | Guest reviews for hotels listed on Orbitz.com |
| Viator Reviews Scraper | Guest reviews for tours/activities listed on Viator |
| GetYourGuide Reviews Scraper | Guest reviews for tours/activities listed on GetYourGuide |

# Actor input Schema

## `hotelUrls` (type: `array`):

HolidayCheck hotel page URLs (.de, .at or .ch). Both the reviews tab (/hr/...) and the hotel-info tab (/hi/...) work, and a bare hotel UUID is also accepted.

## `maxReviewsPerHotel` (type: `integer`):

Stop paginating a hotel after this many reviews. HolidayCheck serves 10 reviews per page, so a popular hotel with 1,000+ reviews needs 100+ requests — hence the default cap. Set to 0 for unlimited (fetch every review).

## `maxConcurrency` (type: `integer`):

Maximum number of hotels processed in parallel.

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

Proxy settings. HolidayCheck was not observed to gate on IP reputation during recon, so a proxy is optional — but Residential is recommended for large production runs from shared platform IPs.

## Actor input object example

```json
{
  "hotelUrls": [
    "https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6"
  ],
  "maxReviewsPerHotel": 300,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "hotelUrls": [
        "https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchfoundry-labs/holidaycheck-reviews-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 = { "hotelUrls": ["https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6"] }

# Run the Actor and wait for it to finish
run = client.actor("fetchfoundry-labs/holidaycheck-reviews-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 '{
  "hotelUrls": [
    "https://www.holidaycheck.de/hr/bewertungen-centrovital-hotel-berlin/454268e5-f1bb-304e-a92d-d4c994ae43c6"
  ]
}' |
apify call fetchfoundry-labs/holidaycheck-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchfoundry-labs/holidaycheck-reviews-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/e2jxtZA49M7Ba2hN8/builds/UR3fSBpiAZYfFchE0/openapi.json
