# Reviews to Fix List & Reply Drafts (`seasons-ai/reviews-to-fix-list`) Actor

Turn Google Maps, TripAdvisor, Booking, App Store and Google Play reviews into a ranked fix list per place or app, with themes, trends and reply drafts.

- **URL**: https://apify.com/seasons-ai/reviews-to-fix-list.md
- **Developed by:** [Seasons AI](https://apify.com/seasons-ai) (community)
- **Categories:** AI, Business, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 analysed reviews

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

## Google Maps, TripAdvisor, Booking, App Store & Google Play Reviews to Fix List + Reply Drafts

Ran a review scraper for **Google Maps, TripAdvisor, Booking.com, the App Store or Google Play**? Pick its dataset here and get back what your customers keep complaining about, ranked by how much it costs you in stars. For each place or app you get:

- **What to fix first**: the top complaint themes, each with a concrete action and the evidence behind it
- **Complaint and praise themes**: how many reviews mention each one, their average stars, and whether the theme is rising or falling compared with the previous period
- **Example quotes**, copied word for word from the reviews
- **Escalate now**: reviews that mention food safety, injury, security, discrimination or legal threats (for apps: security or privacy problems, data loss, wrong charges, child safety)
- **Reply drafts** (optional): a polite reply to each review that mentions the specific points the customer raised. **The Actor never posts anything.** You read the drafts and post the ones you want yourself.

Output is one row per review and one summary row per place or app, plus a Markdown report and CSV files in the key-value store.

### Supported scrapers

The source is detected item by item, so you don't need to say which scraper you ran.

| Scraper | Report | What is read |
|---|---|---|
| [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper) | place report | text, stars, date, place name and ID, owner reply yes/no |
| [Tripadvisor Reviews Scraper](https://apify.com/maxcopell/tripadvisor-reviews) | place report | title, text, rating, date, language, place name, ID and address, owner reply yes/no |
| [Booking Reviews Scraper](https://apify.com/voyager/booking-reviews-scraper) | place report | title, liked and disliked text, score (1–10, mapped to 1–5 stars: 1 → 1, 10 → 5), date, language, hotel ID, property reply yes/no. The hotel name is taken from the hotel's Booking.com link. |
| [App Store Reviews Scraper](https://apify.com/thewolves/appstore-reviews-scraper) | **app report** | title, text, stars, date, app ID, app version. The app name, category and overall rating are looked up in the public App Store listing. |
| [Google Play Reviews Scraper](https://apify.com/thewolves/google-play-reviews-scraper) | **app report** | text, stars, date, language, app ID, app version, developer reply yes/no. The app name is looked up in the public Google Play listing. |

TripAdvisor and Booking.com datasets are mapped from the fields those scrapers publish and have been tested less than Google Maps, App Store and Google Play. If a field comes out empty or wrong, open an issue on the Actor page.

**App reports** are written for a product team: themes are product feedback (crashes, bugs, login and account, missing features, pricing and subscription, ads, UX, performance, customer support), the fix list suggests product, engineering or support actions, and it names the app versions that get the most 1–3 star reviews. If an app name can't be looked up, the app ID is used as the name.

### How to use it

1. Run one of the scrapers above for your place(s) or app(s). If the scraper has a "Personal data" or "Scrape reviewer info" option, you can turn it off, because this Actor doesn't use reviewer data.
2. Open this Actor and pick that run's dataset in **Reviews dataset**.
3. Pick who should get reply drafts, add an optional sign-off, and start the run.

There are two other ways to pass reviews:

- `reviewsUrl`: a public link to a JSON, JSON Lines or CSV file.
- `items`: a JSON array pasted into the form.

Exports of the scrapers above work as is. For other files, columns are matched by common names: `text`/`review`/`content`, `stars`/`rating`/`score` (`4`, `4.2/5`, `8/10` and `★★★★` all work), `publishedAtDate`/`date`, `title`/`placeName`/`place` (the place name), `reviewTitle` (the review headline), `appId` (makes it an app review) and `reviewId`/`id`.

### Input

| Field | Default | Description |
|---|---|---|
| `datasetId` | – | Dataset of a run of one of the supported scrapers, chosen with the picker. Read-only access. |
| `reviewsUrl` | – | Instead of a dataset: a public JSON or CSV file, up to 50 MB. |
| `items` | sample | Instead of a dataset or file: reviews as a JSON array. |
| `maxReviews` | 1000 | Maximum number of reviews with text analysed (and charged) per run, from 1 to 5,000. The newest reviews of each place or app are taken first. |
| `periodDays` | 30 | Length of the trend window: the last N days are compared with the N days before. The window ends at the newest review of each place or app. |
| `themeTaxonomy` | – | Theme names from an earlier run (the `taxonomy` field of a place or app row). New reviews are grouped into these names, so you can compare runs one to one. |
| `allowNewThemes` | true | With `themeTaxonomy`: whether new themes may be added. |
| `businessContext` | – | One or two sentences about the business or app, such as opening hours or a support email. Used in the analysis and in the replies. |
| `replyTo` | `unanswered` | `none`, `negative` (3 stars or less, no owner or developer reply yet), `unanswered` (no reply yet) or `all`. App Store exports don't show developer replies, so every App Store review counts as unanswered. |
| `replyLanguage` | `auto` | `auto` replies in the language of each review. Or name a language. |
| `replySignature` | – | Sign-off line added to every reply. |
| `defaultPlaceName` | `My business` | Place name used for rows that don't have one. Not used for app reviews. |

```json
{
  "datasetId": "<your scraper run's dataset>",
  "replyTo": "negative",
  "replySignature": "— The Harbor Street Cafe team",
  "businessContext": "Independent cafe, open 7:00-16:00. Contact: hello@harborstreet.example"
}
```

### Output

**Place row** (`rowType: "place-summary"`, trimmed):

```json
{
  "rowType": "place-summary",
  "placeName": "Harbor Street Cafe",
  "reviewsAnalyzed": 10,
  "avgStars": 3.3,
  "currentPeriod": { "from": "2026-09-21", "to": "2026-10-21", "reviews": 7, "avgStars": 3.1, "negativeSharePct": 57.1 },
  "previousPeriod": { "from": "2026-08-22", "to": "2026-09-21", "reviews": 4, "avgStars": 3.5, "negativeSharePct": 50 },
  "summary": "Average rating fell this period, mainly because of long wait times. Customers consistently praise the friendly staff and the baked goods.",
  "fixFirst": [
    { "rank": 1, "theme": "Wait Times", "action": "Add a second barista during peak hours and set up a queue system.", "why": "3 mentions, avg 2.0 stars, new this period.", "mentions": 3, "avgStars": 2, "priorityScore": 9, "trend": "new" }
  ],
  "complaints": [
    { "theme": "Wait Times", "mentions": 3, "sharePct": 30, "avgStars": 2, "priorityScore": 9, "trend": "new",
      "exampleQuotes": ["Queue out the door and only one person at the till. 25 minutes for a latte is too long."] }
  ],
  "praise": [{ "theme": "Staff Friendliness", "mentions": 3, "...": "..." }],
  "escalations": [{ "reviewId": "s09", "publishedAt": "2026-09-02", "stars": 1, "reason": "foreign object in food" }],
  "unansweredNegative": 6,
  "taxonomy": { "issues": ["Wait Times", "Food Quality", "Cleanliness"], "praise": ["Staff Friendliness", "..."] }
}
```

**App row** (`rowType: "app-summary"`, `reportType: "app"`, trimmed): the same fields as a place row, with product feedback themes.

```json
{
  "rowType": "app-summary",
  "reportType": "app",
  "source": "app-store",
  "placeName": "Duolingo: Learn Languages",
  "placeId": "570060128",
  "placeCategory": "Education",
  "reviewsAnalyzed": 10,
  "avgStars": 4,
  "fixFirst": [
    { "rank": 1, "theme": "Missing Features", "action": "Have the product team investigate and document user requests for missing features, prioritizing based on user impact.", "mentions": 1, "avgStars": 1, "trend": "n/a" }
  ],
  "taxonomy": { "issues": ["Missing Features", "Customer Support", "Pricing", "AI Errors", "Gamification"], "praise": ["Language Variety", "Learning Effectiveness"] }
}
```

**Review row** (`rowType: "review"`, trimmed):

```json
{
  "rowType": "review",
  "source": "google-maps",
  "placeName": "Harbor Street Cafe",
  "reviewId": "s04",
  "publishedAt": "2026-10-09T00:00:00.000Z",
  "stars": 2,
  "excerpt": "Queue out the door and only one person at the till. 25 minutes for a latte is too long.",
  "status": "analyzed",
  "sentiment": "negative",
  "severity": "medium",
  "issueThemes": ["Wait Times"],
  "issues": [{ "label": "long wait", "theme": "Wait Times", "severity": "medium", "quote": "25 minutes for a latte is too long." }],
  "praiseThemes": [],
  "needsEscalation": false,
  "hasOwnerResponse": false,
  "replyDraft": "Thank you for your feedback. We sincerely apologise for the long wait... — The Harbor Street Cafe team",
  "replyFlags": [],
  "charged": true
}
```

The key-value store also holds:

- `REPORT.md`: the full report for all places and apps
- `fix-list.csv`: one row per complaint theme and place or app, with rank, mentions, average stars, priority, trend, action and quotes
- `reviews.csv`: one row per review, with themes, sentiment and the reply draft
- `OUTPUT`: run summary with links to the files

**How themes are built.** Each review is tagged with short topic labels. Labels from all reviews of a place or app are then grouped into a small, stable set of themes for it: at most 12 complaint themes and 8 praise themes. **Priority** is the number of mentions × star impact, where each mention scores the stars it lost compared with a 5-star visit, with a minimum of 0.5. When a review has no star rating, the issue's severity is used instead. **Trend** compares the share of reviews that mention a theme in the last period with the period before. It is shown as `n/a` when either period has fewer than 3 reviews.

### Privacy

This tool works at business and app level only. It does not use or output reviewer names, usernames, reviewer IDs, avatars, profile links, photos, reviewer locations or the owner's earlier replies. A review row keeps only the review ID, date, stars, app version, the review title and a 240-character excerpt, which is enough to find the review when you reply. Names found in the text are replaced: in quotes and excerpts, staff names are replaced with `[staff member]` and other names with `[name]`; themes, the fix list and summaries refer to staff by role ("the cashier", "a staff member"); reply drafts don't name the reviewer or staff ("our team member"). Your business or app name, address and sign-off are kept as is, even when they contain a first name ("Joe's Pizza"). Name detection is automatic, works best for English reviews and can miss a name, so read quotes and drafts before you share them.

### Reply drafts

- Polite and specific, 40–90 words (apps: 30–80 words), written in the review's language unless you choose one.
- **Store limits:** Google Play developer replies can't be longer than 350 characters, so Google Play drafts are written short, sign-off included. A longer draft is cut at the last full sentence that fits (flag `reply_shortened`); if too little would be left, it is dropped, flagged `reply_dropped_too_long`, and not charged. App Store drafts stay far below Apple's 5,970-character limit.
- App replies ask the user to contact support with their device and app version, and never promise a fix date or a new feature.
- Drafts never promise refunds, discounts, vouchers, free items or other compensation, and never admit liability for health or safety claims. If a draft still mentions compensation, it is dropped, flagged `reply_dropped_compensation`, and not charged.
- A draft with a leftover placeholder such as `[date]` is flagged `has_placeholder`.
- The Actor does not post replies. Read every draft before you post it.

### Pricing

Pay per event. Reviews or reports that fail are free. Rating-only reviews (no text) are counted in the stats for free.

| Event | Price |
|---|---|
| Review analysed (themes, sentiment, severity) | $0.003 |
| Reply draft written | $0.002 |
| Place or app report (taxonomy, ranking, trend, fix list) | $0.02 per place or app |

Example: 200 reviews of one place with replies to 60 unanswered negative reviews cost 200 × $0.003 + 60 × $0.002 + $0.02 = **$0.74**.

Before it starts, the Actor works out the most the run can cost. If that is above the run's maximum cost, it stops without charging and tells you the amount to set. Available on paid Apify plans.

### Limits

- Up to 5,000 reviews with text per run (`maxReviews`), from up to 100 places and apps. A dataset is read up to its first 20,000 items.
- Each review is analysed up to its first 4,000 characters (title included). `businessContext` is capped at 600 characters.
- Up to 400 distinct topic labels per place or app go into theme grouping. Rarer labels join the closest theme, or "Other issues" / "Other praise".
- Themes, actions and quotes are AI-generated from the review text. Quotes are checked word for word against the review, and a quote that doesn't match is removed. Small samples (a few reviews per place or app) give rough themes. Treat the ranking as a guide, not a measurement.
- A file link must be downloadable without a login. Private Apify dataset links don't work as `reviewsUrl`: pick the dataset in **Reviews dataset** instead.

### Good to know

- On the free Apify plan the Actor returns a short preview with an example result and prices instead of running.

# Actor input Schema

## `datasetId` (type: `string`):

Pick the dataset of a review scraper run. Detected automatically, item by item: Google Maps Reviews Scraper (compass/google-maps-reviews-scraper), Tripadvisor Reviews Scraper (maxcopell/tripadvisor-reviews), Booking Reviews Scraper (voyager/booking-reviews-scraper), App Store Reviews Scraper (thewolves/appstore-reviews-scraper) and Google Play Reviews Scraper (thewolves/google-play-reviews-scraper). App store reviews get an app report with product feedback themes (bugs, crashes, missing features, pricing, UX, support). Other datasets work too if each item has review text (`text`) and/or a rating (`stars`, `rating` or `score`). When set, `reviewsUrl` and `items` are ignored. The Actor only reads this dataset.

## `reviewsUrl` (type: `string`):

Instead of a dataset: a public link to a JSON array, JSON Lines or CSV file with one review per row, e.g. an export of one of the scrapers above, or your own columns such as text, stars, publishedAtDate, title (place name). Up to 50 MB.

## `items` (type: `array`):

Instead of a dataset or file: paste reviews as a JSON array, e.g. \[{"title": "My Cafe", "text": "...", "stars": 2, "publishedAtDate": "2026-10-01"}]. Used only when no dataset or file URL is given.

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

Cap on reviews with text analysed (and charged) in this run, 1 to 5,000. The newest reviews of each place or app are kept first. Rating-only reviews (no text) are free and only counted in the stats.

## `periodDays` (type: `integer`):

Each theme's share of reviews in the last N days is compared with the N days before. The period ends at the newest review of each place or app.

## `themeTaxonomy` (type: `array`):

Theme names from an earlier run (the `taxonomy` field of a place or app summary), e.g. Wait Times, Cleanliness, Staff Attitude, or Crashes, Missing Features for an app. Reviews are grouped into these names first, so reports from different runs compare one to one.

## `allowNewThemes` (type: `boolean`):

With theme names given above: allow new themes for topics that fit none of them. Off = leftovers go to Other issues / Other praise.

## `businessContext` (type: `string`):

A sentence or two used in the analysis and reply drafts, e.g. "Family-run cafe, open 7-15. Contact: hello@example.com" or "Language learning app. Support: help@example.com". Up to 600 characters.

## `defaultPlaceName` (type: `string`):

Used when an item has no place name (common in CSV exports of a single location). App reviews use the app's store name, or its app ID when the name can't be looked up.

## `replyTo` (type: `string`):

Draft replies cost a separate event each. The Actor never posts anything: you review and post the drafts yourself. Google Play replies are kept within the store's 350-character limit. App Store exports don't say whether you already replied, so every App Store review counts as unanswered.

## `replyLanguage` (type: `string`):

"auto" answers in the language of each review; or name a language, e.g. English, German.

## `replySignature` (type: `string`):

Line that ends every reply, e.g. "— The Harbor Street Cafe team". Up to 80 characters; it counts toward the Google Play 350-character limit.

## Actor input object example

```json
{
  "items": [
    {
      "title": "Harbor Street Cafe",
      "text": "Waited 35 minutes for two flat whites and the barista forgot one of them. Cakes were lovely though.",
      "stars": 2,
      "publishedAtDate": "2026-10-20T09:12:00Z",
      "reviewId": "sample-1"
    },
    {
      "title": "Harbor Street Cafe",
      "text": "Great coffee and very friendly staff, but the bathroom was dirty and out of soap.",
      "stars": 3,
      "publishedAtDate": "2026-10-11T14:40:00Z",
      "reviewId": "sample-2"
    },
    {
      "title": "Harbor Street Cafe",
      "text": "Best almond croissant in town. Quick service even on a busy Saturday.",
      "stars": 5,
      "publishedAtDate": "2026-09-27T10:05:00Z",
      "reviewId": "sample-3"
    }
  ],
  "maxReviews": 1000,
  "periodDays": 30,
  "allowNewThemes": true,
  "defaultPlaceName": "My business",
  "replyTo": "unanswered",
  "replyLanguage": "auto"
}
```

# Actor output Schema

## `places` (type: `string`):

No description

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

No description

## `report` (type: `string`):

No description

## `fixListCsv` (type: `string`):

No description

## `reviewsCsv` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "items": [
        {
            "title": "Harbor Street Cafe",
            "text": "Waited 35 minutes for two flat whites and the barista forgot one of them. Cakes were lovely though.",
            "stars": 2,
            "publishedAtDate": "2026-10-20T09:12:00Z",
            "reviewId": "sample-1"
        },
        {
            "title": "Harbor Street Cafe",
            "text": "Great coffee and very friendly staff, but the bathroom was dirty and out of soap.",
            "stars": 3,
            "publishedAtDate": "2026-10-11T14:40:00Z",
            "reviewId": "sample-2"
        },
        {
            "title": "Harbor Street Cafe",
            "text": "Best almond croissant in town. Quick service even on a busy Saturday.",
            "stars": 5,
            "publishedAtDate": "2026-09-27T10:05:00Z",
            "reviewId": "sample-3"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("seasons-ai/reviews-to-fix-list").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 = { "items": [
        {
            "title": "Harbor Street Cafe",
            "text": "Waited 35 minutes for two flat whites and the barista forgot one of them. Cakes were lovely though.",
            "stars": 2,
            "publishedAtDate": "2026-10-20T09:12:00Z",
            "reviewId": "sample-1",
        },
        {
            "title": "Harbor Street Cafe",
            "text": "Great coffee and very friendly staff, but the bathroom was dirty and out of soap.",
            "stars": 3,
            "publishedAtDate": "2026-10-11T14:40:00Z",
            "reviewId": "sample-2",
        },
        {
            "title": "Harbor Street Cafe",
            "text": "Best almond croissant in town. Quick service even on a busy Saturday.",
            "stars": 5,
            "publishedAtDate": "2026-09-27T10:05:00Z",
            "reviewId": "sample-3",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("seasons-ai/reviews-to-fix-list").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 '{
  "items": [
    {
      "title": "Harbor Street Cafe",
      "text": "Waited 35 minutes for two flat whites and the barista forgot one of them. Cakes were lovely though.",
      "stars": 2,
      "publishedAtDate": "2026-10-20T09:12:00Z",
      "reviewId": "sample-1"
    },
    {
      "title": "Harbor Street Cafe",
      "text": "Great coffee and very friendly staff, but the bathroom was dirty and out of soap.",
      "stars": 3,
      "publishedAtDate": "2026-10-11T14:40:00Z",
      "reviewId": "sample-2"
    },
    {
      "title": "Harbor Street Cafe",
      "text": "Best almond croissant in town. Quick service even on a busy Saturday.",
      "stars": 5,
      "publishedAtDate": "2026-09-27T10:05:00Z",
      "reviewId": "sample-3"
    }
  ]
}' |
apify call seasons-ai/reviews-to-fix-list --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,seasons-ai/reviews-to-fix-list"
        }
    }
}
```

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/1h7ZIk0Qn9WKp4hCz/builds/kaNzQjSzbLMBspY6u/openapi.json
