# App Store & Google Play Reviews API (`insight.solutions/app-reviews-api`) Actor

Scrape App Store and Google Play reviews as data. One row per review: rating, text, author, app version, review date, developer reply. Plus one app row per app with rating, rating histogram, installs, price and category. Any country, no API key, no login.

- **URL**: https://apify.com/insight.solutions/app-reviews-api.md
- **Developed by:** [Insight Solutions](https://apify.com/insight.solutions) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.12 / 1,000 review returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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 & Google Play Reviews API

**Get any app's reviews from both stores as one flat table.** Give this Actor a list of App Store links, Google Play links, Apple app IDs or Android package names — mixed together, in any order — and get back one row per review: the star rating, the text as written, who wrote it, which app version they were running, when they posted, and the developer's public reply. Each app also gets one row of its own store listing: rating, the per-star histogram, install band, price, category and dates.

No API key. No login. No cookies to paste. No App Store Connect or Play Console access. **$0.20 per 1,000 reviews, proxy included**, apps that could not be read are free, and a run that returns nothing costs nothing at all.

### Try it in 30 seconds

```json
{
  "apps": [
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "com.spotify.music"
  ],
  "country": "us",
  "maxReviewsPerApp": 50,
  "sort": "newest"
}
```

The same app on both stores, one App Store URL and one bare Android package name. Apple app IDs (`324684580`) and full Google Play URLs work too.

### What comes back

One `review` row per review:

```jsonc
{
  "ok": true,
  "rowType": "review",               // "review" | "app" | "diagnostic"

  "store": "google",                 // "apple" | "google"
  "appId": "com.spotify.music",
  "appUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&hl=en&gl=US",
  "appTitle": "Spotify: Music and Podcasts",

  "reviewId": "f7f4c69a-b9d1-4da0-b881-7ce3acd0fc94",
  "rating": 1,                       // 1–5 stars
  "title": null,                     // the review's headline — App Store only
  "text": "worst experience, too many ads",
  "author": "pratiksha",
  "version": "9.1.80.2221",          // the app version the reviewer was running
  "reviewedAt": "2026-09-08T14:19:18.000Z",
  "thumbsUp": 0,                     // readers who marked it helpful

  "developerReply": "Hi. Thanks for sharing this. We hear you and we'll pass your feedback along to the Ads team.",
  "developerRepliedAt": "2026-09-08T16:17:54.000Z",

  "country": "us",
  "language": "en",
  "position": 1,                     // 1-based, in the order the store served it
  "sort": "newest",

  "error": null,
  "errorType": null,
  "scrapedAt": "2026-09-09T05:53:05.487Z",
  "source": "play.google.com",
  "sourceUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&hl=en&gl=US"
}
```

And one `app` row per app, before its reviews, on the same columns:

```jsonc
{
  "ok": true,
  "rowType": "app",
  "store": "google",
  "appId": "com.spotify.music",
  "title": "Spotify: Music and Podcasts",
  "developer": "Spotify AB",
  "description": "With the Spotify music and podcast app, you can play millions of songs…",
  "category": "Music & Audio",
  "price": 0,
  "currency": "USD",
  "isFree": true,
  "rating": 4.345289707183838,
  "ratingCount": 36268768,
  "ratingHistogram": { "1": 3705674, "2": 1066445, "3": 1316488, "4": 3090485, "5": 27089653 },
  "installsText": "1,000,000,000+",
  "version": null,                   // Google no longer publishes one for many apps
  "releaseDate": "2014-05-27T13:12:17.000Z",
  "updatedAt": "2026-09-08T09:26:27.000Z",
  "contentRating": "Teen",
  "iconUrl": "https://play-lh.googleusercontent.com/IzQgYCcnCF…",
  "screenshots": ["https://play-lh.googleusercontent.com/c1jnjL-9CI…"],
  "minimumOs": null,
  "sizeBytes": null                  // App Store rows carry both of these
}
```

Every row — review, app and diagnostic — carries the same keys, so the dataset exports as one rectangular CSV whatever mix of stores and row types a run produced.

### Use cases

- **Release monitoring.** `sort: "newest"` after a ship, filtered on `version`, tells you within the hour whether the 1-stars are about the thing you just changed.
- **Competitive research.** The same query against a competitor's package name, in the same country, on the same day. `ratingHistogram` is the shape of their reputation; the 2- and 3-star reviews are where the real complaints live.
- **Voice-of-customer and sentiment analysis.** One flat array of text with a star rating attached — a labelled dataset, ready to hand to a model, without the labelling.
- **Support triage and bug discovery.** App store reviews are a bug tracker nobody filed. Filter `rating <= 2` and read the last week.
- **Localisation and market research.** Run the same app across `country` codes: the German storefront's rating, its reviews and its complaints are a different dataset from the US one, not a translation of it.
- **Developer-response auditing.** `developerReply` and `developerRepliedAt` (Google Play) show which reviews got answered, how fast, and whether the answer was a template.
- **ASO reporting.** `rating`, `ratingCount`, `installsText` and `updatedAt` tracked over time, per country, from a scheduled run.

### How it works, and why it keeps working

Neither store has a public reviews API. Apple's App Store Connect API only shows you your *own* apps; Google's Play Developer API is the same. This Actor reads the two public surfaces the stores serve to anyone.

| Store | What it reads | Shape |
|---|---|---|
| **App Store** — details | `itunes.apple.com/lookup?id=…` | JSON, one result object. Accepts a bundle ID too |
| **App Store** — reviews | The customer-reviews RSS feed, `/rss/customerreviews/page=N/id=…/sortby=…/json` | JSON, fifty reviews a page, ten pages maximum |
| **Google Play** — details | The public app page | 1.2 MB of HTML with a JSON-LD block and a set of `AF_initDataCallback` data blobs in it |
| **Google Play** — reviews | `POST /_/PlayStoreUi/data/batchexecute`, RPC `UsvDTd` | The Play web client's own reviews call. Token-paginated |

Both stores have a quirk that catches naive scrapers, and both are handled here:

**Apple answers with `content-type: text/javascript`.** The body is JSON. A client that trusts the header sees a script and gives up.

**Google wraps its answer twice.** The response opens with `)]}'` — deliberately invalid JavaScript, so the body cannot be loaded with a `<script>` tag — then an array whose payload row carries the actual reviews as a *string* of JSON inside it. Two decodes, not one. The details page needs a bracket-counting scanner rather than a regex, because the data blobs are megabyte-scale nested arrays full of braces and apostrophes inside strings.

Underneath: **Apify proxy**, one pinned session per parallel worker. When a store refuses an exit IP — HTTP 429, HTTP 403, an empty body, a body that will not decode, or a consent wall in place of the page — that session is retired and **the same page is asked for once more from a different address**. Retrying on an address that was just refused only deepens the block, so it is never done. If the second address is refused too, the walk stops, **keeps every review it already delivered**, and files one free `blocked` row saying where it stopped.

Pages of the same app are spaced 250–600 ms apart. Nothing forces that; it is the difference between reading a store listing and hammering one.

### How it compares

- **Both stores, one schema.** The same columns for an Apple review and a Google one, so a comparison is a `GROUP BY store` rather than a reconciliation project. Where a store genuinely does not publish a field — Apple has no developer replies, Google has no review headline — the column is null rather than faked.
- **The app's own numbers come with the reviews.** `rating`, `ratingCount`, `ratingHistogram` and `installsText` in the same run, so a sample of 100 reviews can be read against the population it came from.
- **Honest about the App Store's ceiling.** Apple's review feed stops at 500 reviews per country per sort order and there is no way past it. This Actor says so in the input description rather than quietly returning 500 and letting you assume that was all of them.
- **Failures are free and legible.** A missing app, an app with no reviews in that storefront, or a block produce a diagnostic row with an `errorType` you can branch on — and no charge. A run that returns nothing at all finishes **FAILED** with the reason in its status message, never a green run containing an apology.
- **A partial walk is kept, not thrown away.** Hit `maxRunSecs` or your charge ceiling on page 12 and you keep pages 1–11.
- **No login, ever.** No Apple ID, no Google account, no cookie jar, no session token. Public endpoints only, which is also why it runs under Apify's **limited permissions**.

### Input reference

| Field | Type | Default | What it does |
|---|---|---|---|
| `apps` **(required)** | array of strings | prefilled with two | App Store URLs, Google Play URLs, Apple numeric IDs, or Android package names. A dotted name is read as Android; `apple:com.spotify.client` forces an iOS bundle lookup. Duplicates are read, and billed, once |
| `country` | string | `us` | Two-letter storefront code. Both stores are per-country: `us` and `de` reviews of the same app are different sets in different languages |
| `language` | string | `en` | The language Google Play answers in — app title, description, category, install text. Apple's review feed has no language parameter |
| `maxReviewsPerApp` | integer | `100` | Reviews per app. `0` = as many as the store will serve, which is tens of thousands on Google Play and **500** on the App Store |
| `sort` | `newest` | `helpful` | `newest` | Reverse-chronological, or each store's own engagement ranking. The two return substantially different reviews |
| `ratingFilter` | integer 0–5 | `0` | Only reviews with this many stars. **Google Play only**; Apple ignores it. `0` = every rating |
| `includeAppDetails` | boolean | `true` | Add one `app` row per app. One extra request, charged as its own event |
| `maxConcurrency` | integer | `3` | Apps in parallel. Each worker keeps its own proxy session. Pages within one app cannot be parallelised |
| `maxRunSecs` | integer | `240` | Whole-run wall-clock budget. When it runs out the Actor keeps what it has and files a free diagnostic row for each app it never reached |
| `proxyConfiguration` | object | Apify residential | Google Play refuses many datacenter ranges. Scraping only the App Store? Switch to `{ "useApifyProxy": true }` and pay much less in proxy traffic |

### Output reference

Every row carries the same keys. `ok: true` is a review or an app row; `ok: false` is a free diagnostic row.

| Field | What it is |
|---|---|
| `rowType` | `review`, `app` or `diagnostic` |
| `store`, `appId`, `appUrl`, `input`, `appTitle` | Which store, the join key, the public listing, the entry you supplied, and the app's name — on every row |
| `title` | The app's name on an `app` row; the review's headline on a `review` row (App Store only) |
| `developer`, `developerId` | App rows: the publisher |
| `description`, `category`, `contentRating` | App rows: the listing text, primary category and age rating, in the requested language |
| `price`, `currency`, `isFree` | App rows: the download price in the storefront's currency |
| `rating` | The app's average on an `app` row; the review's 1–5 stars on a `review` row |
| `ratingCount`, `ratingHistogram` | App rows: how many ratings, and the per-star breakdown (Google Play only) |
| `installsText` | App rows: Google's install band, e.g. `1,000,000,000+`. Apple publishes none |
| `version` | The current version on an `app` row; the version reviewed on a `review` row |
| `releaseDate`, `updatedAt` | App rows: first published, and when the current version shipped |
| `iconUrl`, `screenshots`, `minimumOs`, `sizeBytes` | App rows: the listing's assets and requirements |
| `reviewId`, `author`, `text`, `reviewedAt`, `thumbsUp` | The review itself. `reviewId` is stable, so it is what you deduplicate on between runs |
| `developerReply`, `developerRepliedAt` | The publisher's public answer. Google Play only |
| `country`, `language`, `position`, `sort` | Which storefront, which order, and where this row fell in it |
| `ok`, `error`, `errorType` | Whether this row is data, and if not, why not |
| `scrapedAt`, `source`, `sourceUrl` | When, and from where |

`errorType` on a diagnostic row is one of:

| Value | Meaning | Charged? |
|---|---|---|
| `not-found` | No app with that identifier in that storefront | No |
| `no-reviews` | The app exists and the store returned no reviews for it there | No |
| `blocked` | The store refused our requests from two different proxy exits. Reviews already returned for that app are kept | No |
| `invalid-input` | The entry was not a store link or an app identifier | No |
| `timeout` | The run's `maxRunSecs` budget ran out before this app was reached | No |
| `unavailable` | Something else went wrong; the detail is in `error` | No |

### Pricing

**$0.20 per 1,000 reviews.** Pay-per-event, with proxy already inside that number — there is no separate proxy line on your bill for this Actor.

| Event | What triggers it | FREE | Starter | Scale | Business |
|---|---|---|---|---|---|
| **Review returned** *(primary)* | One review row written to your dataset | $0.0002 | $0.0002 | $0.00016 | $0.00012 |
| App details returned | One `app` row written to your dataset | $0.002 | $0.002 | $0.002 | $0.002 |
| Run started | Once per run, after the first paid row | $0.001 | $0.001 | $0.001 | $0.001 |

**Worked example.** 10 apps on both stores (20 entries), 100 reviews each, app details on, one package name misspelled:

- 19 apps × 100 reviews × $0.0002 = **$0.38**
- 19 app rows × $0.002 = **$0.038**
- 1 run start = **$0.001**
- 1 misspelled package = **$0.00** (free `not-found` row)
- **Total: $0.419**

**Daily monitoring example.** 5 apps, 50 newest reviews each, `includeAppDetails: false`: 250 × $0.0002 + $0.001 = **$0.051 per run**, about **$1.53 a month** on a daily schedule.

What you are never charged for: an app that does not exist, an app with no reviews in that storefront, an entry that was not an app, an app the run never reached before `maxRunSecs`, or a page the store blocked. If a whole run comes back empty it finishes FAILED and bills **nothing at all**, start fee included.

Set `ACTOR_MAX_TOTAL_CHARGE_USD` on a run and the Actor stops fetching once the ceiling is in sight, rather than handing you rows it cannot bill or billing you for rows it cannot hand over. It finishes SUCCEEDED with the ceiling named in its status message, and everything already delivered is yours.

### Limits, and the ones that might bite

**The App Store caps at 500 reviews per app, per country, per sort order.** Ten pages of fifty, and page eleven answers HTTP 400 with "CustomerReviews RSS page depth is limited to 10". Nothing gets past it — not this Actor, not any other tool, because the limit is in Apple's feed. If you need more App Store reviews of one app, run it again against other `country` codes; each storefront is its own set of 500. Google Play has no comparable limit and will page through tens of thousands.

**Reviews are per-country, and so are ratings.** The same app had a 4.35 average on the US Play storefront and 4.18 on the German one in our captures, from the same global rating count. A single-country run is a single-country answer.

**The App Store publishes no developer replies.** Replies exist and are visible on the web page; the RSS feed does not carry them. `developerReply` is therefore always null for Apple rows. Google Play publishes them and they come through.

**Google Play publishes no version for many large apps**, and no download size on the web listing at all. `version` and `sizeBytes` are null for those, which is what the page says. The App Store publishes both for everything.

**`thumbsUp` is near-zero under `newest`.** A review posted an hour ago has had no time to collect helpful votes. Use `sort: "helpful"` when that column is what you are after — in our captures the top most-relevant review had 17,330 votes and the busiest of the last hour's had 99.

**`ratingFilter` is Google Play only**, and worth verifying against your own app before you rely on it: it is a filter slot in an undocumented RPC, not a supported parameter.

**Reviews you can only see signed in** — flagged, removed, or held for moderation — are not here. This Actor does not log in, does not accept cookies and does not take a session token, and it never will; that is a deliberate line, not a missing feature.

**The upstream format may change.** Both stores are read through surfaces they change without notice — that is true of every tool that reads app store reviews, including the ones that do not say so. When a shape changes, rows stop arriving and you get free `blocked` or `not-found` diagnostic rows rather than quietly wrong data, and a run that returns nothing bills nothing.

**Rate and reliability.** Requests go out through proxy sessions pinned per worker, one rotation per block, and a 250–600 ms pause between pages of the same app. Three apps in parallel is the default because it is where throughput and block rate balance; raising `maxConcurrency` speeds a long list up and makes blocks more likely.

### Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with **limited permissions**, uses **pay-per-event** pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The **Integrations** tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

```bash
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~app-reviews-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"apps":["com.spotify.music"],"country":"us","maxReviewsPerApp":50,"sort":"newest"}'
```

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/app-reviews-api").call(run_input={
    "apps": [
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
        "com.spotify.music",
    ],
    "country": "us",
    "maxReviewsPerApp": 200,
    "sort": "newest",
    "includeAppDetails": True,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if not row.get("ok"):
        print("skipped:", row["input"], row["errorType"])
    elif row["rowType"] == "app":
        print(f'{row["store"]:6} {row["title"]}: {row["rating"]} from {row["ratingCount"]} ratings')
    else:
        print(f'  {row["rating"]}★ {row["author"]}: {row["text"][:80]}')
```

Set `includeAppDetails: false` for a pure review feed — the payload shrinks and the app-details event is not billed.

### FAQ

**Do I need an Apple or Google developer account?**
No. Both stores' review data here is public. App Store Connect and the Play Developer API only show you apps you own; this shows you any app, including your competitors'.

**Can I get more than 500 App Store reviews for one app?**
Not from one country. Apple's feed stops at ten pages of fifty, per country per sort order. Run the same app against `us`, `gb`, `ca`, `au` and so on and you get 500 from each. Google Play has no such limit.

**Are Apple and Google reviews the same people?**
No — they are entirely separate populations writing about separate builds, and the ratings usually differ. The `store` column is there so you never accidentally average them together.

**What does `position` mean?**
Where the review fell in the order the store served it, 1-based, per app. With `sort: "newest"` position 1 is the most recent review at the moment of the run.

**How do I deduplicate between runs?**
On `reviewId`. Both stores' IDs are stable — Apple's is a numeric string, Google's a UUID — so an incremental pipeline is an upsert keyed on `(store, appId, reviewId)`.

**Why is `developerReply` always empty for my iOS app?**
Because Apple's review feed does not carry replies, even though they show on the web page. It is a limitation of the source, not of this Actor.

**Can I filter by date?**
Not at the source — neither store takes a date parameter. Ask for `sort: "newest"` and stop reading when `reviewedAt` passes your cutoff, which for a daily monitoring run means a small `maxReviewsPerApp`.

**Does the language input change which reviews come back?**
On Google Play it changes the app text, not the review set — reviews follow the `country` storefront. On the App Store the language input does nothing at all: the feed answers in whatever the storefront speaks.

**What happens if one app fails?**
The others still run. The failed one produces a free diagnostic row and the run finishes `SUCCEEDED`. If *every* app fails, the run finishes `FAILED` and you are billed nothing at all.

**Is the data fresh?**
Live. Every run reads both stores at that moment; nothing is cached.

### Legal and data-protection notes

- **Public listings only.** Every source is a public store page or its public feed. The Actor never logs in, never accepts cookies or session tokens, never takes an API key belonging to anyone else, and never touches a developer console.
- **Reviews are personal data in most jurisdictions.** A review carries a display name and, on Google Play, an account identifier, and under the GDPR and similar laws that is personal data about an identifiable person. You are the controller of whatever you collect: have a lawful basis, keep only what you need, honour deletion requests, and remember that a review deleted on the store stays in your dataset until you remove it.
- **Reviews are their authors' words.** Republishing them, or training on them, is your call and your responsibility, subject to each store's terms and to the law where you operate. Aggregation, sentiment analysis and quotation are the ordinary uses and are what this is built for.
- **Not affiliated with Apple Inc., Google LLC, or with any developer or reviewer whose content you retrieve.** All product names and trademarks belong to their respective owners and are used only to describe which public endpoints this Actor reads.

### Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

**Video, audio & social**

- [YouTube Transcript API](https://apify.com/insight.solutions/youtube-transcript-api) — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- [YouTube Comments API](https://apify.com/insight.solutions/youtube-comments-api) — comments and replies with likes, pinned and hearted flags, newest or top sort.
- [YouTube Channel API](https://apify.com/insight.solutions/youtube-channel-api) — a channel's videos, Shorts and live streams, plus YouTube search.
- [Podcast Search, Episodes & Charts API](https://apify.com/insight.solutions/podcast-api) — Apple Podcasts search, charts and full episode feeds.
- [Bluesky Scraper](https://apify.com/insight.solutions/bluesky-scraper) — profiles, posts, followers and follows from the public AT Protocol API.
- [Telegram Channel Scraper](https://apify.com/insight.solutions/telegram-channel-scraper) — posts, views and channel stats from public Telegram channels.
- [Substack Scraper](https://apify.com/insight.solutions/substack-scraper) — posts with full free text, comments and publication profiles.

**News, documents & the web**

- [Google News Search, Topics & Real Article URLs](https://apify.com/insight.solutions/google-news-api) — news search and topic feeds with the publisher's real URL decoded.
- [Website to Markdown — Content Extractor for LLMs & RAG](https://apify.com/insight.solutions/website-content-extractor) — any site as clean Markdown, text and heading-aware chunks.
- [Internet Archive API](https://apify.com/insight.solutions/internet-archive-api) — archive.org search, item metadata, files and reviews.
- [Wayback Machine Toolkit](https://apify.com/insight.solutions/wayback-toolkit) — archived URL inventories, snapshots and text diffs between dates.
- [Website Technology Detector](https://apify.com/insight.solutions/website-tech-detector) — the tech stack behind any site, with the evidence for each detection.
- [Domain Intelligence API](https://apify.com/insight.solutions/domain-intelligence-api) — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- [SEO Page Audit](https://apify.com/insight.solutions/seo-page-audit) — sitemap crawl with on-page checks, structured data and broken-link reports.
- [Keyword Suggestions API](https://apify.com/insight.solutions/keyword-suggestions-api) — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- [Website Contact Extractor](https://apify.com/insight.solutions/website-contact-extractor) — emails, phone numbers and social profiles from any list of websites.

**Business, finance & jobs**

- [Congress & Insider Trades API](https://apify.com/insight.solutions/congress-insider-trades-api) — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- [SEC EDGAR API](https://apify.com/insight.solutions/sec-edgar-api) — filings, XBRL financials and full-text search by ticker or CIK.
- [Y Combinator Companies, Batches & Founders](https://apify.com/insight.solutions/yc-companies-directory) — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- [Career Site Jobs API](https://apify.com/insight.solutions/ats-jobs-api) — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- [New Job Postings Monitor](https://apify.com/insight.solutions/job-postings-monitor) — new, closed and changed postings on the career sites you watch.
- [Shopify Products API](https://apify.com/insight.solutions/shopify-products-api) — any Shopify store's catalogue, variants, prices and stock signals.

**Apps & games**

- [App Store Top Charts & App Search API](https://apify.com/insight.solutions/app-charts-api) — Apple top charts by country and genre, plus app search and details.
- [Steam Reviews API](https://apify.com/insight.solutions/steam-reviews-api) — Steam reviews with playtime, helpfulness and game details.
- [Steam Game Data API](https://apify.com/insight.solutions/steam-store-stats-api) — prices, tags, review scores, live player counts and top charts.

# Actor input Schema

## `apps` (type: `array`):

One entry per app, from either store. An App Store link (`https://apps.apple.com/us/app/…/id324684580`), a bare Apple app ID (`324684580`), a Google Play link (`https://play.google.com/store/apps/details?id=com.spotify.music`) or a bare Android package name (`com.spotify.music`) all work. A dotted name is read as an Android package; to look up an **iOS bundle ID** instead, prefix it: `apple:com.spotify.client`. Duplicates are read, and billed, once.

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

Two-letter ISO country code, lower case. Both stores are per-country: `us` reviews and `de` reviews of the same app are different sets of reviews, written in different languages, and the App Store's average rating differs between them too. This is the single most consequential input after the app list — a run against `us` will never show you what German users are saying.

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

The language Google Play should answer in — it changes the app title, description, category name and install text, but not which reviews come back. The App Store's review feed has no language parameter at all: it answers in whatever the storefront speaks. Use a plain code (`en`, `de`) or a regional one (`en-GB`, `pt-BR`).

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

How many reviews to return for each app. Set 0 for as many as the store will serve — and note the ceilings are very different: **Google Play** will page through tens of thousands, while **the App Store RSS feed stops at 500 per country per sort order** (ten pages of fifty) and nothing can get past that. To read more of an App Store app, run it again against another `country`.

## `sort` (type: `string`):

`newest` is reverse-chronological and is what you want for monitoring. `helpful` is each store's own engagement ranking — the App Store's "most helpful", Google Play's "most relevant" — which surfaces the long, heavily-voted reviews people actually read on the listing. They return genuinely different sets: in our captures the two orders for the same app shared no reviews at all.

## `ratingFilter` (type: `integer`):

Return only reviews with this many stars, 1 to 5. **Google Play only** — the App Store's review feed has no rating filter, and Apple apps ignore this. Set 0 for every rating, which is the default and the only setting that gives you a representative sample.

## `includeAppDetails` (type: `boolean`):

Add one `app` row per app, before its reviews: title, developer, rating and rating count, the per-star histogram and install band (Google), price, category, content rating, version and dates. It is one extra request per app and it is charged separately from reviews. Turning it off also removes the Actor's only way to tell a missing app from an app with no reviews, so those come back as `no-reviews` instead of `not-found`.

## `maxConcurrency` (type: `integer`):

How many apps are read at once. Each parallel worker keeps its own proxy session, so an app that gets blocked burns only its own exit IP. Pages *within* one app cannot be parallelised — each page hands over the pointer to the next — so this is the only lever on speed.

## `maxRunSecs` (type: `integer`):

Wall-clock budget for the whole run. When it is reached the Actor stops fetching, keeps and bills for every row it already wrote, and files a free diagnostic row for each app it never reached. An app stopped part-way keeps its rows and is not marked as failed. Nothing is charged for work that did not happen.

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

Google Play refuses a lot of datacenter address ranges, so the default asks for Apify's residential proxy and the proxy cost is already inside the per-review price. Apple's endpoints are happy with datacenter addresses — if you are only scraping the App Store you can switch to `{ "useApifyProxy": true }` and pay a good deal less in proxy traffic. Sessions rotate automatically when an exit IP is refused.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "com.spotify.music",
    "324684580",
    "apple:com.spotify.client"
  ],
  "country": "us",
  "language": "en",
  "maxReviewsPerApp": 50,
  "sort": "newest",
  "ratingFilter": 0,
  "includeAppDetails": true,
  "maxConcurrency": 2,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per review — rating, text, author, version, date and developer reply — plus one row per app with its store listing. Apps that could not be read get a free diagnostic row saying why. Delivered as JSON items in the default dataset.

# 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 = {
    "apps": [
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
        "com.spotify.music"
    ],
    "country": "us",
    "language": "en",
    "maxReviewsPerApp": 50,
    "sort": "newest",
    "ratingFilter": 0,
    "includeAppDetails": true,
    "maxConcurrency": 2,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("insight.solutions/app-reviews-api").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 = {
    "apps": [
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
        "com.spotify.music",
    ],
    "country": "us",
    "language": "en",
    "maxReviewsPerApp": 50,
    "sort": "newest",
    "ratingFilter": 0,
    "includeAppDetails": True,
    "maxConcurrency": 2,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("insight.solutions/app-reviews-api").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 '{
  "apps": [
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "com.spotify.music"
  ],
  "country": "us",
  "language": "en",
  "maxReviewsPerApp": 50,
  "sort": "newest",
  "ratingFilter": 0,
  "includeAppDetails": true,
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call insight.solutions/app-reviews-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,insight.solutions/app-reviews-api"
        }
    }
}

```

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/fjgKZBGtqRGmUVKoc/builds/eMDMnzj411GnSkvRn/openapi.json
