# Competitor Complaint Miner: App Store & Google Play Reviews (`damodaran_dhanuauraman/competitor-complaint-miner`) Actor

Turn competitor reviews from the App Store, Google Play, Chrome Web Store and Hacker News into ranked complaint themes, feature requests and short quotes with links. Reviewer names removed. Offline analysis or your own LLM key.

- **URL**: https://apify.com/damodaran_dhanuauraman/competitor-complaint-miner.md
- **Developed by:** [Damodaran Dhanusuraman](https://apify.com/damodaran_dhanuauraman) (community)
- **Categories:** Marketing, AI, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Competitor Complaint Miner

**Competitor review analysis API: App Store, Google Play and Chrome Web Store reviews (plus Hacker News mentions) turned into ranked complaint themes, feature requests and short quotes with links. $0.0015 per review + $0.30 per competitor report. No AI key needed.**

Find out what users complain about in your competitors' products. This Actor collects public reviews from the **Apple App Store**, **Google Play**, the **Chrome Web Store** and **Hacker News**. It turns them into a ranked list of **complaint themes** with counts, share of reviews, severity, trend, **requested features**, and **short quotes with links**. It also compares competitors side by side.

Reviewer names, avatars and user IDs are **never read or stored**. You get only the review text, stars, date, helpful votes, app version and a link.

### What you get

For each competitor:

- **Review rows**: source, stars, date, title, text, helpful votes, app version, link, sentiment, and matched themes.
- **Insight report**: ranked complaint themes, each with the number of reviews, share of reviews, average stars, severity (low/medium/high), trend (rising/falling/stable when dates allow), matched keywords, and up to 10 quotes (each 200 characters max) with links.
- **Feature requests**: sentences like "I wish…", "please add…", "would be great if…", "no way to…", grouped by keyword, with quotes.
- **Per-source status**: `ok`, `empty`, `blocked`, `not-found` or `failed`, with a message, so you can see what worked.

Across competitors:

- **Comparison table**: each theme's share of reviews for every competitor, which competitor has the highest share, and which pain points all competitors share.
- **Readable report**: a Markdown summary (`REPORT_MD`) and the full JSON (`REPORT`) in the key-value store.

### Sources

| Source | How to add it | What it reads |
|---|---|---|
| Apple App Store | App Store URL or `appstore:<app id>` | Apple's public customer-review RSS feed (about the 500 most recent reviews per country) |
| Google Play | Play URL or `play:<package>` | The public reviews feed of the Google Play web page, filtered by stars |
| Chrome Web Store | Extension URL or `cws:<extension id>` | The public reviews feed of the extension page, filtered by stars |
| Hacker News | `hn:<term>` | The official HN Algolia search API (comments). HN has no stars, so only comments with complaint or request words near your term are kept |
| Trustpilot (experimental) | Trustpilot URL or `trustpilot:<domain>` | Public review pages. **In our tests (Nov 2026), Trustpilot showed an anti-bot check to both our test machine and Apify's servers, so this source returned `blocked`.** It is kept in case access works for you. A blocked source is never charged. |

G2, Capterra and similar sites are **not supported on purpose** because of their terms and anti-bot protection.

The Actor uses only public pages you can open without logging in. It uses no cookies or logins, waits between requests, and backs off when a site slows it down. Sites can change their pages or block access at any time. When that happens the source shows `blocked` or `failed`, the run continues with the other sources, and you are not charged for it.

### Input

| Field | Default | Notes |
|---|---|---|
| `competitors` | required | One per line: `Name \| source, source, …`. Example: `Todoist \| https://apps.apple.com/us/app/todoist-to-do-list-planner/id572688855, play:com.todoist, hn:Todoist` |
| `maxReviewsPerCompetitor` | 200 | 1–500, shared fairly across that competitor's sources. If one source is blocked or small, the others fill the gap. |
| `minRating` / `maxRating` | 1 / 3 | Focus on complaints (1–3 stars). Set max to 5 for all reviews. |
| `dateFrom` / `dateTo` | none | `YYYY-MM-DD` or relative like `90 days`, `6 months`. |
| `language` | none | e.g. `en`, `de`. Used by Google Play, the Chrome Web Store and Trustpilot. |
| `country` | `us` | App Store and Google Play country. |
| `analysisMode` | `offline` | `offline` or `llm` (see below). |
| `llmProvider`, `llmApiKey`, `llmModel` | | Only for LLM mode. Your own key, stored as a secret input. |
| `includeReviewRows` | true | Turn off to get only the reports. You then pay only for the reports. |
| `hnComplaintsOnly` | true | Turn off to keep every HN mention. |
| `maxQuotesPerTheme` | 3 | 1–10. |

Example API input:

```json
{
  "competitors": [
    "Todoist | https://apps.apple.com/us/app/todoist-to-do-list-planner/id572688855, play:com.todoist, hn:Todoist",
    "TickTick | appstore:626144601, play:com.ticktick.task",
    "Grammarly | cws:kbfnbcaeplbcioakkpcpgfkobkghlhen"
  ],
  "maxReviewsPerCompetitor": 200,
  "dateFrom": "6 months"
}
```

### Offline mode vs LLM mode

**Offline (default).** No AI model is used and there is no AI cost. Themes come from a fixed keyword list: bugs and crashes, speed, pricing and billing, ads, support, login and account, sync and data loss, UI, bad updates, privacy, notifications, compatibility, outages, quality, orders and delivery. Frequent phrases in reviews that match none of these are added as extra themes. Sentiment comes from the stars (word lists for Hacker News). Feature requests come from phrase patterns. **This is a heuristic.** It counts keyword and phrase matches and does not understand context, so a review can land in the wrong theme or in more than one. Ambiguous words are matched only in complaint phrases (for example "my order never arrived", not "sorted in the order created"; "servers are down", not "write down"), and these cases are covered by regression tests. Read the quotes before you act on a theme.

**LLM mode (bring your own key).** Choose OpenAI, Anthropic, Google Gemini, xAI or OpenRouter and add your own API key. Up to 150 review snippets per competitor go to your provider, which groups them into themes. The Actor then rebuilds the counts, shares and quotes from the real reviews, so quotes are never written by the AI. If the AI call fails, the run falls back to offline mode and says so in the report. Your provider bills you for this usage. The key is used only for the run and is never logged or written to the output. Default models: `gpt-4.1-mini`, `claude-haiku-4-5`, `gemini-2.5-flash`, `grok-4.7`, `openai/gpt-4.1-mini` (OpenRouter). You can change the model with `llmModel`.
*Honest note:* the LLM adapters follow each provider's public API docs, but they have only been tested with sample responses, not with live API calls. Please report any problem in the Issues tab.

### Output sample (from a real run on Apify, 3 Nov 2026)

Input: Todoist (App Store, Google Play, HN), TickTick (App Store, Google Play), Grammarly (Chrome Web Store, Trustpilot), 30 reviews each, 1–3 stars. The run took 12 seconds. Trustpilot returned `blocked`, and the other sources returned reviews.

One complaint theme (`insights` dataset, view **Complaint themes**):

```json
{
  "competitor": "TickTick",
  "rank": 1,
  "theme": "Pricing, subscriptions and billing",
  "reviews": 9,
  "share": 0.3,
  "severity": "medium",
  "trend": "rising",
  "olderShare": 0.2,
  "newerShare": 0.32,
  "avgRating": 1.89,
  "keywords": ["pay", "expensive", "subscription", "premium", "free version", "price"],
  "quotes": [
    {
      "quote": "This is now more expensive than Todoist. Not worth it.",
      "rating": 1,
      "date": "2026-10-26T18:05:37.000Z",
      "source": "googleplay",
      "url": "https://play.google.com/store/apps/details?id=com.ticktick.task&reviewId=6312bc5d-df2d-40e8-ad88-fe91face4690&hl=en"
    }
  ]
}
```

One feature request:

```json
{ "competitor": "TickTick", "request": "unlimited habits", "mentions": 1,
  "quotes": [{ "quote": "Wish you could just do unlimited habits, but it’s still pretty good", "rating": 3, "source": "appstore", "url": "https://apps.apple.com/us/app/id626144601?see-all=reviews" }] }
```

One comparison row:

```json
{ "theme": "Pricing, subscriptions and billing", "worstCompetitor": "TickTick", "averageShare": 0.211, "competitorsAffected": 3,
  "byCompetitor": { "Todoist": { "share": 0.067, "reviews": 2 }, "TickTick": { "share": 0.3, "reviews": 9 }, "Grammarly": { "share": 0.267, "reviews": 8 } } }
```

A blocked source in the per-source status:

```json
{ "source": "trustpilot", "ref": "grammarly.com", "status": "blocked", "reviewsReturned": 0, "message": "Blocked by the site (HTTP 403, anti-bot challenge). Not retried." }
```

### Pricing (pay per event)

| Event | Price | When it is charged |
|---|---|---|
| `review-collected` | **$0.0015** per review | For each review row actually returned in your dataset |
| `insight-report` | **$0.30** per competitor | Once for each competitor that has at least one collected review. One report covers up to 500 reviews. |

Example: 3 competitors × 200 reviews = 600 × $0.0015 + 3 × $0.30 = **$1.80**.

- Failed, blocked and empty sources are **not charged**. A competitor with no reviews gets no report and no charge.
- A run with no valid competitors stops with a clear message and charges nothing.
- With `includeReviewRows` off, you pay only $0.30 per report.
- The run respects your **maximum cost per run**. Before each competitor, the Actor keeps enough budget for its report, collects only what fits, and then stops.
- Apify platform usage for this Actor is small (HTTP requests only, no browser).

### Limits and good practice

- Apple's RSS feed shows only about the 500 most recent reviews per country. Google Play and the Chrome Web Store return up to 500 reviews per star level in this Actor.
- The App Store feed has no link to a single review, so App Store quotes link to the app's reviews page.
- Hacker News results are mentions, not reviews. A word like "notion" is also a normal English word, so use a capitalised name (`hn:Notion`, matched case-sensitively) or a distinctive term.
- Themes, severity and trend are computed signals, not human analysis. Trend needs at least 10 dated reviews spanning 4+ weeks; otherwise it says `insufficient-data`.
- Reviews are public text written by real people. Quote them briefly with a link, as this Actor does, and follow the source sites' terms and your local privacy rules when you use the data.

### How it compares

- **vs. a raw review scraper:** a scraper gives you thousands of rows to read. This Actor also groups them into ranked themes with counts, severity, trend, feature requests and quotes, and compares competitors side by side. If you only want raw rows, a plain scraper may be cheaper.
- **vs. a review-analytics SaaS:** no subscription; you pay per review and per report. Offline mode is keyword-based, not AI. LLM mode uses your own key and has only been tested with sample responses so far.
- **Coverage, honestly:** App Store, Google Play and the Chrome Web Store work. Hacker News gives mentions, not reviews. Trustpilot is currently blocked by an anti-bot check in our tests. G2 and Capterra are not supported.

### Schedule it (monthly complaint digest)

1. Save your competitor list as a **Task** in Apify Console, with `dateFrom: "30 days"`.
2. Add a monthly **Apify Schedule**.
3. Send the `insights` dataset or the `REPORT_MD` record to Notion, Slack or Google Docs. Set `includeReviewRows: false` if you only want the reports ($0.30 per competitor).

### Use in n8n, Make, Clay or any HTTP tool

```
POST https://api.apify.com/v2/acts/damodaran_dhanuauraman~competitor-complaint-miner/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>
Content-Type: application/json

{ "competitors": ["Todoist | play:com.todoist, appstore:572688855"], "maxReviewsPerCompetitor": 50 }
```

This returns the review rows. For the ranked themes, read the `insights` dataset or the `REPORT` key-value record of the run (both are linked in the run's Output tab). In n8n or Make, use the Apify "Run Actor" module on a schedule, then fetch the `insights` dataset and create a Notion page or Slack message with the top themes and quotes.

### FAQ

**Do I need an AI key?** No. Offline mode works without one. LLM mode is optional and uses your own key.
**Are reviewer names included?** No. Names, avatars and user IDs are never read or stored.
**What does a run cost?** 3 competitors × 200 reviews ≈ $1.80. Blocked, failed and empty sources are free.
**How accurate are the themes?** They are keyword signals, not human analysis. Always read the quotes before you act on a theme.
**Can I add other sources?** Open an issue with the source you need. Shopify App Store and WordPress.org are on the list.

### Feedback

Found a wrong theme, a source that stopped working, or a missing source (Shopify App Store and WordPress.org are on the list)? Please open an issue in the **Issues** tab.

# Actor input Schema

## `competitors` (type: `array`):

One competitor per line: a name, a pipe, then one or more sources separated by commas. Sources can be store URLs or short prefixes: App Store URL or appstore:<app id>; Google Play URL or play:<package>; Chrome Web Store URL or cws:<extension id>; Trustpilot URL or trustpilot:<domain>; Hacker News search with hn:<term>. Example: "Todoist | https://apps.apple.com/us/app/todoist-to-do-list-planner/id572688855, play:com.todoist, hn:Todoist". A single URL without a name also works. G2 and Capterra are not supported.

## `maxReviewsPerCompetitor` (type: `integer`):

Reviews to collect per competitor, split across its sources. One insight report covers up to 500 reviews.

## `minRating` (type: `integer`):

Lowest star rating to include (1-5). Hacker News has no stars and is not filtered by rating.

## `maxRating` (type: `integer`):

Highest star rating to include. The default 3 focuses on complaints (1-3 stars). Use 5 to include all reviews.

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

Only reviews on or after this date. Use YYYY-MM-DD or a relative value like "90 days", "6 months", "1 year".

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

Only reviews on or before this date (YYYY-MM-DD).

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

Two-letter language code, e.g. en, de, fr. Used by Google Play and the Chrome Web Store, and as a Trustpilot filter. Leave empty for English on Google Play/Chrome and all languages on Trustpilot.

## `country` (type: `string`):

Two-letter country code for the App Store and Google Play, e.g. us, gb, in, de. An App Store URL with a country overrides this.

## `analysisMode` (type: `string`):

offline (default, free of any AI cost): keyword lexicon, phrase clustering and rating-based sentiment. llm: your own LLM API key groups the complaints; counts and quotes still come from the real reviews. Falls back to offline if the LLM call fails.

## `llmProvider` (type: `string`):

Which API your key is for.

## `llmApiKey` (type: `string`):

Your own key. Used only for this run to send up to 150 review snippets per competitor to your provider. Never logged or written to the output. Your provider bills you for this usage.

## `llmModel` (type: `string`):

Defaults: openai gpt-4.1-mini, anthropic claude-haiku-4-5, gemini gemini-2.5-flash, xai grok-4.7, openrouter openai/gpt-4.1-mini.

## `includeReviewRows` (type: `boolean`):

When off, only the insight reports are saved, and you pay only for the reports (no per-review charge).

## `hnComplaintsOnly` (type: `boolean`):

Keep only Hacker News comments where complaint or feature-request words appear next to the term. Turn off to keep every mention.

## `maxQuotesPerTheme` (type: `integer`):

Short quotes (max 200 characters, with links) shown for each complaint theme.

## Actor input object example

```json
{
  "competitors": [
    "Notion | https://apps.apple.com/us/app/notion-notes-tasks-ai/id1232780281, play:notion.id, trustpilot:notion.so, hn:Notion",
    "Grammarly | cws:kbfnbcaeplbcioakkpcpgfkobkghlhen, trustpilot:grammarly.com"
  ],
  "maxReviewsPerCompetitor": 100,
  "minRating": 1,
  "maxRating": 3,
  "dateFrom": "6 months",
  "country": "us",
  "analysisMode": "offline",
  "llmProvider": "openai",
  "includeReviewRows": true,
  "hnComplaintsOnly": true,
  "maxQuotesPerTheme": 3
}
```

# Actor output Schema

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

Collected review rows: text, stars, date, source, link, helpful votes. No reviewer names.

## `themes` (type: `string`):

Ranked complaint themes per competitor with counts, share, severity, trend and quotes.

## `featureRequests` (type: `string`):

No description

## `overview` (type: `string`):

No description

## `comparison` (type: `string`):

No description

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

No description

## `reportMarkdown` (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 = {
    "competitors": [
        "Todoist | https://apps.apple.com/us/app/todoist-to-do-list-planner/id572688855, play:com.todoist",
        "TickTick | appstore:626144601, play:com.ticktick.task"
    ],
    "maxReviewsPerCompetitor": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("damodaran_dhanuauraman/competitor-complaint-miner").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 = {
    "competitors": [
        "Todoist | https://apps.apple.com/us/app/todoist-to-do-list-planner/id572688855, play:com.todoist",
        "TickTick | appstore:626144601, play:com.ticktick.task",
    ],
    "maxReviewsPerCompetitor": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("damodaran_dhanuauraman/competitor-complaint-miner").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 '{
  "competitors": [
    "Todoist | https://apps.apple.com/us/app/todoist-to-do-list-planner/id572688855, play:com.todoist",
    "TickTick | appstore:626144601, play:com.ticktick.task"
  ],
  "maxReviewsPerCompetitor": 100
}' |
apify call damodaran_dhanuauraman/competitor-complaint-miner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,damodaran_dhanuauraman/competitor-complaint-miner"
        }
    }
}
```

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/00DJvkBoiegMLpDfg/builds/E9pDlv1wjrnScpz79/openapi.json
