# Review insights agent (`apifydemo3/aabw-demo`) Actor

LangGraph AI agent in JavaScript that scrapes a business's reviews and clusters them into themes with sentiment and suggested actions.

- **URL**: https://apify.com/apifydemo3/aabw-demo.md
- **Developed by:** [Apify Demo #3](https://apify.com/apifydemo3) (community)
- **Categories:** AI, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.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/platform/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

## Review insights agent

<!-- This is an Apify template readme -->

**Turn hundreds of customer reviews into a handful of decisions.** This Actor is an AI agent that
takes a **business name or Google Maps URL**, pulls its recent reviews, and clusters them into clear
**themes** with sentiment, a representative quote, and a suggested action for each. It works for
restaurants, hotels, apps, airlines, and any business listed on Google Maps.

The agent is built with [LangGraph](https://langchain-ai.github.io/langgraphjs/) and runs on the
Apify platform, so you get API access, scheduling, integrations, proxy rotation, and run monitoring
out of the box.

### What does the review insights agent do?

Reading reviews one by one does not scale. This agent does the reading for you:

1. It calls the [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper)
   as a tool to fetch recent reviews for the business you name.
2. It groups those reviews into recurring themes (for example "slow service", "great location",
   "buggy checkout").
3. For each theme it scores the sentiment, picks a representative verbatim quote, and proposes a
   concrete action.

Each theme becomes one row in the dataset, ready to export or pipe into another tool.

### Why use the review insights agent?

- **Prioritize what to fix** - see the themes that drive negative sentiment, ranked and quotable.
- **Brief a team fast** - hand product, ops, or support a short list of themes instead of a review dump.
- **Track over time** - schedule runs and watch how sentiment on each theme moves.
- **Competitive research** - run it on a competitor's listing to learn what their customers value.

### How to use the review insights agent

1. Enter a **business name** (for example `Shake Shack Madison Square Park`) or paste a **Google Maps
   place URL** into the input.
2. Optionally set how many reviews to pull, the language, the sort order, and the model.
3. Click **Start** and wait for the run to finish.
4. Open the **Output** tab to see one row per theme, or download the dataset.

The OpenAI API key is not a form field - it is read from the `OPENAI_API_KEY` environment variable
(see [Getting started](#getting-started) and [Deploy to Apify](#deploy-to-apify)).

### Input

Configure the run on the **Input** tab or via the API.

| Field         | Description                                                                            |
| ------------- | -------------------------------------------------------------------------------------- |
| `business`    | Required. A business name or a Google Maps place URL.                                  |
| `maxReviews`  | Maximum number of recent reviews to analyze. Default `50`.                             |
| `language`    | Language code for the reviews. Default `en`.                                           |
| `reviewsSort` | Order in which reviews are fetched: newest, most relevant, highest, or lowest ranking. |
| `modelName`   | OpenAI model used for clustering and sentiment. Default `gpt-4o`.                      |

Example input:

```json
{
    "business": "Shake Shack Madison Square Park",
    "maxReviews": 50,
    "reviewsSort": "newest",
    "modelName": "gpt-4o"
}
````

### Output

The Actor stores one item per theme in the dataset. You can download the dataset in various formats
such as JSON, HTML, CSV, or Excel.

Example output item:

```json
{
    "business": "Shake Shack Madison Square Park",
    "theme": "Long wait times",
    "sentiment": "negative",
    "sentimentScore": -0.6,
    "exampleQuote": "Great burgers but we waited 40 minutes to order.",
    "suggestedAction": "Add a second register or mobile ordering during peak hours.",
    "reviewCount": 12
}
```

#### Data table

| Field             | Description                                               |
| ----------------- | --------------------------------------------------------- |
| `theme`           | Short label for the recurring topic.                      |
| `sentiment`       | Overall sentiment: positive, negative, mixed, or neutral. |
| `sentimentScore`  | Sentiment from -1 (very negative) to 1 (very positive).   |
| `exampleQuote`    | A short, representative verbatim quote from a review.     |
| `suggestedAction` | A concrete action the business could take.                |
| `reviewCount`     | Approximate number of reviews that mention the theme.     |
| `business`        | The business the run analyzed.                            |

### How much does it cost?

The Actor uses the [pay-per-event](https://docs.apify.com/platform/actors/publishing/monetize#pay-per-event-pricing-model)
pricing model, charging a flat fee when a run starts, a small fee for each theme it produces, and a
flat fee when the analysis completes. On top of that you pay your OpenAI usage on your own key, plus
the usage of the Google Maps Reviews Scraper it calls. Keep `maxReviews` modest to control cost - 50
recent reviews is usually enough to surface the main themes.

### Tips and advanced options

- Lower `maxReviews` and use `gpt-4o-mini` for cheaper, faster runs during testing.
- Set `reviewsSort` to `lowestRanking` to focus the analysis on complaints.
- Paste the exact Google Maps place URL when a business name is ambiguous or has many locations.

### FAQ, disclaimers, and support

**Which sites does it cover?** Any business with a Google Maps listing.

**Is scraping reviews legal?** The Actor collects publicly available review content. You are
responsible for using the data in line with Google's Terms of Service and applicable laws, and for
not storing personal data without a lawful basis.

**Known limitations.** Very new or unlisted businesses may return no reviews, and an ambiguous name
can resolve to the wrong location - use a place URL in that case.

Found a bug or want a custom version? Open an issue on the Actor's **Issues** tab.

### Resources

- [Apify Actors](https://docs.apify.com/platform/actors)
- [LangGraph documentation](https://langchain-ai.github.io/langgraph/tutorials/introduction/)
- [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper)
- [What are AI agents?](https://blog.apify.com/what-are-ai-agents/)
- [Pay-per-event monetization](https://docs.apify.com/sdk/js/docs/next/guides/pay-per-event)

### Getting started

Run the Actor locally with:

```bash
apify run
```

Set your keys first:

- Run `apify login` so `APIFY_TOKEN` is available for the reviews scraper.
- Provide your OpenAI key through the `OPENAI_API_KEY` environment variable. The easiest way is to
  store it as an Apify secret once - `apify secrets add OPENAI_API_KEY <your-key>` - which
  `.actor/actor.json` references as `@OPENAI_API_KEY`, so it resolves both locally with `apify run`
  and on the platform after `apify push`. Alternatively, export `OPENAI_API_KEY` in your shell.

### Deploy to Apify

1. Log in to Apify:

   ```bash
   apify login
   ```

2. Deploy and build the Actor on the Apify platform:

   ```bash
   apify push
   ```

   The `OPENAI_API_KEY` secret is uploaded on push and injected as an environment variable at
   runtime, so the deployed Actor has the key without any input field.

### Documentation reference

- [Apify SDK for JavaScript documentation](https://docs.apify.com/sdk/js)
- [Apify platform documentation](https://docs.apify.com/platform)
- [Join our developer community on Discord](https://discord.com/invite/jyEM2PRvMU)

# Actor input Schema

## `business` (type: `string`):

The business to analyze - a name (e.g. "Shake Shack Madison Square Park") or a Google Maps place URL.

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

Maximum number of recent reviews to fetch and analyze.

## `reviewsSort` (type: `string`):

How the reviews scraper orders reviews before fetching them.

## `language` (type: `string`):

Language code for the scraped reviews (e.g. "en", "de", "cs").

## `modelName` (type: `string`):

The LLM used for tool orchestration and clustering reviews into themes.

## Actor input object example

```json
{
  "business": "Shake Shack Madison Square Park",
  "maxReviews": 50,
  "reviewsSort": "newest",
  "language": "en",
  "modelName": "gpt-4o"
}
```

# Actor output Schema

## `results` (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 = {
    "business": "Shake Shack Madison Square Park"
};

// Run the Actor and wait for it to finish
const run = await client.actor("apifydemo3/aabw-demo").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 = { "business": "Shake Shack Madison Square Park" }

# Run the Actor and wait for it to finish
run = client.actor("apifydemo3/aabw-demo").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 '{
  "business": "Shake Shack Madison Square Park"
}' |
apify call apifydemo3/aabw-demo --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Review insights agent",
        "description": "LangGraph AI agent in JavaScript that scrapes a business's reviews and clusters them into themes with sentiment and suggested actions.",
        "version": "0.0",
        "x-build-id": "EiQeKHyZ12PC1Z8fS"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/apifydemo3~aabw-demo/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-apifydemo3-aabw-demo",
                "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/apifydemo3~aabw-demo/runs": {
            "post": {
                "operationId": "runs-sync-apifydemo3-aabw-demo",
                "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/apifydemo3~aabw-demo/run-sync": {
            "post": {
                "operationId": "run-sync-apifydemo3-aabw-demo",
                "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": [
                    "business"
                ],
                "properties": {
                    "business": {
                        "title": "Business",
                        "type": "string",
                        "description": "The business to analyze - a name (e.g. \"Shake Shack Madison Square Park\") or a Google Maps place URL."
                    },
                    "maxReviews": {
                        "title": "Max reviews",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum number of recent reviews to fetch and analyze.",
                        "default": 50
                    },
                    "reviewsSort": {
                        "title": "Reviews sort order",
                        "enum": [
                            "newest",
                            "mostRelevant",
                            "highestRanking",
                            "lowestRanking"
                        ],
                        "type": "string",
                        "description": "How the reviews scraper orders reviews before fetching them.",
                        "default": "newest"
                    },
                    "language": {
                        "title": "Language",
                        "type": "string",
                        "description": "Language code for the scraped reviews (e.g. \"en\", \"de\", \"cs\").",
                        "default": "en"
                    },
                    "modelName": {
                        "title": "OpenAI model (currently the only supported provider)",
                        "enum": [
                            "gpt-4o-mini",
                            "gpt-4o"
                        ],
                        "type": "string",
                        "description": "The LLM used for tool orchestration and clustering reviews into themes.",
                        "default": "gpt-4o"
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
