# Review Response Prioritizer by Location (`practical_ai_lab_jp/pal-maps-reviews-response-prioritizer`) Actor

Prioritize review-response work across multiple locations using authorized review exports. Highlights recent low ratings, repeated service issues and the highest-priority location/topic combinations without exporting reviewer identities.

- **URL**: https://apify.com/practical_ai_lab_jp/pal-maps-reviews-response-prioritizer.md
- **Developed by:** [Practical AI Lab](https://apify.com/practical_ai_lab_jp) (community)
- **Categories:** Lead generation, SEO tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 location priority reports

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

## Review Response Prioritizer by Location

Rank which locations and complaint topics need attention first, using review data **you already have**. The Actor does not scrape Google Maps or any other site, makes no network requests, and never reads or outputs reviewer names or profile data.

### What it does

1. Reads your review records (location, rating 1–5, date, optional text).
2. Compares a recent window (default 30 days) with the equally long period before it.
3. Detects complaint topics in low-rated reviews with transparent English keyword rules (plus optional custom topics).
4. Outputs two ranked record types in one dataset:
   - `location` records: priority score, recent low ratings, rating change versus the prior period, recurring issues, confidence label.
   - `topic` records: topics ranked across all locations, with locations affected and recurrence counts.
5. Saves a `SUMMARY` record with window dates, input statistics and limitations.

### Priority score (0–100)

`min(100, 12 × recency-weighted recent low ratings + 8 × recurring topics + 10 × rating drop)`

- Recency weight falls linearly from 1.0 (as-of day) to 0.5 (oldest day of the window).
- A recurring topic appears in at least `minRecurrence` recent low-rated reviews at that location.
- Rating drop counts only when both windows have at least 3 reviews.

### Sample input

```json
{
  "reviews": [
    {"locationName": "Harbor Cafe", "rating": 1, "publishedAt": "2026-10-20", "text": "Long wait and the staff were rude."},
    {"locationName": "Harbor Cafe", "rating": 2, "publishedAt": "2026-10-18", "text": "Wrong order and slow service."},
    {"locationName": "Central Gym", "rating": 1, "publishedAt": "2026-10-22", "text": "Dirty showers and a bad smell."},
    {"locationName": "Central Gym", "rating": 2, "publishedAt": "2026-10-15", "text": "Locker room is filthy."}
  ],
  "recentDays": 30,
  "lowRatingMax": 2
}
```

Accepted field aliases: location (`locationName`, `location`, `placeName`, `place`), id (`locationId`, `placeId`), rating (`rating`, `stars`), text (`text`, `review`, `comment`), date (`publishedAt`, `date`, `reviewDate`). Dates must be ISO format.

### Input options

| Field | Default | Meaning |
|---|---|---|
| `reviews` | required | Your review records |
| `recentDays` | 30 | Recent window length (7–180) |
| `lowRatingMax` | 2 | Ratings at or below count as low |
| `asOfDate` | newest review date | Reference day for windows |
| `minRecurrence` | 2 | Low-rated mentions needed to call a topic recurring |
| `useDefaultTopics` | true | Use built-in keyword topics |
| `customTopics` | none | Your own topic → keyword lists |
| `maxLocations` | 50 | Cap on ranked locations |
| `maxReviews` | 20000 | Cap on reviews processed |

### Limitations and sample-size caveats

- Only the data you supply is analysed. Missing or unrepresentative exports produce misleading rankings.
- Keyword topic detection is simple: it misses paraphrases, sarcasm and other languages, and can over-match. Treat topics as leads and read the original reviews.
- With fewer than 5 recent reviews at a location, confidence is `low`; 5–14 is `medium`; 15 or more is `higher`. Even "higher" is not statistical proof.
- The score is a relative triage aid, not a measure of true quality, and is not calibrated to any business outcome.
- Records with missing location, invalid rating, or invalid date are skipped and counted in `SUMMARY`. Reviews older than two windows or after the as-of date are not scored.
- The Actor does not detect fake or duplicate reviews.

### Privacy

Reviewer names, profile links and photos are ignored. Review text is used only for topic matching and is not written to the output. Do not include data you have no right to process.

### Billing

The code emits a `location-priority-report` event per ranked location after results are saved. Invalid input and failed runs end before any charge. The price, if any, is configured separately by the publisher.

# Actor input Schema

## `reviews` (type: `array`):

Review records you already have (for example from your own export). Each needs a location (locationName or locationId), a rating from 1 to 5 and a date (publishedAt, YYYY-MM-DD). Optional: text. Reviewer name and profile fields are ignored and never output.

## `recentDays` (type: `integer`):

Length of the recent window. The same-length window before it is the comparison period.

## `lowRatingMax` (type: `integer`):

Ratings at or below this value count as low.

## `asOfDate` (type: `string`):

Optional YYYY-MM-DD. Defaults to the newest review date in your data, which keeps results reproducible.

## `minRecurrence` (type: `integer`):

A topic is 'recurring' at a location when it appears in at least this many recent low-rated reviews.

## `useDefaultTopics` (type: `boolean`):

Use the built-in English keyword topics (wait time, cleanliness, billing, and more).

## `customTopics` (type: `object`):

Optional map of topic name to a list of lowercase keywords, for example {"Allergy handling": \["allergen", "nut"]}.

## `maxLocations` (type: `integer`):

Upper bound of ranked locations returned (and billed).

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

Safety cap on reviews read from the input.

## Actor input object example

```json
{
  "reviews": [
    {
      "locationName": "Harbor Cafe",
      "rating": 1,
      "publishedAt": "2026-10-20",
      "text": "Long wait and the staff were rude."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 2,
      "publishedAt": "2026-10-18",
      "text": "Wrong order and slow service."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 5,
      "publishedAt": "2026-10-05",
      "text": "Great coffee."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 5,
      "publishedAt": "2026-09-10",
      "text": "Lovely place."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 4,
      "publishedAt": "2026-09-12",
      "text": "Good."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 5,
      "publishedAt": "2026-09-15",
      "text": "Friendly."
    },
    {
      "locationName": "Central Gym",
      "rating": 1,
      "publishedAt": "2026-10-22",
      "text": "Dirty showers and a bad smell."
    },
    {
      "locationName": "Central Gym",
      "rating": 2,
      "publishedAt": "2026-10-15",
      "text": "Locker room is filthy."
    },
    {
      "locationName": "Central Gym",
      "rating": 4,
      "publishedAt": "2026-10-10",
      "text": "Good equipment."
    },
    {
      "locationName": "Park Dental",
      "rating": 5,
      "publishedAt": "2026-10-21",
      "text": "Quick and clean."
    },
    {
      "locationName": "Park Dental",
      "rating": 4,
      "publishedAt": "2026-10-12",
      "text": "Fine visit."
    },
    {
      "locationName": "Park Dental",
      "rating": 2,
      "publishedAt": "2026-10-02",
      "text": "Billing mistake, charged twice."
    }
  ],
  "recentDays": 30,
  "lowRatingMax": 2,
  "minRecurrence": 2,
  "useDefaultTopics": true,
  "maxLocations": 50,
  "maxReviews": 20000
}
```

# Actor output Schema

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

Download rows from the latest run in JSON/CSV.

# 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 = {
    "reviews": [
        {
            "locationName": "Harbor Cafe",
            "rating": 1,
            "publishedAt": "2026-10-20",
            "text": "Long wait and the staff were rude."
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 2,
            "publishedAt": "2026-10-18",
            "text": "Wrong order and slow service."
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 5,
            "publishedAt": "2026-10-05",
            "text": "Great coffee."
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 5,
            "publishedAt": "2026-09-10",
            "text": "Lovely place."
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 4,
            "publishedAt": "2026-09-12",
            "text": "Good."
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 5,
            "publishedAt": "2026-09-15",
            "text": "Friendly."
        },
        {
            "locationName": "Central Gym",
            "rating": 1,
            "publishedAt": "2026-10-22",
            "text": "Dirty showers and a bad smell."
        },
        {
            "locationName": "Central Gym",
            "rating": 2,
            "publishedAt": "2026-10-15",
            "text": "Locker room is filthy."
        },
        {
            "locationName": "Central Gym",
            "rating": 4,
            "publishedAt": "2026-10-10",
            "text": "Good equipment."
        },
        {
            "locationName": "Park Dental",
            "rating": 5,
            "publishedAt": "2026-10-21",
            "text": "Quick and clean."
        },
        {
            "locationName": "Park Dental",
            "rating": 4,
            "publishedAt": "2026-10-12",
            "text": "Fine visit."
        },
        {
            "locationName": "Park Dental",
            "rating": 2,
            "publishedAt": "2026-10-02",
            "text": "Billing mistake, charged twice."
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ai_lab_jp/pal-maps-reviews-response-prioritizer").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 = { "reviews": [
        {
            "locationName": "Harbor Cafe",
            "rating": 1,
            "publishedAt": "2026-10-20",
            "text": "Long wait and the staff were rude.",
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 2,
            "publishedAt": "2026-10-18",
            "text": "Wrong order and slow service.",
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 5,
            "publishedAt": "2026-10-05",
            "text": "Great coffee.",
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 5,
            "publishedAt": "2026-09-10",
            "text": "Lovely place.",
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 4,
            "publishedAt": "2026-09-12",
            "text": "Good.",
        },
        {
            "locationName": "Harbor Cafe",
            "rating": 5,
            "publishedAt": "2026-09-15",
            "text": "Friendly.",
        },
        {
            "locationName": "Central Gym",
            "rating": 1,
            "publishedAt": "2026-10-22",
            "text": "Dirty showers and a bad smell.",
        },
        {
            "locationName": "Central Gym",
            "rating": 2,
            "publishedAt": "2026-10-15",
            "text": "Locker room is filthy.",
        },
        {
            "locationName": "Central Gym",
            "rating": 4,
            "publishedAt": "2026-10-10",
            "text": "Good equipment.",
        },
        {
            "locationName": "Park Dental",
            "rating": 5,
            "publishedAt": "2026-10-21",
            "text": "Quick and clean.",
        },
        {
            "locationName": "Park Dental",
            "rating": 4,
            "publishedAt": "2026-10-12",
            "text": "Fine visit.",
        },
        {
            "locationName": "Park Dental",
            "rating": 2,
            "publishedAt": "2026-10-02",
            "text": "Billing mistake, charged twice.",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("practical_ai_lab_jp/pal-maps-reviews-response-prioritizer").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 '{
  "reviews": [
    {
      "locationName": "Harbor Cafe",
      "rating": 1,
      "publishedAt": "2026-10-20",
      "text": "Long wait and the staff were rude."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 2,
      "publishedAt": "2026-10-18",
      "text": "Wrong order and slow service."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 5,
      "publishedAt": "2026-10-05",
      "text": "Great coffee."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 5,
      "publishedAt": "2026-09-10",
      "text": "Lovely place."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 4,
      "publishedAt": "2026-09-12",
      "text": "Good."
    },
    {
      "locationName": "Harbor Cafe",
      "rating": 5,
      "publishedAt": "2026-09-15",
      "text": "Friendly."
    },
    {
      "locationName": "Central Gym",
      "rating": 1,
      "publishedAt": "2026-10-22",
      "text": "Dirty showers and a bad smell."
    },
    {
      "locationName": "Central Gym",
      "rating": 2,
      "publishedAt": "2026-10-15",
      "text": "Locker room is filthy."
    },
    {
      "locationName": "Central Gym",
      "rating": 4,
      "publishedAt": "2026-10-10",
      "text": "Good equipment."
    },
    {
      "locationName": "Park Dental",
      "rating": 5,
      "publishedAt": "2026-10-21",
      "text": "Quick and clean."
    },
    {
      "locationName": "Park Dental",
      "rating": 4,
      "publishedAt": "2026-10-12",
      "text": "Fine visit."
    },
    {
      "locationName": "Park Dental",
      "rating": 2,
      "publishedAt": "2026-10-02",
      "text": "Billing mistake, charged twice."
    }
  ]
}' |
apify call practical_ai_lab_jp/pal-maps-reviews-response-prioritizer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,practical_ai_lab_jp/pal-maps-reviews-response-prioritizer"
        }
    }
}
```

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/fneQwShSVIm8xHoZk/builds/ANnh639Md0jjDzCOq/openapi.json
