# App Store Review Pain Miner (`creative_maitake/app-store-review-pain-miner`) Actor

Mine low-star public App Store reviews into scored product pain points and grouped app opportunity ideas.

- **URL**: https://apify.com/creative\_maitake/app-store-review-pain-miner.md
- **Developed by:** [Brian Murray](https://apify.com/creative_maitake) (community)
- **Categories:** Developer tools, Automation
- **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

## App Store Review Pain Miner

Find product pain points and app ideas by mining low-star public Apple App Store reviews.

This Actor searches Apple's public App Store APIs for a keyword/category, fetches recent public reviews for matching apps, filters to low-star reviews, classifies complaints with transparent deterministic rules, and outputs both review-level rows and grouped opportunity summaries.

### What you can use it for

- Indie app idea research
- Competitor complaint mining
- App Store niche validation
- Product backlog discovery
- Pricing/UX/support/reliability pain scans

Low-star reviews often reveal paid-pain patterns: subscription resentment, unreliable sync, confusing workflows, missing integrations, poor support, or privacy concerns.

### Example input

```json
{
  "query": "habit tracker",
  "country": "us",
  "maxApps": 5,
  "maxReviewsPerApp": 40,
  "maxReviewRating": 3,
  "includeUnknown": false
}
````

### Input fields

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `query` | string | required | App Store keyword/category to search, e.g. `habit tracker`, `budget app`, `invoice maker`. |
| `country` | string | `us` | Two-letter App Store country code used by Apple's public APIs. Invalid values fall back to `us`. |
| `maxApps` | integer | `5` | Number of search results to inspect. Capped at 25. |
| `maxReviewsPerApp` | integer | `25` | Number of recent reviews per app to inspect. Capped at 100. |
| `maxReviewRating` | integer | `3` | Only include reviews at or below this star rating. |
| `includeUnknown` | boolean | `false` | Include low-star reviews that do not match a known pain category. |

### Output

#### Dataset: review-level pain points

Each dataset row is one public App Store review with classification metadata:

```json
{
  "reviewKey": "983826477:costs-too-much",
  "appId": 983826477,
  "appName": "Productive - Habit Tracker",
  "developer": "Mosaic S.r.l.",
  "appRating": 4.6,
  "appUrl": "https://apps.apple.com/us/app/productive-habit-tracker/id983826477",
  "reviewTitle": "Too expensive",
  "reviewText": "Costs too much for a simple habit tracker...",
  "reviewRating": 1,
  "painCategory": "pricing",
  "confidence": 0.67,
  "severity": 1.0,
  "opportunityScore": 0.84,
  "matchedTerms": ["expensive", "too much"],
  "painSummary": "Pricing pain: Costs too much for a simple habit tracker...",
  "opportunity": "Offer a clearer, cheaper, or one-time-purchase alternative with fewer ads and transparent limits."
}
```

#### Key-value output: grouped opportunities

The Actor also writes `OPPORTUNITIES`, a grouped list by app and pain category. This is usually the best starting point for research triage:

```json
{
  "appName": "Strong Workout Tracker Gym Log",
  "painCategory": "reliability",
  "reviewCount": 7,
  "averageConfidence": 0.72,
  "averageSeverity": 0.87,
  "opportunityScore": 0.76,
  "matchedTerms": ["bug", "crashes", "sync"],
  "topPainSummaries": ["Reliability pain: ..."],
  "opportunity": "Differentiate on stability, fast bug fixes, dependable sync, and trustworthy data handling."
}
```

The `SUMMARY` key includes run metadata and the top 10 opportunity groups.

### Pain categories

| Category | Detects | Opportunity angle |
| --- | --- | --- |
| `pricing` | subscriptions, expensive plans, paywalls, ads | clearer pricing, cheaper tiers, one-time purchase, fewer ads |
| `reliability` | crashes, bugs, broken sync, lost data | stable sync, data safety, fast bug fixes |
| `ux` | confusing UI, clunky flows, too many steps | simpler workflow and opinionated defaults |
| `features` | missing features, imports/exports/integrations | focused workflow or integration gap |
| `support` | customer support, refunds, no response | responsive support and visible changelogs |
| `privacy` | privacy/tracking/permissions/personal data | privacy-first positioning and local-data options |
| `unknown` | unmatched low-star reviews | manual review when `includeUnknown` is enabled |

### How to read the scores

- `confidence`: how specifically the review matched the category keywords.
- `severity`: rating-derived pain score; 1-star reviews score highest.
- `opportunityScore`: average of confidence and severity, useful for sorting.

Scores are triage signals, not proof of market demand. Always read the source `reviewText` before making product decisions.

### Dogfood results from MVP validation

The Actor found useful opportunity clusters in real App Store scans, including:

- `invoice maker`: repeated pricing complaints around invoice limits and subscription cost.
- `calendar`: paid users complaining about ads and missing/unstable calendar features.
- `receipt scanner`: sync/data-loss/support complaints around tax/reimbursement workflows.
- `workout tracker`: repeated watch sync and crash complaints.
- `family organizer`: setup confusion and missing attachment/recurrence workflows.

These are promising enough for public beta, with the caveat that classification is deterministic and should be used for triage.

### Notes and limitations

- Uses Apple's public iTunes Search and RSS customer-review endpoints.
- Does not log in to Apple, bypass access controls, or collect private data.
- Classification is deterministic keyword matching, not LLM analysis. This keeps runs cheap and explainable.
- Apple endpoint availability varies by country/app; some apps may have unavailable review feeds.
- Search results can include adjacent apps when a query is broad.

### Local development

```bash
python -m pytest tests/test_pain_miner.py -q
apify validate-schema
apify run --purge
```

### Deployment

```bash
apify push
```

# Actor input Schema

## `query` (type: `string`):

Keyword or app category to search in the public Apple App Store, e.g. habit tracker, budget app, meal planner.

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

Two-letter App Store country code used by Apple's public APIs.

## `maxApps` (type: `integer`):

Number of App Store search results to inspect. Capped at 25.

## `maxReviewsPerApp` (type: `integer`):

Number of recent reviews to inspect for each app. Capped at 100.

## `maxReviewRating` (type: `integer`):

Only include reviews at or below this star rating, because low reviews are best for pain mining.

## `includeUnknown` (type: `boolean`):

If enabled, include low-star reviews that do not match a known pain category.

## Actor input object example

```json
{
  "query": "habit tracker",
  "country": "us",
  "maxApps": 5,
  "maxReviewsPerApp": 25,
  "maxReviewRating": 3,
  "includeUnknown": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `opportunities` (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 = {
    "query": "habit tracker",
    "country": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("creative_maitake/app-store-review-pain-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 = {
    "query": "habit tracker",
    "country": "us",
}

# Run the Actor and wait for it to finish
run = client.actor("creative_maitake/app-store-review-pain-miner").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 '{
  "query": "habit tracker",
  "country": "us"
}' |
apify call creative_maitake/app-store-review-pain-miner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=creative_maitake/app-store-review-pain-miner",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "App Store Review Pain Miner",
        "description": "Mine low-star public App Store reviews into scored product pain points and grouped app opportunity ideas.",
        "version": "0.2",
        "x-build-id": "AfIfoQEEgHQCjXRhi"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/creative_maitake~app-store-review-pain-miner/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-creative_maitake-app-store-review-pain-miner",
                "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/creative_maitake~app-store-review-pain-miner/runs": {
            "post": {
                "operationId": "runs-sync-creative_maitake-app-store-review-pain-miner",
                "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/creative_maitake~app-store-review-pain-miner/run-sync": {
            "post": {
                "operationId": "run-sync-creative_maitake-app-store-review-pain-miner",
                "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": [
                    "query"
                ],
                "properties": {
                    "query": {
                        "title": "App Store search query",
                        "type": "string",
                        "description": "Keyword or app category to search in the public Apple App Store, e.g. habit tracker, budget app, meal planner."
                    },
                    "country": {
                        "title": "Country code",
                        "type": "string",
                        "description": "Two-letter App Store country code used by Apple's public APIs.",
                        "default": "us"
                    },
                    "maxApps": {
                        "title": "Maximum apps to inspect",
                        "minimum": 1,
                        "maximum": 25,
                        "type": "integer",
                        "description": "Number of App Store search results to inspect. Capped at 25.",
                        "default": 5
                    },
                    "maxReviewsPerApp": {
                        "title": "Maximum recent reviews per app",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Number of recent reviews to inspect for each app. Capped at 100.",
                        "default": 25
                    },
                    "maxReviewRating": {
                        "title": "Maximum review rating",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Only include reviews at or below this star rating, because low reviews are best for pain mining.",
                        "default": 3
                    },
                    "includeUnknown": {
                        "title": "Include unclassified reviews",
                        "type": "boolean",
                        "description": "If enabled, include low-star reviews that do not match a known pain category.",
                        "default": false
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
