# Review Sentiment Analyzer: Bulk Sentiment & Topic Tags (`jobdata_labs/review-sentiment-analyzer`) Actor

Add sentiment score, label, topics and defect flags to reviews you already have. Paste reviews or point to any Apify dataset. Rule-based, 5 languages, no LLM costs, no scraping.

- **URL**: https://apify.com/jobdata_labs/review-sentiment-analyzer.md
- **Developed by:** [Momi](https://apify.com/jobdata_labs) (community)
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 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/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 Sentiment Analyzer

Add a sentiment score, a sentiment label, topics and defect flags to reviews **you already have**. Paste them in, or point the Actor at any Apify dataset (for example the output of a review scraper you ran), and get one enriched row per review plus an aggregate summary.

This Actor does not scrape any website. It only processes the data you give it.

### What it does

- **Sentiment score and label** for every review: a score from -1.0 to +1.0 and `POSITIVE`, `NEUTRAL` or `NEGATIVE`.
- **Topic tags**: Quality & Build, Price & Value, Shipping & Delivery, Durability & Defects, Customer Service, Usability.
- **Defect flag** when the reviewer reports a broken or non-working product.
- **Rating/text mismatch flag** when stars and wording strongly disagree (5 stars with a complaint), which is useful for spotting mis-rated or suspicious reviews.
- **Summary report** with the sentiment breakdown, average score, average rating and sentiment per topic.
- **Works with any field names**: common names are auto-detected, or you can map them yourself.
- **Five languages**: English, Italian, Spanish, German and French.

### How the analysis works

The engine is rule-based, not a large language model. It counts positive and negative terms from built-in word lists, handles simple negations ("not good", "doesn't work"), and blends the result with the star rating when one is present (60% rating, 40% text). The `sentimentBasis` field tells you which signals were used for each review.

What that means in practice:

- Results are deterministic, fast and cheap: thousands of reviews take seconds.
- Sarcasm, irony and subtle wording are not understood. Reviews with no rating and no known sentiment words come back as `NEUTRAL` with `sentimentBasis: "none"`.
- Languages other than the five listed are scored from the rating only.

If you need nuance on every single review, use an LLM-based tool. If you need a fast, consistent first pass over a large volume, this fits.

### Input

| Field | Type | Description |
| :--- | :--- | :--- |
| `reviews` | Array | Reviews as plain strings or objects. Optional if `datasetId` is set. |
| `datasetId` | String | ID or name of an Apify dataset in your account. Read only. |
| `textField` | String | Field with the review text. Dot paths (`review.body`) work. Auto-detected if empty. |
| `titleField` | String | Field with the review title. Auto-detected if empty. |
| `ratingField` | String | Field with the rating. Numbers and strings like `4.0 out of 5 stars` work. Auto-detected if empty. |
| `idField` | String | Field with the review ID, copied to the output. Auto-detected if empty. |
| `ratingScale` | Integer | Highest possible rating: `5` for stars, `10` for a 1-10 scale. Default `5`. |
| `maxItems` | Integer | Stop after this many reviews. `0` means no limit. Default `1000`. |
| `keepFields` | Array | Extra fields to copy unchanged to each result, for example `productId`. |

Auto-detected field names: text (`text`, `reviewText`, `review`, `body`, `reviewBody`, `content`, `comment`, `message`, `snippet`, `description`), title (`title`, `reviewTitle`, `headline`, `subject`), rating (`rating`, `stars`, `ratingScore`, `score`, `starRating`, `reviewRating`, `ratingValue`), ID (`id`, `reviewId`, `review_id`, `uuid`).

#### Example: inline reviews

```json
{
  "reviews": [
    { "id": "r1", "title": "Great value", "text": "Excellent build quality, arrived early.", "rating": 5 },
    { "id": "r2", "text": "Broken after three weeks. Refund refused.", "rating": 1 },
    "Not good, the packaging was damaged."
  ]
}
```

#### Example: a dataset from another Actor

```json
{
  "datasetId": "YOUR_DATASET_ID",
  "textField": "reviewText",
  "ratingField": "stars",
  "keepFields": ["productId", "date"],
  "maxItems": 5000
}
```

### Output

One dataset row per analyzed review:

```json
{
  "reviewId": "r2",
  "reviewTitle": "Stopped working",
  "reviewText": "Broken after three weeks. Customer service refused a refund. Waste of money.",
  "rating": 1,
  "sentimentScore": -1,
  "sentimentLabel": "NEGATIVE",
  "sentimentBasis": "rating+text",
  "positiveTerms": 0,
  "negativeTerms": 3,
  "detectedTopics": ["Price & Value", "Durability & Defects", "Customer Service"],
  "isDefectReported": true,
  "ratingTextMismatch": false,
  "source": "input"
}
```

The aggregate report is saved in the run's key-value store under the key `SUMMARY`, and is also printed in the log:

```json
{
  "reviewsAnalyzed": 5,
  "averageSentimentScore": 0.06,
  "averageRating": 3.2,
  "sentimentBreakdown": {
    "positive": { "count": 2, "percent": 40 },
    "neutral": { "count": 1, "percent": 20 },
    "negative": { "count": 2, "percent": 40 }
  },
  "topics": [
    { "topic": "Shipping & Delivery", "mentions": 3, "positive": 2, "neutral": 0, "negative": 1 }
  ],
  "defectsReported": { "count": 1, "percent": 20 }
}
```

### Privacy

Only the review ID, title, text and rating are copied to the output. Reviewer names, profile links and every other field in your source data are left out unless you list them in `keepFields`.

### Pricing

You pay per analyzed review; the current rate is shown on the Actor's pricing tab. Rows skipped because they have neither text nor rating are not charged. Use `maxItems` to cap the cost of a run.

### Common uses

- Triage the output of a review scraper: sort by `sentimentScore`, filter `isDefectReported`.
- Track sentiment per topic over time by running the same dataset mapping on a schedule.
- Pre-filter a large review set before sending only the negative or mismatched ones to an LLM.
- Feed alerts in Make, Zapier or n8n when a run finds new negative reviews.

### FAQ

**Does it collect reviews from Amazon, Google or other sites?**
No. It analyzes data you provide. Collect reviews with a source you are allowed to use, then run this Actor on the result.

**Can I read a dataset from another account?**
Only datasets your account can access. The Actor gets read-only access to the one you select.

**My fields have unusual names. What do I do?**
Set `textField`, `ratingField`, `titleField` and `idField`. Nested fields use dot paths, for example `review.rating.value`.

**Why is a review NEUTRAL when it reads as negative?**
The wording was probably outside the built-in word lists and there was no rating. Open an issue with the example and the lists can be extended.

# Actor input Schema

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

Reviews to analyze. Each item is either a plain text string or an object with a text field and, optionally, a title, a rating and an ID. Leave empty if you use a dataset instead.

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

ID or name of an Apify dataset from your account that contains reviews, for example the output of a review scraper you already ran. It is read only; results go to this run's own dataset.

## `textField` (type: `string`):

Name of the field holding the review text. Dot paths such as review.body are supported. Leave empty to auto-detect common names (text, reviewText, review, body, content, comment).

## `titleField` (type: `string`):

Name of the field holding the review title or headline. Leave empty to auto-detect (title, reviewTitle, headline).

## `ratingField` (type: `string`):

Name of the field holding the star rating. Numbers and strings such as '4.0 out of 5 stars' both work. Leave empty to auto-detect (rating, stars, ratingScore, score).

## `idField` (type: `string`):

Name of the field holding the review ID, copied to the output so you can join results back to your data. Leave empty to auto-detect (id, reviewId).

## `ratingScale` (type: `integer`):

Highest possible rating in your data: 5 for stars, 10 for a 1-10 scale.

## `maxItems` (type: `integer`):

Stop after this many reviews. You are charged only for reviews actually analyzed. Set to 0 for no limit.

## `keepFields` (type: `array`):

Field names from your data to copy unchanged to each result, for example productId or date. By default only the ID, title, text and rating are kept, so reviewer names and other personal data are not carried over.

## Actor input object example

```json
{
  "reviews": [
    {
      "id": "r1",
      "title": "Great value",
      "text": "Excellent build quality and it arrived two days early. Easy to set up, highly recommend.",
      "rating": 5
    },
    {
      "id": "r2",
      "title": "Stopped working",
      "text": "Broken after three weeks. Customer service refused a refund. Waste of money.",
      "rating": 1
    },
    {
      "id": "r3",
      "text": "It is ok for the price, nothing special.",
      "rating": 3
    },
    {
      "id": "r4",
      "title": "Ottimo prodotto",
      "text": "Materiali solidi e spedizione veloce, lo consiglio.",
      "rating": 5
    },
    {
      "id": "r5",
      "text": "Not good. The packaging was damaged and the seller was slow to reply.",
      "rating": 2
    }
  ],
  "ratingScale": 5,
  "maxItems": 1000
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "reviews": [
        {
            "id": "r1",
            "title": "Great value",
            "text": "Excellent build quality and it arrived two days early. Easy to set up, highly recommend.",
            "rating": 5
        },
        {
            "id": "r2",
            "title": "Stopped working",
            "text": "Broken after three weeks. Customer service refused a refund. Waste of money.",
            "rating": 1
        },
        {
            "id": "r3",
            "text": "It is ok for the price, nothing special.",
            "rating": 3
        },
        {
            "id": "r4",
            "title": "Ottimo prodotto",
            "text": "Materiali solidi e spedizione veloce, lo consiglio.",
            "rating": 5
        },
        {
            "id": "r5",
            "text": "Not good. The packaging was damaged and the seller was slow to reply.",
            "rating": 2
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jobdata_labs/review-sentiment-analyzer").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": [
        {
            "id": "r1",
            "title": "Great value",
            "text": "Excellent build quality and it arrived two days early. Easy to set up, highly recommend.",
            "rating": 5,
        },
        {
            "id": "r2",
            "title": "Stopped working",
            "text": "Broken after three weeks. Customer service refused a refund. Waste of money.",
            "rating": 1,
        },
        {
            "id": "r3",
            "text": "It is ok for the price, nothing special.",
            "rating": 3,
        },
        {
            "id": "r4",
            "title": "Ottimo prodotto",
            "text": "Materiali solidi e spedizione veloce, lo consiglio.",
            "rating": 5,
        },
        {
            "id": "r5",
            "text": "Not good. The packaging was damaged and the seller was slow to reply.",
            "rating": 2,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("jobdata_labs/review-sentiment-analyzer").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": [
    {
      "id": "r1",
      "title": "Great value",
      "text": "Excellent build quality and it arrived two days early. Easy to set up, highly recommend.",
      "rating": 5
    },
    {
      "id": "r2",
      "title": "Stopped working",
      "text": "Broken after three weeks. Customer service refused a refund. Waste of money.",
      "rating": 1
    },
    {
      "id": "r3",
      "text": "It is ok for the price, nothing special.",
      "rating": 3
    },
    {
      "id": "r4",
      "title": "Ottimo prodotto",
      "text": "Materiali solidi e spedizione veloce, lo consiglio.",
      "rating": 5
    },
    {
      "id": "r5",
      "text": "Not good. The packaging was damaged and the seller was slow to reply.",
      "rating": 2
    }
  ]
}' |
apify call jobdata_labs/review-sentiment-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobdata_labs/review-sentiment-analyzer"
        }
    }
}
```

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/EQcWA46OdiwwkrC5y/builds/ynYr0JeNy7r3gV32Q/openapi.json
