# TopHotels Review Scraper (`valiant_quarter/tophotels-reviews-scraper`) Actor

Scrape TopHotels.ru hotel reviews: full review text, pros & cons, category ratings (placement/service/food), recommendation, traveled-with type, stay & publish dates, reviewer info, photos and the hotelier's owner response — as structured JSON.

- **URL**: https://apify.com/valiant\_quarter/tophotels-reviews-scraper.md
- **Developed by:** [valiant quarter](https://apify.com/valiant_quarter) (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

$2.90 / 1,000 results

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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## TopHotels Reviews Scraper 🏨⭐

**Scrape TopHotels.ru hotel reviews at scale — full review text, category ratings, pros & cons,
and the hotel's own reply to each review.** No login, no API key, no browser.

Just paste a TopHotels hotel URL and export clean, structured review data (JSON, CSV, Excel)
for reputation monitoring, sentiment analysis and competitive research.

---

### ✨ What this TopHotels scraper does

- ⭐ Scrapes hotel reviews from **TopHotels.ru** by hotel URL
- 📝 Extracts **full review text, pros & cons, title, language**
- 🔢 Reads **category ratings** (placement / service / food, 0–5 scale) and the overall score
- 💬 Captures the **hotelier's owner response** to each review (text + date + responder)
- 📅 Reports both the **stay date** and the **publish date** for every review
- 🧑 Includes reviewer info (name, city, country, review count, profile link) and review photos
- 🗓️ Optional **date range filter** (`dateFrom`/`dateTo`) applied cheaply during pagination —
  no wasted requests on reviews outside your range
- 📦 Outputs structured **JSON / CSV / Excel** via the Apify dataset
- 🚀 Plain HTTP under the hood (no browser) — fast and cheap to run

### 💡 Use cases

- **Hotel reputation monitoring** — track new guest reviews and hotelier replies over time
- **Sentiment analysis** — feed review text into an NLP/LLM pipeline
- **Competitive analysis** — compare guest feedback and management responsiveness across hotels
- **Market research** — understand what travelers to Turkey/Russia-market resorts praise or complain about
- **Review aggregation** — combine TopHotels data with other OTA/review-platform sources

### 🚀 How to scrape TopHotels reviews (step by step)

1. Open the hotel on TopHotels, e.g. `https://tophotels.ru/hotel/al24898` (Lago Hotel 5*, Side).
2. Paste that URL into the **Hotel URLs** field.
3. Set **Max reviews per hotel** (newest first) and, optionally, a **date range**.
4. Click **Start** and download the results from the dataset.

### ⚙️ Input

| Field | Type | Description |
|-------|------|-------------|
| `startUrls` | array | TopHotels hotel URLs. Any URL containing the `al<id>` slug works. |
| `maxReviewsPerHotel` | integer | Stop after this many reviews (newest published first). `0` = all. |
| `includeRawData` | boolean | Attach raw parsed payloads under `raw`. |
| `dateFrom` / `dateTo` | string (`YYYY-MM-DD`) | Only reviews published in this range. |
| `proxyConfiguration` | object | Proxy settings (optional — the site has no bot protection). |

#### Example input

```json
{
    "startUrls": [{ "url": "https://tophotels.ru/hotel/al24898" }],
    "maxReviewsPerHotel": 100,
    "dateFrom": "2025-01-01"
}
````

### 📤 Output

Each dataset item is a single review:

```json
{
    "hotelId": "24898",
    "hotelName": "Lago Hotel 5*",
    "hotelUrl": "https://tophotels.ru/hotel/al24898",
    "reviewId": "2763401",
    "reviewUrl": "https://tophotels.ru/review/2763401",
    "title": "Рекомендую",
    "text": "Достаточно хороший отель, отдыхали семьей...",
    "ratings": { "placement": 5, "service": 4, "food": 4 },
    "generalRating": 4.3,
    "ratingScale": 5,
    "recommendation": true,
    "traveledWith": "FAMILY",
    "travelDate": "2025-05-08T00:00:00.000Z",
    "entryDate": "2026-03-17T00:00:00.000Z",
    "ownerResponse": {
        "text": "Уважаемый Евгений, благодарим вас за выбор нашего отеля...",
        "respondedAt": "2026-03-17T11:37:00.000Z",
        "authorName": "lago Hotel"
    }
}
```

#### Output fields

| Field | Description |
|-------|--------------|
| `hotelId`, `hotelName`, `hotelUrl` | Property the review belongs to |
| `reviewId`, `reviewUrl` | TopHotels' unique review id and permalink |
| `title`, `text`, `extraTexts.pros/cons` | Review content (mini reviews may have `null` title/text) |
| `ratings`, `generalRating`, `ratingScale` | Category ratings + overall score, scale is always `5` |
| `recommendation`, `traveledWith` | "Recommends the hotel" flag and travel-party type |
| `travelDate`, `stayFrom`, `stayTo`, `entryDate` | Stay dates and publish date (ISO 8601) |
| `user` | Reviewer name, city, country, review count, profile URL |
| `media` | Reviewer photos |
| `ownerResponse` | The hotel's reply — `null` if the hotelier hasn't responded |

A per-hotel run summary (`reviews`, `withOwnerResponse`, `totalOnSite`, `dateFiltered`) is stored
in the key-value store under `SUMMARY`.

### ❓ FAQ

**Does this need a proxy?** No — TopHotels served every page tested (including with zero
request headers) with HTTP 200 and no challenge. Apify proxy is optional.

**What does `ownerResponse` look like when the hotel hasn't replied?** `null`. Roughly 6% of
reviews have a hotelier reply on the sites checked so far — set a larger `maxReviewsPerHotel`
to increase your chances of capturing more.

**How is the date range filter applied?** On `entryDate` (the review's publish date), which is
available directly on the listing page — so out-of-range reviews never trigger a detail-page
request, keeping filtered runs cheap.

***

Keywords: tophotels scraper, tophotels.ru reviews, hotel review scraper, hotel reputation
monitoring, hotel owner response scraper, Russian OTA review scraper, Turkey hotel reviews.

# Actor input Schema

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

TopHotels.ru hotel URLs, e.g. https://tophotels.ru/hotel/al24898. Any URL containing the hotel slug (al<id>) works.

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

Maximum number of reviews to scrape per hotel (newest published first). Use 0 to scrape all reviews (note: large hotels can have 1000+ reviews, one HTTP request per review).

## `includeRawData` (type: `boolean`):

Attach the raw list-item and detail-page derived payload for each review under the `raw` key.

## `dateFrom` (type: `string`):

Only return reviews published (entryDate) on or after this date (YYYY-MM-DD). Leave empty for no lower bound.

## `dateTo` (type: `string`):

Only return reviews published (entryDate) on or before this date (YYYY-MM-DD). Leave empty for no upper bound.

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

Proxy settings. TopHotels.ru showed no bot protection during recon, so this run works fine without a proxy; Apify automatic proxy is recommended for larger/production runs.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://tophotels.ru/hotel/al24898"
    }
  ],
  "maxReviewsPerHotel": 5,
  "includeRawData": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped reviews with text, ratings and owner responses (JSON)

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

Per-hotel counts: reviews scraped, owner responses found, total on site

# 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://tophotels.ru/hotel/al24898"
        }
    ],
    "maxReviewsPerHotel": 5,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("valiant_quarter/tophotels-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 = {
    "startUrls": [{ "url": "https://tophotels.ru/hotel/al24898" }],
    "maxReviewsPerHotel": 5,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("valiant_quarter/tophotels-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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://tophotels.ru/hotel/al24898"
    }
  ],
  "maxReviewsPerHotel": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call valiant_quarter/tophotels-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=valiant_quarter/tophotels-reviews-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "TopHotels Review Scraper",
        "description": "Scrape TopHotels.ru hotel reviews: full review text, pros & cons, category ratings (placement/service/food), recommendation, traveled-with type, stay & publish dates, reviewer info, photos and the hotelier's owner response — as structured JSON.",
        "version": "1.0",
        "x-build-id": "Pn2MdYqfteLY3cBmY"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/valiant_quarter~tophotels-reviews-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-valiant_quarter-tophotels-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/valiant_quarter~tophotels-reviews-scraper/runs": {
            "post": {
                "operationId": "runs-sync-valiant_quarter-tophotels-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/valiant_quarter~tophotels-reviews-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-valiant_quarter-tophotels-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "startUrls"
                ],
                "properties": {
                    "startUrls": {
                        "title": "Hotel URLs",
                        "type": "array",
                        "description": "TopHotels.ru hotel URLs, e.g. https://tophotels.ru/hotel/al24898. Any URL containing the hotel slug (al<id>) works.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "maxReviewsPerHotel": {
                        "title": "Max reviews per hotel",
                        "minimum": 0,
                        "maximum": 50000,
                        "type": "integer",
                        "description": "Maximum number of reviews to scrape per hotel (newest published first). Use 0 to scrape all reviews (note: large hotels can have 1000+ reviews, one HTTP request per review).",
                        "default": 0
                    },
                    "includeRawData": {
                        "title": "Include raw data",
                        "type": "boolean",
                        "description": "Attach the raw list-item and detail-page derived payload for each review under the `raw` key.",
                        "default": false
                    },
                    "dateFrom": {
                        "title": "Date from",
                        "type": "string",
                        "description": "Only return reviews published (entryDate) on or after this date (YYYY-MM-DD). Leave empty for no lower bound."
                    },
                    "dateTo": {
                        "title": "Date to",
                        "type": "string",
                        "description": "Only return reviews published (entryDate) on or before this date (YYYY-MM-DD). Leave empty for no upper bound."
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings. TopHotels.ru showed no bot protection during recon, so this run works fine without a proxy; Apify automatic proxy is recommended for larger/production runs.",
                        "default": {
                            "useApifyProxy": true
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
