# App Store Scraper - iOS & Google Play Apps & Reviews (`harvestlab/app-store-scraper`) Actor

Scrape Apple App Store and Google Play app listings with ratings, reviews, prices, downloads, and developer info. Search by keyword or app URL. AI-powered ASO analysis with competitor benchmarking and keyword opportunities. For mobile marketers, ASO teams, and app market researchers.

- **URL**: https://apify.com/harvestlab/app-store-scraper.md
- **Developed by:** [Nick](https://apify.com/harvestlab) (community)
- **Categories:** AI, Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 app scrapeds

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/platform/actors/running/actors-in-store#pay-per-event

## 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 Scraper - iOS & Google Play Apps & Reviews

Scrape app listings from Apple App Store and Google Play Store. Extract app names, ratings, reviews, prices, download counts, developer info, screenshots, and more. Search by keyword or scrape specific app URLs directly.

Optionally generate AI-powered ASO (App Store Optimization) analysis with keyword opportunities, competitor benchmarking, market positioning, and actionable recommendations for mobile marketers.

### Features

- **Dual store support**: Scrape both Apple App Store and Google Play in a single run, or target one store
- **Search scraping**: Enter keywords like "fitness tracker" or "photo editor" to find and scrape matching apps
- **Direct URL scraping**: Provide specific App Store or Google Play URLs for detailed extraction
- **Apple App Store**: Uses the official iTunes Search API — fast, reliable, returns structured JSON with 30+ fields per app
- **Google Play**: Server-rendered HTML scraping with rating, install count, developer, and metadata extraction
- **User reviews**: Scrape up to 500 reviews per Apple app (via iTunes RSS paging) or visible Google Play reviews — configurable via `maxReviews`. Apple supports both `mostRecent` and `mostHelpful` sort orders.
- **Per-review sentiment + topic + urgency (free)**: Every review is automatically graded with sentiment (positive/neutral/negative), topic tags (crash, bug, login, pricing, feature_request, ux, performance, ads, privacy, update, praise, support), urgency (low/medium/high), and a churn-signal flag — with a per-app roll-up (`review_sentiment_summary`). Free, deterministic, runs without any LLM API key. Turns the scraper into a PM + churn-alert tool on top of the raw listing data.
- **Reputation-delta watch mode**: Opt in with `trackReputation=true` to persist per-app sentiment history across runs in a named key-value store. Each subsequent run computes `negative_rate_delta`, `urgency_high_delta`, `churn_signal_delta`, `new_topic_tags_emerged`, `fading_topic_tags`, and a `sentiment_trend` (improving/declining/stable), and emits a single `reputation_change_digest` summary row so PM dashboards can pivot on the biggest shifts. Pair with a scheduled weekly run against your app + top-N competitors to turn the scraper into a reputation watchdog.
- **AI ASO analysis**: Competitive analysis, keyword opportunities, market gaps, and optimization recommendations
- **Multi-LLM support**: Choose between OpenRouter (recommended — 300+ models), Anthropic (Claude), Google AI (Gemini), OpenAI, or Ollama (self-hosted) for AI analysis
- **Global coverage**: Search any country's app store with country code parameter (US, UK, DE, JP, etc.)
- **Pay-per-result pricing**: Only pay for apps successfully scraped

### Data Extracted

#### Apple App Store Fields

| Field | Description |
|-------|-------------|
| `app_id` | Numeric App Store ID |
| `bundle_id` | App bundle identifier (e.g., com.example.app) |
| `name` | App name |
| `developer` | Developer/publisher name |
| `developer_id` | Developer's App Store ID |
| `developer_url` | Link to developer's App Store page |
| `price` | Numeric price (0 for free apps) |
| `formatted_price` | Display price string (e.g., "Free", "$4.99") |
| `currency` | Currency code (USD, EUR, etc.) |
| `rating` | Average user rating (out of 5) |
| `rating_count` | Total number of ratings |
| `current_version_rating` | Rating for the current version |
| `description` | App description (first 1000 characters) |
| `release_notes` | Latest version release notes |
| `version` | Current version number |
| `icon_url` | App icon image URL (512px) |
| `screenshots` | Up to 5 iPhone screenshot URLs |
| `size_bytes` | App download size in bytes |
| `primary_genre` | Main category (Games, Productivity, etc.) |
| `genres` | All categories the app belongs to |
| `content_rating` | Age rating (4+, 9+, 12+, 17+) |
| `minimum_os_version` | Minimum iOS version required |
| `supported_devices` | Number of supported device types |
| `languages` | Supported language codes |
| `release_date` | Original release date |
| `updated_date` | Last update date |
| `app_url` | Direct link to the App Store listing (legacy — prefer `url`) |
| `url` | Portfolio-standard direct link (same value as `app_url`) |

#### Google Play Fields

| Field | Description |
|-------|-------------|
| `app_id` | Package name (e.g., com.example.app) |
| `name` | App name |
| `developer` | Developer/publisher name |
| `rating` | Average user rating (out of 5) |
| `rating_count` | Total number of ratings |
| `installs` | Install count range (e.g., "10M+") |
| `price` | Numeric price (0 for free apps) |
| `description` | App description |
| `icon_url` | App icon image URL |
| `screenshots` | Screenshot image URLs |
| `primary_genre` | Main category |
| `content_rating` | Content rating label |
| `requires_android` | Minimum Android version |
| `updated_date` | Last update date |
| `in_app_purchases` | Whether app has in-app purchases |
| `contains_ads` | Whether app contains ads |
| `app_url` | Direct link to the Google Play listing (legacy — prefer `url`) |
| `url` | Portfolio-standard direct link (same value as `app_url`) |

#### Review Fields (when `includeReviews: true`)

Each review in `reviews[]` carries:

| Field | Description |
|-------|-------------|
| `author` | Reviewer display name |
| `rating` | Star rating 1-5 |
| `title` | Review title (Apple only) |
| `text` | Review body text |
| `date` | Review date (Apple; ISO 8601) |
| `version` | App version reviewed (Apple) |
| `vote_count` / `vote_sum` | Helpful votes (Apple) |
| `sentiment_label` | `positive` / `neutral` / `negative` — heuristic classifier |
| `sentiment_score` | Float in `[-1.0, 1.0]` (rating-weighted + lexicon blend) |
| `sentiment_signal` | `rating` / `lexicon` / `both` — which signal drove the score |
| `topic_tags` | List of tags: `crash`, `bug`, `login`, `pricing`, `feature_request`, `ux`, `performance`, `ads`, `privacy`, `update`, `praise`, `support` |
| `urgency` | `low` / `medium` / `high` — escalation priority (high = negative + crash/bug/pricing/login/privacy OR churn signal) |
| `is_churn_signal` | `true` if the review explicitly signals uninstall / unsubscribe / switching |

#### Per-App `review_sentiment_summary`

When reviews are scraped, each app also gets a rollup object:

```json
"review_sentiment_summary": {
    "review_count": 100,
    "sentiment_counts": {"positive": 58, "neutral": 15, "negative": 27},
    "negative_rate": 0.27,
    "urgency_high_count": 12,
    "churn_signal_count": 5,
    "top_topic_tags": [
        {"tag": "crash", "count": 18},
        {"tag": "pricing", "count": 11},
        {"tag": "praise", "count": 9}
    ]
}
````

Point a PM dashboard at `negative_rate` / `urgency_high_count` / `churn_signal_count` for each app in your competitive set and you have a churn-risk + complaint-ticker feed, no LLM required.

#### Reputation-Delta Watch Mode (optional, `trackReputation=true`)

Adds these fields to every app on every run AFTER the first:

| Field | Description |
|-------|-------------|
| `reputation_delta_tracked` | `true` when this app had its sentiment compared against history |
| `negative_rate_previous` / `negative_rate_delta` | Prior run's negative rate + current - previous (float) |
| `urgency_high_previous` / `urgency_high_delta` | Prior high-urgency count + delta (int) |
| `churn_signal_previous` / `churn_signal_delta` | Prior churn-signal count + delta (int) |
| `new_topic_tags_emerged` | Topic tags present in this run that were NEVER in prior history |
| `fading_topic_tags` | Topic tags seen in prior history but absent this run |
| `sentiment_trend` | `improving` / `declining` / `stable` / `unknown` (vs prior-history mean negative\_rate) |
| `reputation_change_detected` | `true` when any threshold (8pp negative-rate / 2 urgency / 2 churn / new topics) crossed |
| `change_kind` | `new` / `negative_rate_up` / `negative_rate_down` / `urgency_up` / `urgency_down` / `churn_up` / `churn_down` / `new_topics` / `none` |
| `history_sample_count` | Number of prior snapshots stored for this app |
| `first_seen_at` / `last_seen_at` | Earliest + most recent prior snapshot timestamps (ISO 8601) |

One `reputation_change_digest` row per run summarises the biggest movers:

```json
{
    "type": "reputation_change_digest",
    "watch_label": "duolingo",
    "total_apps": 5,
    "apps_tracked": 5,
    "apps_with_history": 3,
    "changes_detected": 2,
    "top_negative_rate_movers": [...],
    "newly_emerged_topics": [...],
    "churn_and_urgency_risers": [...]
}
```

History is stored in a **named** Apify key-value store (`app-store-reputation-history`), keyed by watch label, capped at 52 snapshots per app (roughly one year at weekly cadence). Use distinct `watchLabel` values to track unrelated keyword clusters without cross-contamination (e.g., `my-app`, `competitor-audit-q2`).

### Input Configuration

#### Search Parameters

- **Search Query**: Keywords to search for (e.g., "fitness tracker", "photo editor", "vpn")
- **App URLs**: Direct Apple App Store or Google Play URLs to scrape specific apps
- **App Store**: Which store to search — Both Stores, Apple App Store only, or Google Play only (default: both)
- **Country**: Country code for regional results — us, gb, de, jp, fr, etc. (default: us)
- **Max Apps**: Maximum number of apps to scrape per store (1-200, default: 25)
- **Include Reviews**: Scrape user reviews per app (default: off)
- **Max Reviews**: How many reviews to pull per app when reviews are included (1-500, default: 10). Apple iTunes RSS supports up to 500 via 10-page pagination; Google Play is limited to what's rendered in the initial HTML (typically 40-80).
- **Review Sort** (Apple only): `mostRecent` (default) for fresh user feedback, or `mostHelpful` for the most-upvoted opinions.

#### Reputation Watch Mode

- **Track Reputation Deltas** (`trackReputation`): Persist per-app sentiment history across runs in a named key-value store (default: off). Auto-enables `includeReviews`.
- **Watch Label** (`watchLabel`): Custom label used as the history slot key. Defaults to `searchQuery` or first app URL. Use distinct labels (e.g. `my-app`, `duolingo-competitors`) so separate tracking campaigns don't overwrite each other.

#### AI Analysis Settings

- **Enable AI Analysis**: Toggle AI-powered ASO analysis (default: off)
- **LLM Provider**: OpenRouter (recommended — 300+ models), Anthropic (Claude), Google AI (Gemini), or Ollama (self-hosted)
- **API Key**: Your chosen provider's API key
- **Model Override**: Optional custom model selection

#### Proxy Settings

Datacenter proxies generally work for both app stores. Apple's iTunes API is public and rarely blocks. Google Play may occasionally require residential proxies for high-volume scraping.

### AI ASO Analysis

When enabled, the AI analysis provides:

- **Market Overview**: Category distribution, pricing trends, average ratings, competitive density
- **Keyword Opportunities**: High-potential keywords with low competition, long-tail phrases, trending terms
- **Competitive Benchmarks**: Rating distributions, pricing strategy patterns, feature comparisons across top apps
- **Monetization Insights**: Free vs. paid vs. freemium ratios, price point analysis, in-app purchase prevalence
- **ASO Recommendations**: Title optimization suggestions, description keyword placement, screenshot strategy, localization priorities

### Example Usage

#### Search both stores

```json
{
    "searchQuery": "fitness tracker",
    "store": "both",
    "country": "us",
    "maxApps": 25,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
}
```

#### Scrape specific app URLs

```json
{
    "appUrls": [
        "https://apps.apple.com/us/app/instagram/id389801252",
        "https://play.google.com/store/apps/details?id=com.instagram.android"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
}
```

#### Search with AI analysis

```json
{
    "searchQuery": "meditation app",
    "store": "apple",
    "maxApps": 50,
    "enableAiAnalysis": true,
    "llmProvider": "openrouter",
    "openrouterApiKey": "sk-or-...",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
}
```

#### Reputation watch mode (weekly scheduled run against your app + competitors)

```json
{
    "appUrls": [
        "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128",
        "https://apps.apple.com/us/app/babbel-language-learning/id829587759",
        "https://apps.apple.com/us/app/busuu-language-learning/id379968583"
    ],
    "includeReviews": true,
    "maxReviews": 100,
    "reviewSort": "mostRecent",
    "trackReputation": true,
    "watchLabel": "language-learning-apps",
    "proxyConfiguration": {"useApifyProxy": true}
}
```

Run this on a weekly schedule. Every subsequent run surfaces `negative_rate_delta`, `new_topic_tags_emerged`, `sentiment_trend`, and a `reputation_change_digest` summary row — exactly the signal feed a PM needs when a competitor ships an update that tanks their reviews.

#### Search Google Play in Japan

```json
{
    "searchQuery": "ゲーム",
    "store": "google",
    "country": "jp",
    "maxApps": 25,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
}
```

### Pricing

This actor uses pay-per-event pricing — only pay for the results you receive:

| Event | Price | Notes |
|-------|-------|-------|
| App scraped | $0.003 per app | $3 per 1,000 apps |
| Review scraped | $0.0002 per review | Only billed when `includeReviews` is on. Apple feed supports up to 500 reviews/app; $0.10 for a full 500-review pull. |
| Reputation delta tracked | $0.005 per app | Only billed when `trackReputation` is on. Covers persistent-history read + delta computation + new-topic detection + digest row. |
| AI analysis completed | $0.05 per analysis | One flat charge per dataset, regardless of app count. |

#### Cost examples

- 50 apps, no reviews, no AI — **$0.15**
- 50 apps + 20 reviews each (1,000 reviews), no AI — **$0.35**
- 5 apps + 100 reviews each + reputation watch (weekly cadence) — **$0.14/run** (scrape + reviews + reputation)
- 100 apps + AI ASO analysis — **$0.35**
- 25 apps + 100 reviews each (2,500 reviews) + AI — **$0.625**

### Technical Details

- Apple App Store data comes from the official iTunes Search API — public, no authentication required, returns structured JSON
- Google Play data is extracted from server-rendered HTML pages using BeautifulSoup
- Proxy rotation on each request for resilience against rate limiting
- Polite crawling with 1-3 second delays between Google Play requests
- Exponential backoff with jitter on 403/429 responses
- URL parsing supports both `apps.apple.com/*/app/*/id{ID}` and `play.google.com/store/apps/details?id={ID}` formats
- Lightweight httpx-based actor — runs on 256 MB memory

### Output Example

```json
{
    "store": "apple",
    "app_id": "389801252",
    "bundle_id": "com.burbn.instagram",
    "name": "Instagram",
    "developer": "Instagram, Inc.",
    "app_url": "https://apps.apple.com/us/app/instagram/id389801252",
    "price": 0.0,
    "currency": "USD",
    "rating": 4.7,
    "rating_count": 28500000,
    "description": "Bringing you closer to the people and things you love...",
    "category": "Photo & Video",
    "content_rating": "12+",
    "version": "325.0.0",
    "release_date": "2010-10-06",
    "size_bytes": 256000000,
    "minimum_os_version": "16.0",
    "languages": ["EN", "AR", "DE", "ES", "FR", "JA", "KO", "ZH"],
    "icon_url": "https://is1-ssl.mzstatic.com/image/...",
    "screenshots": ["https://..."],
    "scraped_at": "2026-04-12T10:30:00Z"
}
```

### Use Cases

- **App Market Research**: Analyze competitor apps by category, ratings, and pricing across both stores
- **ASO (App Store Optimization)**: Study keyword trends, app titles, and descriptions for optimization strategies
- **Investment Due Diligence**: Research app ratings, review trends, and market positioning before investing
- **Competitive Intelligence**: Monitor competitor apps for version updates, rating changes, and new features
- **Lead Generation**: Build lists of app developers and companies for B2B outreach
- **Academic Research**: Collect app ecosystem data for studies on mobile app markets and technology adoption

### Integration & Export

Export data in multiple formats from Apify:

- **JSON** — structured data for APIs and analytics platforms
- **CSV** — import into Google Sheets, Excel, or databases
- **Excel** — ready for business reporting and presentations

Integrate with 2,000+ apps via Apify connectors: Google Sheets, Slack, Zapier, Make (Integromat), webhooks, and Amazon S3. Schedule recurring runs to monitor app store changes over time.

### Frequently Asked Questions

**Does this scrape both iOS and Android apps?**
Yes. Set the `store` parameter to `"both"` (default) to search both Apple App Store and Google Play simultaneously for any keyword. You can also set `"apple"` or `"google"` to target one store.

**Are the reviews included?**
Yes — set `includeReviews: true` and configure `maxReviews` (default 10, up to 500 on Apple). On Apple you can also choose `reviewSort: "mostHelpful"` for the most-upvoted reviews. Review extraction is billed separately at $0.0002/review so "apps only" runs stay cheap.

**What is per-review sentiment and how is it scored?**
When `includeReviews: true`, every review is automatically annotated with `sentiment_label` (positive/neutral/negative), `sentiment_score` (-1.0 to +1.0), 12 possible `topic_tags` (crash, bug, login, pricing, feature\_request, ux, performance, ads, privacy, update, praise, support), `urgency` (low/medium/high), and `is_churn_signal` (true/false). The sentiment score blends the star rating (70%) with a lexicon polarity signal (30%); urgency is rule-based (negative + crash/bug/pricing/login/privacy → high). This layer is deterministic and runs free — no LLM key required. Each app also gets a `review_sentiment_summary` rollup with counts, negative\_rate, urgency\_high\_count, churn\_signal\_count, and top topic tags, so you can pipe the dataset straight into a PM / churn dashboard.

**How does reputation-delta watch mode work?**
Set `trackReputation: true`. The first run stores a per-app sentiment snapshot (negative rate, urgency counts, churn counts, top topic tags) in a named Apify key-value store (`app-store-reputation-history`). Every subsequent run compares against prior snapshots and annotates each app with `negative_rate_delta`, `urgency_high_delta`, `churn_signal_delta`, `new_topic_tags_emerged`, `fading_topic_tags`, a `sentiment_trend` (improving / declining / stable vs. history mean), and a `change_kind` tag when a threshold is crossed (8 percentage-point negative-rate swing, or ≥2 urgency / churn count change). One `reputation_change_digest` summary row is also pushed per run listing the top movers. Use `watchLabel` to keep separate tracking campaigns in separate history slots. The feature auto-enables `includeReviews` since it needs review sentiment as input.

**Can I scrape apps from different countries?**
Yes. Set the `country` parameter to any ISO country code (us, gb, de, jp, kr, etc.) to get localized app listings and pricing.

**How accurate are the ratings?**
Apple App Store ratings come directly from the official iTunes API and are highly accurate. Google Play ratings are extracted from the HTML and may have minor rounding differences.

### Limitations

- Apple iTunes Search API returns a maximum of 200 results per query
- Google Play search results are limited by what's rendered in the initial HTML (typically 40-60 apps)
- Google Play HTML structure may change without notice, potentially affecting extraction accuracy
- Apple reviews: up to 500 per app via iTunes RSS pagination (10 pages × ~50); older reviews beyond that are not feed-exposed
- Google Play reviews: only the reviews rendered in the initial HTML are available (typically 40-80); the full review corpus loads via a client-side RPC and is not publicly scrapable without a full browser
- Google Play `size`, `version`, and `requires_android` moved behind a client-side `batchexecute` RPC in 2023; they return `"Not provided"` / empty on most pages today
- Per-review sentiment / topic / urgency is a deterministic heuristic layer (lexicon + regex) — great for dashboards and alerting, not a replacement for LLM-grade nuance
- Reputation watch mode needs at least two runs before deltas are meaningful; the first run stores history only (`change_kind: "new"`, `history_sample_count: 0`). Small review samples (<20 reviews) produce noisy negative\_rate deltas — bump `maxReviews` to 100+ for stable trend signals.
- Some app metadata (exact download numbers, revenue) is not publicly available
- Google Play may serve different content based on geographic IP location

### Legal and Compliance

This actor is provided as a technical tool. Users are responsible for ensuring their use complies with:

- [Apple Media Services Terms and Conditions](https://www.apple.com/legal/internet-services/itunes/) — including rules on automated access and redistribution of catalog data
- [Google Play Terms of Service](https://play.google.com/about/play-terms/)
- Applicable data protection laws (GDPR, CCPA, etc.)
- Local laws regarding web scraping and data collection

**Data sources used:**

- **Apple App Store** data is fetched via the public [iTunes Search API](https://performance-partners.apple.com/search-api) — a documented endpoint Apple provides for third-party use (notably by the Apple Search Partners program). Requires no authentication and returns structured JSON.
- **Google Play Store** data is extracted from publicly visible storefront HTML. No authenticated account, session cookie, or private endpoint is used.

This actor does not collect or return facial images, private user data, or any content behind a login. The actor implements polite crawling practices with delays between requests and respects rate-limit signals from the target stores.

For questions or concerns, please open an issue on the [GitHub repository](https://github.com/nickcuper/apify-actors).

The developer of this actor is not responsible for any misuse or legal consequences arising from its use.

# Actor input Schema

## `searchQuery` (type: `string`):

Search keyword(s) to find apps (e.g., 'AI chatbot', 'meditation', 'budget tracker').

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

CLI alias for 'searchQuery'. Hidden from Console form.

## `q` (type: `string`):

CLI alias for 'searchQuery'. Hidden from Console form.

## `search` (type: `string`):

CLI alias for 'searchQuery'. Hidden from Console form.

## `keyword` (type: `string`):

CLI alias for 'searchQuery'. Hidden from Console form.

## `searchTerm` (type: `string`):

CLI alias for 'searchQuery'. Hidden from Console form.

## `appUrls` (type: `array`):

Direct Apple App Store or Google Play URLs to scrape.

## `urls` (type: `array`):

CLI alias for 'appUrls'. Hidden from Console form.

## `links` (type: `array`):

CLI alias for 'appUrls'. Hidden from Console form.

## `store` (type: `string`):

Which app store to search. 'Both' scrapes Apple App Store and Google Play in parallel for broader coverage.

## `platform` (type: `string`):

Alternative name for App Store field. Accepts: 'both', 'apple'/'ios', 'google'/'android'. Use either store or platform.

## `platforms` (type: `array`):

Array alias for the App Store field. Accepts values like \['google\_play'], \['apple\_app\_store'], or both. Prefer the 'store' field.

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

Country code for regional results (e.g., 'us', 'gb', 'de', 'jp').

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

Maximum number of apps to scrape per store.

## `maxResults` (type: `integer`):

CLI alias for 'maxApps'. Hidden from Console form.

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

CLI alias for 'maxApps'. Hidden from Console form.

## `includeReviews` (type: `boolean`):

Scrape user reviews for each app. Use 'Max Reviews' to control volume per app (default 10, up to 500 on Apple).

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

How many reviews to pull per app. Apple iTunes RSS supports up to 500 (10 pages x 50). Google Play is limited by the initial HTML (typically 40-80).

## `reviewSort` (type: `string`):

How to sort reviews on Apple App Store. 'mostRecent' for fresh user feedback, 'mostHelpful' for the most-upvoted opinions. Google Play ignores this field.

## `trackReputation` (type: `boolean`):

Persist per-app sentiment history across runs in a named Apify key-value store. Each run computes negative-rate delta, urgency-high delta, churn-signal delta, newly-emerged topic tags, and a sentiment trend (improving/declining/stable). Emits a 'reputation\_change\_digest' summary row per run. Pair with a scheduled weekly run to turn the scraper into a reputation watchdog. Implies 'includeReviews=true'. Billed at $0.005 per app tracked.

## `watchLabel` (type: `string`):

Custom label used as the key-value-store slot for reputation history. Defaults to searchQuery or first app URL. Use distinct labels when tracking different keyword clusters so histories don't mix (e.g., 'duolingo-competitors', 'my-app').

## `enableAiAnalysis` (type: `boolean`):

Run AI-powered ASO and competitive analysis. Requires an LLM API key.

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

Which AI provider to use for analysis.

## `openrouterApiKey` (type: `string`):

API key for OpenRouter (openrouter.ai). Required if using OpenRouter provider.

## `anthropicApiKey` (type: `string`):

API key for Anthropic Claude (console.anthropic.com). Required if using Anthropic provider.

## `googleApiKey` (type: `string`):

API key for Google AI (Gemini). Get one at aistudio.google.com/app/apikey

## `openaiApiKey` (type: `string`):

API key from platform.openai.com (required if using OpenAI provider)

## `ollamaBaseUrl` (type: `string`):

Base URL for Ollama API. Default: http://localhost:11434

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

Specific model to use. Leave empty for the provider default (google/gemini-2.0-flash-001 for OpenRouter, claude-sonnet-4-20250514 for Anthropic, gemini-2.5-flash for Google AI, gpt-4o-mini for OpenAI, llama3.1 for Ollama).

## `proxyConfiguration` (type: `object`):

Proxy settings. Datacenter proxies usually work for app stores.

## Actor input object example

```json
{
  "searchQuery": "AI chatbot",
  "store": "both",
  "country": "us",
  "maxApps": 25,
  "includeReviews": false,
  "maxReviews": 10,
  "reviewSort": "mostRecent",
  "trackReputation": false,
  "enableAiAnalysis": false,
  "llmProvider": "openrouter",
  "ollamaBaseUrl": "http://localhost:11434",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "searchQuery": "AI chatbot",
    "ollamaBaseUrl": "http://localhost:11434",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("harvestlab/app-store-scraper").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 = {
    "searchQuery": "AI chatbot",
    "ollamaBaseUrl": "http://localhost:11434",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("harvestlab/app-store-scraper").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 '{
  "searchQuery": "AI chatbot",
  "ollamaBaseUrl": "http://localhost:11434",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call harvestlab/app-store-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "App Store Scraper - iOS & Google Play Apps & Reviews",
        "description": "Scrape Apple App Store and Google Play app listings with ratings, reviews, prices, downloads, and developer info. Search by keyword or app URL. AI-powered ASO analysis with competitor benchmarking and keyword opportunities. For mobile marketers, ASO teams, and app market researchers.",
        "version": "1.9",
        "x-build-id": "SzWQfkTR6c4AI8AKq"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/harvestlab~app-store-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-harvestlab-app-store-scraper",
                "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/harvestlab~app-store-scraper/runs": {
            "post": {
                "operationId": "runs-sync-harvestlab-app-store-scraper",
                "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/harvestlab~app-store-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-harvestlab-app-store-scraper",
                "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",
                "properties": {
                    "searchQuery": {
                        "title": "Search Query",
                        "type": "string",
                        "description": "Search keyword(s) to find apps (e.g., 'AI chatbot', 'meditation', 'budget tracker')."
                    },
                    "query": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for 'searchQuery'. Hidden from Console form."
                    },
                    "q": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for 'searchQuery'. Hidden from Console form."
                    },
                    "search": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for 'searchQuery'. Hidden from Console form."
                    },
                    "keyword": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for 'searchQuery'. Hidden from Console form."
                    },
                    "searchTerm": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for 'searchQuery'. Hidden from Console form."
                    },
                    "appUrls": {
                        "title": "App URLs",
                        "type": "array",
                        "description": "Direct Apple App Store or Google Play URLs to scrape.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "urls": {
                        "title": "App URLs (CLI alias)",
                        "type": "array",
                        "description": "CLI alias for 'appUrls'. Hidden from Console form.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "links": {
                        "title": "App URLs (CLI alias)",
                        "type": "array",
                        "description": "CLI alias for 'appUrls'. Hidden from Console form.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "store": {
                        "title": "App Store",
                        "enum": [
                            "both",
                            "apple",
                            "google"
                        ],
                        "type": "string",
                        "description": "Which app store to search. 'Both' scrapes Apple App Store and Google Play in parallel for broader coverage.",
                        "default": "both"
                    },
                    "platform": {
                        "title": "Platform (alias for App Store)",
                        "type": "string",
                        "description": "Alternative name for App Store field. Accepts: 'both', 'apple'/'ios', 'google'/'android'. Use either store or platform."
                    },
                    "platforms": {
                        "title": "Platforms (array alias for App Store)",
                        "type": "array",
                        "description": "Array alias for the App Store field. Accepts values like ['google_play'], ['apple_app_store'], or both. Prefer the 'store' field.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "country": {
                        "title": "Country",
                        "type": "string",
                        "description": "Country code for regional results (e.g., 'us', 'gb', 'de', 'jp').",
                        "default": "us"
                    },
                    "maxApps": {
                        "title": "Max Apps",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "Maximum number of apps to scrape per store.",
                        "default": 25
                    },
                    "maxResults": {
                        "title": "Max Apps (CLI alias)",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "CLI alias for 'maxApps'. Hidden from Console form."
                    },
                    "maxItems": {
                        "title": "Max Items (CLI alias)",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "CLI alias for 'maxApps'. Hidden from Console form."
                    },
                    "includeReviews": {
                        "title": "Include Reviews",
                        "type": "boolean",
                        "description": "Scrape user reviews for each app. Use 'Max Reviews' to control volume per app (default 10, up to 500 on Apple).",
                        "default": false
                    },
                    "maxReviews": {
                        "title": "Max Reviews Per App",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "How many reviews to pull per app. Apple iTunes RSS supports up to 500 (10 pages x 50). Google Play is limited by the initial HTML (typically 40-80).",
                        "default": 10
                    },
                    "reviewSort": {
                        "title": "Review Sort Order (Apple only)",
                        "enum": [
                            "mostRecent",
                            "mostHelpful"
                        ],
                        "type": "string",
                        "description": "How to sort reviews on Apple App Store. 'mostRecent' for fresh user feedback, 'mostHelpful' for the most-upvoted opinions. Google Play ignores this field.",
                        "default": "mostRecent"
                    },
                    "trackReputation": {
                        "title": "Track Reputation Deltas (watch mode)",
                        "type": "boolean",
                        "description": "Persist per-app sentiment history across runs in a named Apify key-value store. Each run computes negative-rate delta, urgency-high delta, churn-signal delta, newly-emerged topic tags, and a sentiment trend (improving/declining/stable). Emits a 'reputation_change_digest' summary row per run. Pair with a scheduled weekly run to turn the scraper into a reputation watchdog. Implies 'includeReviews=true'. Billed at $0.005 per app tracked.",
                        "default": false
                    },
                    "watchLabel": {
                        "title": "Watch Label",
                        "type": "string",
                        "description": "Custom label used as the key-value-store slot for reputation history. Defaults to searchQuery or first app URL. Use distinct labels when tracking different keyword clusters so histories don't mix (e.g., 'duolingo-competitors', 'my-app')."
                    },
                    "enableAiAnalysis": {
                        "title": "Enable AI Analysis",
                        "type": "boolean",
                        "description": "Run AI-powered ASO and competitive analysis. Requires an LLM API key.",
                        "default": false
                    },
                    "llmProvider": {
                        "title": "LLM Provider",
                        "enum": [
                            "openrouter",
                            "anthropic",
                            "google",
                            "openai",
                            "ollama"
                        ],
                        "type": "string",
                        "description": "Which AI provider to use for analysis.",
                        "default": "openrouter"
                    },
                    "openrouterApiKey": {
                        "title": "OpenRouter API Key",
                        "type": "string",
                        "description": "API key for OpenRouter (openrouter.ai). Required if using OpenRouter provider."
                    },
                    "anthropicApiKey": {
                        "title": "Anthropic API Key",
                        "type": "string",
                        "description": "API key for Anthropic Claude (console.anthropic.com). Required if using Anthropic provider."
                    },
                    "googleApiKey": {
                        "title": "Google AI API Key",
                        "type": "string",
                        "description": "API key for Google AI (Gemini). Get one at aistudio.google.com/app/apikey"
                    },
                    "openaiApiKey": {
                        "title": "OpenAI API Key",
                        "type": "string",
                        "description": "API key from platform.openai.com (required if using OpenAI provider)"
                    },
                    "ollamaBaseUrl": {
                        "title": "Ollama Base URL",
                        "type": "string",
                        "description": "Base URL for Ollama API. Default: http://localhost:11434"
                    },
                    "llmModel": {
                        "title": "LLM Model",
                        "type": "string",
                        "description": "Specific model to use. Leave empty for the provider default (google/gemini-2.0-flash-001 for OpenRouter, claude-sonnet-4-20250514 for Anthropic, gemini-2.5-flash for Google AI, gpt-4o-mini for OpenAI, llama3.1 for Ollama)."
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Proxy settings. Datacenter proxies usually work for app stores."
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
