# Google Play App Ratings Scraper (`elidev/google-play-ratings`) Actor

Extract one Google Play app's public aggregate rating and rating count as timestamped JSON. Enter an app URL and page language; no EliDev key or Google login. Native, bounded runs with strict URL validation. Individual reviews are not included. Pay per delivered snapshot, plus a small start fee.

- **URL**: https://apify.com/elidev/google-play-ratings.md
- **Developed by:** [EliDev](https://apify.com/elidev) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 0 monthly users, 98.9% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.00 / 1,000 app rating results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Play App Ratings Scraper - EliDev

Get an Android app’s **public aggregate rating and rating count** as a timestamped JSON snapshot. Enter one official Google Play app URL and a page language. This Actor runs natively on Apify: **no EliDev API key, Google login, browser or proxy required**.

**Aggregate ratings only.** Individual written reviews, reviewer profiles, downloads, rankings, app binaries and catalog search are outside this product.

### What you receive

| Field | Meaning |
|---|---|
| source / kind / status | google_play / ratings / completed for a valid snapshot |
| url / app_id | Validated source URL and Android package identifier |
| language | Requested page language; not a guaranteed country-specific rating |
| data.rating | Observed finite aggregate rating from 1 to 5 |
| data.rating_count | Public aggregate count, or null if unavailable; not written-review count |
| observed_at | UTC observation timestamp |

A missing rating fails extraction instead of fabricating zero. Rating count may be null. Values can change and need not match every country, device or Google Play account.

Use it to record app-quality indicators, populate a small dashboard, or compare dated snapshots your application retains. One app per run; no batch processing or continuous monitoring is bundled.

### Quick start

1. Open Input and keep the prefilled WhatsApp example, or supply an official app-details URL you are entitled to access.
2. Choose en, fr, de, es, it, pt or nl. Example listings: https://play.google.com/store/apps/details?id=com.whatsapp and https://play.google.com/store/apps/details?id=com.google.android.youtube (the app listing, not YouTube channel statistics).
3. Click **Run** with the default settings (256 MB RAM, 120 seconds). The Actor's configured default spending ceiling is **$0.02**. Console may supply its own higher platform default; this is an allowance, not the amount charged. One ordinary success produces one $0.003 result plus one $0.00005 start event. You can choose a lower sufficient ceiling in Apify's run options.
4. After **SUCCEEDED**, open **Output → App Ratings** to see App ID, App Rating (1–5) and Number of Ratings first, followed by language, URL, observation time, source, kind and status. The separate OUTPUT key-value record contains the business/billing summary. An old OUTPUT alone does not prove the current status of a resurrected run.
5. Export JSON/CSV within your retention period. Keep the run ID when investigating errors; do not blindly start another chargeable run.

### Input

```json
{"url":"https://play.google.com/store/apps/details?id=com.whatsapp","language":"en"}
```

Both fields have defaults. HTTPS, exact hostname play.google.com and path /store/apps/details are required, with one valid id parameter. Optional hl must match language. Explicit ports, credentials, fragments, tracking/unknown/duplicate parameters, other hosts, lists and search pages are rejected. Unknown INPUT fields are refused. Provider and kind are fixed; there is no apiKey, userId, runId, review limit, dates or arbitrary destination.

Never put a secret in INPUT, even in an unknown field: Apify can store INPUT before validation.

### Observed Dataset example

The **App Ratings** view flattens the nested data object for display only: its columns use data.rating and data.rating_count. Raw Dataset JSON keeps the historical nested data object. Use Storage → Dataset → Preview or the authenticated `GET /v2/datasets/{datasetId}/items` endpoint without a view parameter for raw JSON. This is one stored item with two ways to read it, not two billed results. Display schemas are attached to each run's storage; a new release does not necessarily repair older runs' saved views.

This is a real private-cloud snapshot from 30 October 2026, **not a live promise** for WhatsApp:

```json
{
  "source": "google_play",
  "kind": "ratings",
  "url": "https://play.google.com/store/apps/details?id=com.whatsapp",
  "app_id": "com.whatsapp",
  "language": "en",
  "status": "completed",
  "data": {"rating": 4.6240997314453125, "rating_count": 246712920},
  "observed_at": "2026-10-30T20:17:06.765308+00:00"
}
```

OUTPUT is a separate summary. Example successful summary (budget-dependent values can differ):

```json
{"status":"completed","source":"google_play","kind":"ratings","item_count":1,"billing":{"mode":"ppe-dataset","event":"apify-default-dataset-item","charged_count":1,"event_charge_limit_reached":true,"chargeable_within_limit":0}}
```

Business failure produces OUTPUT with status failed and a safe error code, **not a paid error row**. Require both terminal run status and business output to indicate success. A valid row delivered before interruption may remain chargeable even if the final run status is not SUCCEEDED.

### Pricing and spending limits

| Event | USD | Unit |
|---|---:|---|
| App Rating Result — primary event | **$0.003** | One valid snapshot written to the default Dataset |
| Actor Start | **$0.00005** | One platform start event at the supported 128–256 MB allocation |

A million underlying app ratings still produce **one result event**, not a million. Both events are synthetic and charged automatically by Apify. This Actor never manually charges the same synthetic event. No separate PPE + platform-usage surcharge is enabled by EliDev; consult the price and account terms displayed for your run.

A start event may apply to invalid input, unavailable sources, aborts or restarts without a result. An ordinary successful run totals **$0.00305**, before platform discounts/taxes. We do **not** promise a single start fee across retries or resurrections. A private resurrection counted another startup under the same run ID; customer accounting of that case has not yet been independently verified.

The configured Actor default is maxTotalChargeUsd **$0.02**. REST requests that omit this option use the platform's effective run defaults; Console or integrations may supply a different ceiling, including Apify's $5 default. Any positive finite sufficient ceiling is accepted: a larger allowance does not create more results or increase the unit price. Explicit client limits are never raised by this Actor. A ceiling below **$0.00305** cannot cover an ordinary start and one result; after restarts, the SDK also checks the actual remaining allowance. Zero, negative, invalid or unbounded effective budgets are refused; an absent effective budget is not silently replaced with unlimited spending. Startup may already have happened before a refusal, but no extraction or fake result follows. Apify enforces event limits; after one delivered result the workflow ends. The limit is per run, not your monthly bill or combined concurrent runs. Shutdown/bookkeeping may still consume creator resources after a limit.

### API integration

Use your own Apify token in server-side Authorization headers. It authenticates to Apify only; no EliDev key is required. Never expose it in browsers, public repositories, INPUT or query strings. These examples use the official asynchronous run/status/storage API.

### Python: asynchronous run lifecycle

Server-side example. Install httpx and load APIFY_TOKEN from a secret store. Tokens stay in Authorization headers, not query strings or INPUT.

```python
import os
import time
import httpx

root = "https://api.apify.com/v2"
actor = "MqFAPgPTheRv2gMGA"
with httpx.Client(
    headers={"Authorization": "Bearer " + os.environ["APIFY_TOKEN"]},
    timeout=15,
    follow_redirects=False,
    trust_env=False,
) as client:
    response = client.post(
        f"{root}/actors/{actor}/runs",
        params={"memory": 256, "timeout": 120, "maxTotalChargeUsd": 0.02},
        json={"url": "https://play.google.com/store/apps/details?id=com.whatsapp", "language": "en"},
    )
    response.raise_for_status()
    run = response.json()["data"]
    deadline = time.monotonic() + 150
    while run["status"] in {"READY", "RUNNING", "ABORTING", "TIMING-OUT"}:
        if time.monotonic() >= deadline:
            raise TimeoutError("Polling deadline reached; inspect the existing run before retrying")
        time.sleep(3)
        response = client.get(f"{root}/actor-runs/{run['id']}")
        response.raise_for_status()
        run = response.json()["data"]
    if run["status"] != "SUCCEEDED":
        raise RuntimeError("Actor did not succeed: " + run["status"])
    response = client.get(f"{root}/key-value-stores/{run['defaultKeyValueStoreId']}/records/OUTPUT")
    response.raise_for_status()
    if response.json().get("status") != "completed":
        raise RuntimeError("Business result is not completed")
    response = client.get(f"{root}/datasets/{run['defaultDatasetId']}/items", params={"limit": 1})
    response.raise_for_status()
    rows = response.json()
    if len(rows) != 1 or rows[0].get("status") != "completed":
        raise RuntimeError("Missing valid result")
    print(rows)
```

Do not automatically retry an ambiguous POST: it may have created a billable second run. Keep the returned run ID. For HTTP 429, respect Retry-After when retrying safe status reads; authentication and validation errors require correcting the cause.

### JavaScript / Node.js

Server only; never expose APIFY_TOKEN in browser code or NEXT_PUBLIC variables. This example starts a run and returns its ID for later polling:

```javascript
const token = process.env.APIFY_TOKEN;
if (!token) throw new Error('APIFY_TOKEN is required');
const root = 'https://api.apify.com/v2';
const response = await fetch(
  `${root}/actors/MqFAPgPTheRv2gMGA/runs?memory=256&timeout=120&maxTotalChargeUsd=0.02`,
  { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(15000),
    headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({url:'https://play.google.com/store/apps/details?id=com.whatsapp',language:'en'}) });
if (!response.ok) throw new Error(`Apify HTTP ${response.status}`);
const {data: run} = await response.json();
console.log({runId:run.id, status:run.status});
// GET /actor-runs/{run.id} every ~3 s, with a 150 s overall deadline.
// After SUCCEEDED: GET /datasets/{defaultDatasetId}/items?limit=1
// and GET /key-value-stores/{defaultKeyValueStoreId}/records/OUTPUT.
```

### cURL

Linux shell, APIFY_TOKEN already supplied securely in the environment; input contains no token:

```bash
curl --silent --show-error --fail-with-body --max-time 20 \
  -X POST 'https://api.apify.com/v2/actors/MqFAPgPTheRv2gMGA/runs?memory=256&timeout=120&maxTotalChargeUsd=0.02' \
  -H "Authorization: Bearer ${APIFY_TOKEN:?APIFY_TOKEN required}" \
  -H 'Content-Type: application/json' \
  --data '{"url":"https://play.google.com/store/apps/details?id=com.whatsapp","language":"en"}'
```

Do not use verbose shell tracing, recorded terminals or logs that capture Authorization. On a shared host, use your platform’s secret-injection mechanism; command-line arguments may be inspectable. Keep the run ID and use the authenticated status/Dataset/OUTPUT endpoints above.

### Errors, interruptions and recovery

- invalid_input / source_kind_mismatch / language_conflict: correct URL or fields; no result row.
- run_budget_required / unsafe_run_budget / event_budget_exhausted: set a supported positive ceiling; no fabricated result.
- provider_not_found / provider_data_unavailable / provider_rate_limited: inspect availability; do not evade source restrictions.
- robots_denied / unsafe_dns / redirect_rejected: the Actor stops without a proxy or access-control bypass.
- replay_conflict / replay_requires_review: stored state is inconsistent or incomplete; inspect the existing run before retrying.

A client timeout does not prove the remote run stopped. Poll the existing run with a finite deadline; respect Retry-After on safe GET retries. Never automatically repeat an ambiguous POST. Abort the existing run through Apify when necessary. Non-graceful aborts need not execute cleanup handlers.

Persistent RESULT_STATE binds input, run and verified user. Confirmed same-run delivery can restore OUTPUT without a second Dataset write; uncertain writes/charges require manual reconciliation. **Cloud resurrection is best effort, not a promised recovery service.** Fresh-context checks reject stale runs. A historical 20-second resurrection timed out; successful customer recovery is not claimed. A distinct run has distinct storage and is separately chargeable. Deleting a row or retrying is not a refund.

### Security, privacy and retention

Public admission still requires an authenticated, matching Apify run: actor, user, run, status, time/resource limits and storage IDs are verified. No customer-declared userId proves identity. Run, Dataset and key-value store access are set to **RESTRICTED**. Publishing the Actor does not publish your results; authorized members/tokens and voluntary debug shares can still access them. Anonymous denial has been tested; independent customer isolation and paid billing remain a separate validation.

No raw HTML, Google cookie, reviewer identity or token is exported. Only the aggregate snapshot is in Dataset. Input, OUTPUT, state and operational metadata reside in Apify storage under its access/retention policy. Unnamed storage retention observed for the creator’s Free account is seven days; your plan, named stores and exports may differ. This is not permanent archiving. Retain exports/support material only as long as your purposes require.

### Technical limits and source restrictions

One app/snapshot per run; 128–256 MB RAM, maximum 120-second container timeout, 45-second source workflow, at most four source requests and 8.6 MB decoded downloads. DNS/IPs and every redirect are revalidated. Robots rules and source refusals are respected. No login, CAPTCHA solver, residential proxy, browser or circumvention fallback.

Source changes, consent challenges, missing aggregates, rate limits and regions can prevent extraction. No uptime SLA, universal coverage, store ranking or country-specific accuracy is promised. This Actor grants no source-data licence: factual statistics, copyrighted content, database rights and contractual access restrictions are separate questions. Use it for lawful, permitted purposes, respecting applicable Google/Google Play terms. Do not use repeated runs as an unauthorized bulk database export.

### FAQ

**Does it collect written reviews?** No: aggregate rating and count only.

**Does rating_count mean written-review count?** No. It follows the listing’s aggregate count and may be null.

**Can I submit multiple apps, a country or a search URL?** No. One app URL and a page language; no country-specific guarantee.

**Why can a failed run cost something?** Startup events can be counted without a result; a valid row delivered before interruption may also remain chargeable. Check actual pricing and counters.

**Is every retry free?** No. New runs are separate billable operations; startups after resurrection are not promised free. Persistent-state checks prevent duplicating confirmed same-run result delivery, but are not a financial transaction guarantee.

**Is this an official Google service?** No. EliDev is independent, not affiliated with, endorsed by or sponsored by Google. Google Play and app names belong to their respective owners. No source licence or partnership is claimed.

### Support and permitted use

Open an issue in this Actor’s [Issues tab](https://apify.com/elidev/google-play-ratings/issues) with the run ID, safe error code and a minimal non-sensitive description. Do not post tokens, cookies, private INPUT, customer identities or billing details publicly. Use Apify’s private support flow when sensitive run sharing is necessary. No guaranteed response time is advertised.

Respect access controls, source limits, intellectual-property/database rights, applicable contracts and data protection. Do not collect unauthorized personal information or misrepresent affiliation. These conditions grant no Google licence. Financial incidents require run-specific reconciliation and, where applicable, [Apify’s compensation procedure](https://docs.apify.com/actors/monetize/compensation-requests); no automatic refund or synthetic-event deletion is promised.

# Actor input Schema

## `url` (type: `string`):

One HTTPS app-details URL on play.google.com. No credentials, tracking parameters, reviews, lists or search pages.

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

Requested page language, without selecting a country or guaranteeing locale-specific ratings.

## Actor input object example

```json
{
  "url": "https://play.google.com/store/apps/details?id=com.whatsapp",
  "language": "en"
}
```

# Actor output Schema

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

No description

## `output` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "url": "https://play.google.com/store/apps/details?id=com.whatsapp",
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("elidev/google-play-ratings").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 = {
    "url": "https://play.google.com/store/apps/details?id=com.whatsapp",
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("elidev/google-play-ratings").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 '{
  "url": "https://play.google.com/store/apps/details?id=com.whatsapp",
  "language": "en"
}' |
apify call elidev/google-play-ratings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,elidev/google-play-ratings"
        }
    }
}
```

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/MqFAPgPTheRv2gMGA/builds/ntqBHvxvcaDpBYt3a/openapi.json
