# Google Play Data Extractor (`epctex/google-play-scraper`) Actor

Get valuable info & reviews from Google Play! Access title, price, ratings, download rates, screenshots, released date, version number & developer details for any region or language. Unlimited & lightning-fast extraction. Export data in XML, JSON, CSV, Excel, or HTML formats.

- **URL**: https://apify.com/epctex/google-play-scraper.md
- **Developed by:** [epctex](https://apify.com/epctex) (community)
- **Categories:** E-commerce
- **Stats:** 1,412 total users, 50 monthly users, 48.5% runs succeeded, 27 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.10 / 1,000 dataset items

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?

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 Scraper

Google Play Scraper is a commercial-grade Google Play API built for teams that require dependable, structured Play Store data without maintaining scraping infrastructure in-house. Since Google Play does not provide a proper free API, the actor gives you app details, search results, developer portfolios, and reviews within a single job, on a transparent pay-per-event model — no proxies to manage, no platform costs beyond the data you collect.

This Google Play scraping solution is built around four query types that can be run independently or combined in one job: app detail lookups, keyword search, developer portfolio scraping, and review collection nested under any of the three. Because every mode accepts input in the same run, a single execution can cover a targeted app lookup, a category-defining search, and a competitor's full catalog at once.

### Google Play Scraper Capabilities

The Google Play API supports the following features:

- **App records** — Provide an app detail URL or a bare app id and get the full Google Play record.
- **Search results** — Provide a search URL or just a keyword, with an optional free/paid filter.
- **Developer portfolios** — Provide a developer URL or id and scrape every app they publish.
- **Reviews** — Nest them under each app record, or write them as their own flat records.
- **Any region** — Read the store in any country and language, per run or per URL.
- **Limit results** — Control output by total items or by review pages per app.

### Supported Google Play Data Sources

| Data Source | How to Target It | What It Covers |
|---|---|---|
| App details | App detail URL, or a bare app id (`com.example.app`) in `startUrls` | The full Google Play record for one app |
| Search results | Search URL in `startUrls`, or the `search` keyword field | Apps matching a keyword, with the same full record as a detail lookup |
| Developer portfolios | Developer URL or numeric developer id in `startUrls` | Every app published by a developer |
| Reviews | `includeReviews` or `onlyIncludeReviews` on any app, search, or developer query | Individual review objects, nested under the app or written flat |

### Who Can Use This Google Play Scraper

The Google Play Scraper is applicable wherever public Play Store data carries commercial or analytical value. Typical adopters include:

**App intelligence and competitive analysis teams.** Track a competitor's full catalog through developer portfolio scraping, and monitor rating, install count, and version history across regions.

**ASO and marketing teams.** Run keyword-driven search to see which apps rank for a term, in which country and language, and how pricing and category positioning compare.

**Product and UX organizations.** Pull nested review threads for an app to surface recurring complaints, feature requests, and version-specific feedback, sorted by newest, rating, or helpfulness.

**Market research and consumer insight teams.** Search across a category-defining keyword and region to observe how an app segment is priced, described, and reviewed in the user's own language.

**Academic and social science researchers.** Collect reproducible Google Play datasets defined by app, developer, keyword, region, and language, with explicit item and page caps for controlled sample sizes.

**AI and machine learning teams.** Assemble structured app-metadata and review corpora for model training, evaluation, and benchmarking.

**Investment research and financial analysis.** Monitor install growth, rating trends, and developer output for public or acquisition-target companies with an app presence.

**Journalists and OSINT analysts.** Trace a developer's published apps, a specific app's rating history, or public sentiment in its reviews.

### Migrating from the Rental Actor

This actor replaces the previous rental (monthly-subscription) Google Play Scraper. The app and review objects keep the same field names, so most integrations keep working.

What changed:

- **Pricing is now pay-per-event** instead of a flat monthly rental. You are charged per query and per result, so small runs cost a fraction of a subscription.
- **`proxy` has been removed.** Proxying is handled for you.
- **`endReviewsPage` is now `maxReviewPages`** — the old name still works, with the same meaning.
- **`onlyIncludeReviews` now implies `includeReviews`.** It writes reviews only, with no app record, whether or not `includeReviews` is set alongside it. A saved task that set it alone will get reviews where it previously got app records.
- **`search`, `country`, `lang`, `price`, `reviewsSort`, `maxItems` and `customMapFunction` are new.** None of them existed on the rental actor.
- **Category and top-chart start URLs are no longer scraped**, and are reported instead.

### Pricing: Transparent Event-Based Costs

Pay only for what you scrape. No subscriptions, no hidden fees.

| Event Type | Price | What's Included |
|---|---|---|
| App Detail | $0.001 per app id | Each app id you provide directly |
| Search Query | $0.003 per search | Each search request, first 30 dataset items free |
| Developer Query | $0.003 per portfolio | Each developer portfolio request, first 30 dataset items free |
| Review Query | $0.015 per app | Each app whose reviews you fetch, first 150 reviews free |
| Dataset Item | $0.0001 per item | Each record saved to output, beyond a query's included items |

Each search or developer query includes its first 30 dataset items, and each review query includes the first 150 reviews of its app — every review after that is a dataset item, whether it is written flat or nested into the app record. Unsupported start URLs carry no query charge, and their error record is included at no cost. Every allowance above applies to paid plans; on the free plan each stored record is charged $0.0001.

#### Cost Examples

| Scenario | Charges | Calculation | Total Cost |
|---|---|---|---|
| 1 app detail URL | 1 app detail + 1 dataset item | $0.001 + $0.0001 | $0.0011 |
| 1 search, 30 apps | 1 search query, 30 items included | $0.003 | $0.003 |
| 1 search, 250 apps | 1 search query + 220 dataset items | $0.003 + $0.022 | $0.025 |
| 1 app's reviews (`onlyIncludeReviews`, `maxReviewPages: 1`) | 1 review query, 150 reviews included | $0.015 | $0.015 |
| 1 app's reviews (`onlyIncludeReviews`, `maxReviewPages: 3`) | 1 review query + 300 dataset items | $0.015 + $0.03 | $0.045 |
| Search + reviews, 30 apps (`includeReviews`, `maxReviewPages: 1`) | 1 search query + 30 review queries | $0.003 + $0.45 | ~$0.453 |

### Controlling Google Play Scraping Costs

The event-based model rewards precise job design. A few principles follow directly from the rate card:

- **Set `maxReviewPages` whenever you enable a review flag.** Left unset, reviews page until the app runs out of them, and every review past the first 150 is a dataset item — for every app in the run.
- **Use `maxItems` to cap the apps you collect.** It trims the result batch before any reviews are fetched, so each app you drop is a review query you do not pay for.
- **Choose nested vs. flat reviews by output shape, not price.** Reviews cost the same whether written nested (`includeReviews`) or flat (`onlyIncludeReviews`).
- **Leave the review flags off when you only need app metadata.**
- **Prefer the developer name over the numeric id for large portfolios.** Google does not paginate the numeric developer view, so a numeric id returns only that developer's first ~10 apps.

Use `maxItems` and `maxReviewPages` to enforce hard ceilings on any run before scaling it. On the Free plan, `maxItems` is not user-adjustable — every demo run is capped at 10 items regardless of what you set (see [Demo Mode](#demo-mode)). On paid plans, `maxItems` is respected up to your plan's own per-run ceiling, whichever is lower.

### Demo Mode

Free-plan users can try this actor without a subscription. The limit is 5 runs per month and 10 items per run, and this cap overrides any `maxItems` value you provide — the Free plan does not let you scrape past 10 items per run. The API is not available on the Free plan. For unrestricted use, subscribe to a paid plan.

### How to Use the Google Play Scraper

1. **Define your targets.** Supply `startUrls`, `search`, or both. At least one is required.
2. **Set the region.** Use `country` and `lang` at the run level, or `gl`/`hl` parameters on individual start URLs to mix regions in one run.
3. **Decide on reviews.** Set `includeReviews` to nest reviews under each app, or `onlyIncludeReviews` to write them as flat records with no app record.
4. **Apply sorting.** Set `reviewsSort` to `NEWEST`, `RATING`, or `HELPFULNESS` when scraping reviews.
5. **Set volume limits.** Apply `maxItems` to cap total output, and `maxReviewPages` to cap review pages per app. On the Free plan these are capped regardless of the value you set — see [Demo Mode](#demo-mode).
6. **Run a small test first.** Execute a low-volume run to review the returned structure before committing to a large job.
7. **Scale.** Increase limits once the output matches your requirements.

### Input Parameters

| Field | Type | Description | Default |
|---|---|---|---|
| `startUrls` | array | App detail, search and developer URLs, bare app ids (`com.example.app`) and numeric developer ids | `[]` |
| `search` | string | A search keyword. Runs as its own query, and can be combined with `startUrls` | — |
| `country` | string | Two-letter country code. A `gl` parameter on a start URL overrides it for that entry | `US` |
| `lang` | string | Language of the results. An `hl` parameter on a start URL overrides it for that entry | `en` |
| `price` | string | `all` / `free` / `paid`. Applies to search only | `all` |
| `includeReviews` | boolean | Attach reviews to every app record under `reviewsList` | `false` |
| `onlyIncludeReviews` | boolean | Write reviews as flat records without the app record. Implies `includeReviews` | `false` |
| `reviewsSort` | string | `NEWEST` / `RATING` / `HELPFULNESS` | `NEWEST` |
| `maxReviewPages` | integer | Review pages to fetch per app, each up to 150 reviews. Unset fetches every page an app has | — |
| `maxItems` | integer | Caps the records written across every query and mode combined. Reviews nested under `includeReviews` are part of their app's record, not records of their own, so `maxReviewPages` is what bounds them. On the Free plan every run is capped at 10 items regardless of this value | `1000` |
| `customMapFunction` | string | Reshape each record before it is written | — |

At least one of `startUrls` or `search` is required. If you provide both, both are scraped. `maxItems` is prefilled with 1000 in the interface and collects everything when you clear it; `maxReviewPages` is unset by default.

### Input Examples

**Scrape a single app by URL**

```json
{
    "startUrls": ["https://play.google.com/store/apps/details?id=com.instagram.android"]
}
```

**Search for apps by keyword**

```json
{
    "search": "habit tracker",
    "country": "US",
    "lang": "en",
    "price": "free",
    "maxItems": 100
}
```

**Scrape a developer's full portfolio**

```json
{
    "startUrls": ["https://play.google.com/store/apps/developer?id=Supercell"]
}
```

**Combine start URLs and search, with reviews**

```json
{
    "startUrls": [
        "https://play.google.com/store/apps/details?id=com.instagram.android",
        "https://play.google.com/store/search?q=workout&c=apps",
        "https://play.google.com/store/apps/developer?id=Supercell"
    ],
    "search": "habit tracker",
    "country": "US",
    "lang": "en",
    "price": "free",
    "includeReviews": true,
    "reviewsSort": "NEWEST",
    "maxReviewPages": 2,
    "maxItems": 50
}
```

#### URL Patterns

```text
https://play.google.com/store/apps/details?id=com.instagram.android
https://play.google.com/store/search?q=workout&c=apps
https://play.google.com/store/apps/developer?id=Supercell
https://play.google.com/store/apps/dev?id=5700313618786177705
com.instagram.android
5700313618786177705
```

#### Tips

- You can mix app detail, search and developer URLs in `startUrls`, and add a `search` keyword on top — each entry is scraped as its own query.
- When you want one specific app, copy and paste its Play Store URL, or just its app id.
- Per-URL `hl` and `gl` parameters override the run-level `lang` and `country`, so one run can mix regions. Entries that resolve to the same query in the same region are scraped once.
- A developer entry can be a name (`?id=Supercell`) or a numeric id. As a bare entry without a URL, only numeric developer ids are recognised — use a developer URL to pass a name.
- **Prefer the developer name for large portfolios.** Google does not paginate the numeric developer view, so `?id=5700313618786177705` returns only that developer's first ~10 apps. The same developer requested by name — `?id=Google+LLC` — returns the full list.

#### Not Supported: Category and Top-Chart URLs

Google Play category and chart pages — `/store/apps/category/...`, `/store/apps/top`, `/store/apps/collection/...` — cannot be scraped. Google Play's search accepts a free-text term, not a category id, so a category page has no faithful equivalent as a query. To cover a category, use a search keyword that describes it instead.

Each such entry is written to the dataset as an error record — free on a paid plan, with neither a query charge nor a dataset-item charge — so you can see exactly which input was not used:

```json
{
  "inputSource": {
    "queryIndex": 0,
    "mode": "unsupported",
    "filters": { "country": "US", "lang": "en" },
    "paginationToken": null
  },
  "error": true,
  "code": "C003",
  "entry": "https://play.google.com/store/apps/category/GAME_ACTION",
  "message": "Google Play category and top-chart URLs are not supported. Use a search URL, a search keyword, an app detail URL or a developer URL instead."
}
```

### Output Examples

**App**

```json
{
  "inputSource": {
    "queryIndex": 0,
    "mode": "detail",
    "appId": "com.tinybuildgames.helloneighbor",
    "filters": { "country": "US", "lang": "en" },
    "paginationToken": null
  },
  "title": "Hello Neighbor",
  "appId": "com.tinybuildgames.helloneighbor",
  "url": "https://play.google.com/store/apps/details?id=com.tinybuildgames.helloneighbor",
  "summary": "Hello Neighbor is a stealth horror game about sneaking into your neighbor's house.",
  "installs": "50,000,000+",
  "minInstalls": 50000000,
  "score": 3.9,
  "scoreText": "3.9",
  "ratings": 1051408,
  "reviews": 20762,
  "free": true,
  "price": 0,
  "priceText": "Free",
  "currency": "USD",
  "developer": "tinyBuild Games",
  "developerId": "tinyBuild+Games",
  "developerEmail": "support@tinybuild.com",
  "developerLegalName": "TinyBuild LLC",
  "genre": "Action",
  "categories": [{ "name": "Action", "id": "GAME_ACTION" }],
  "contentRating": "Teen",
  "version": "2.3.15",
  "released": "Dec 6, 2018",
  "updated": 1749574800000,
  "screenshots": ["https://play-lh.googleusercontent.com/..."]
}
```

`reviews` is the number of reviews the app has, an integer. It is never the review objects themselves — those live in `reviewsList`. Search and developer results carry the same full app record as a detail lookup, not a lighter summary.

**Reviews (nested, `includeReviews`)**

```json
{
  "appId": "com.instagram.android",
  "reviews": 4550757,
  "reviewsList": [
    {
      "id": "474d5f41-cd43-4421-b5be-d08a9086724d",
      "userName": "A Google user",
      "score": 5,
      "text": "Love it",
      "date": "2026-08-31T09:12:03.000Z",
      "thumbsUp": 2,
      "appId": "com.instagram.android",
      "appCountry": "US",
      "appLanguage": "en"
    }
  ]
}
```

`reviewsList` holds every review of the app, bounded by `maxReviewPages`. `maxItems` does not bound it — the app and its reviews are one record, so a nested review is not an item to cap. Set `maxReviewPages` whenever you scrape reviews across a search or a developer portfolio, or every app in the run pages until it runs out of reviews.

The app and all of its reviews are stored as a single record, and a record holds at most 9 MB, so `reviewsList` stops at roughly 20,000 reviews, with a warning in the log. Use `onlyIncludeReviews` for apps with more reviews than that: one review per record has no such ceiling.

With `onlyIncludeReviews`, the app record is skipped and those same review objects are written as top-level records instead. There `maxItems` does bound the paging, because each review is its own record.

#### Input Attribution

Every record carries an `inputSource` object attributing it to the input that produced it:

| Field | Description |
|---|---|
| `queryIndex` | Which resolved input produced the record, counting from 0 |
| `mode` | `detail`, `search`, `developer`, `review` or `unsupported` — the path the record came from |
| `appId` / `searchQuery` / `developerId` | Whichever apply. A record found through a search carries both the `searchQuery` that found it and its own `appId` |
| `filters` | The `country`, `lang`, `price` and `reviewsSort` actually used for the query — absent keys were not sent |
| `paginationToken` | The token that fetched this review page. `null` on the first page and on everything that is not a review |

### Custom Map Function

`customMapFunction` receives each output object and returns whatever you want stored in its place. With `includeReviews` it receives the app record before `reviewsList` is attached, so reviews pass through untouched:

```javascript
(object) => {
    return {
        appId: object.appId,
        title: object.title,
        score: object.score,
        installs: object.installs,
    };
}
```

If your function throws, the original record is written unchanged and a warning is logged, so an error there never loses you data.

### During the Run

The actor logs its progress as it works through each start URL, search, or review page. If an input entry cannot be scraped — for example a category or top-chart URL — it is written to the dataset as an error record with an explanatory code and message, at no charge, rather than silently dropped. If your input is invalid altogether, such as leaving both `startUrls` and `search` empty, the actor stops immediately with an explanation of what went wrong.

### Frequently Asked Questions

**What can the Google Play Scraper collect?** The actor collects Google Play app details, search results, developer portfolios, and app reviews, sourced through direct URLs, bare app/developer ids, or a search keyword, in any combination in the same run.

**Can I scrape app reviews?** Yes. Set `includeReviews: true` to nest reviews under their app's `reviewsList`, or `onlyIncludeReviews: true` to write them as flat top-level records instead. Review retrieval is billed as a review query at $0.015 per app, with the first 150 reviews included.

**Can I run start URLs and a keyword search in one job?** Yes. `search` and `startUrls` can be supplied together in a single run. At least one of the two must be provided.

**How do I scrape a developer's whole app catalog?** Provide a developer URL or numeric developer id in `startUrls`. Prefer the developer name over the numeric id — Google does not paginate the numeric view, so it returns only the first ~10 apps.

**How do I read the store in a different country or language?** Set `country` and `lang` at the run level, or add `gl`/`hl` query parameters to an individual start URL to override them for just that entry.

**How do I sort reviews?** Set `reviewsSort` to `NEWEST`, `RATING`, or `HELPFULNESS`.

**How do I cap the volume of a run?** `maxItems` caps total records across every query and mode, and defaults to 1000. `maxReviewPages` caps review pages per app and is unlimited by default.

**Can I customise the output?** Yes. `customMapFunction` takes each object as an argument and returns a mapped object to store in its place. If it throws, the original record is written unchanged.

**Can I scrape category or top-chart pages?** No. Google Play's search only accepts a free-text term, not a category id, so category and top-chart URLs have no faithful equivalent as a query. Each such entry is written as a free error record instead — use a search keyword that describes the category.

**Does a large review set cost more per app?** The review query itself is a flat $0.015 per app regardless of how many reviews it has; only reviews beyond the first 150 are charged as dataset items.

**Are dataset items always charged?** Dataset items are $0.0001 per item stored, and on paid plans each search or developer query includes its first 30 items free, and each review query includes its first 150 reviews free. On the free plan every stored record is charged.

**What happens if my input is invalid?** The actor stops immediately and returns an explanation of the problem. Leaving both `startUrls` and `search` empty is one such case.

**Is there a free way to test the Google Play Scraper?** Yes. Demo Mode allows free-plan users to run the actor without a subscription, limited to 5 runs per month and 10 items per run.

**Is API access included on the Free plan?** No. The API is not available on the Free plan. A paid plan is required for API access and unrestricted use.

**I'm migrating from the rental actor — what changed?** See [Migrating from the Rental Actor](#migrating-from-the-rental-actor) above: pricing moved to pay-per-event, `proxy` was removed, `endReviewsPage` was renamed to `maxReviewPages`, `onlyIncludeReviews` now implies `includeReviews`, and category/top-chart URLs are reported rather than scraped.

### Support and Custom Integrations

Please visit us through [epctex.com](https://epctex.com) to see all available products. For custom integrations, reach out via the chat box on [epctex.com](https://epctex.com). For support: <business@epctex.com>.

# Actor input Schema

## `startUrls` (type: `array`):

URLs to start with. Supports app detail URLs, search URLs and developer URLs, as well as bare app ids (com.example.app) and numeric developer ids. Category and top-chart URLs are not supported. Can be combined with a search keyword - each entry is scraped as its own query.

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

The keyword to search on Google Play. Runs as a separate query from Start URLs - you can provide both.

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

Two-letter country code the Play Store is read as. Applies to app details, search, developer portfolios and reviews. A gl parameter on a Start URL overrides it for that entry.

## `lang` (type: `string`):

Language of the results, e.g. en, pt-BR, zh-Hant-TW or es-419. Applies to app details, search and reviews. An hl parameter on a Start URL overrides it for that entry.

## `price` (type: `string`):

Filter search results by price. Only applies to the search keyword and to search Start URLs.

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

Fetch reviews for every scraped app and nest them under the app record in reviewsList. Every review page is charged, so set Maximum review pages per app unless you really want all of them.

## `onlyIncludeReviews` (type: `boolean`):

Write reviews as flat records without the app record. Suggested when there are many reviews, since the reviews nested into an app record are capped by the maximum number of items and by the record size limit. Implies Include reviews.

## `reviewsSort` (type: `string`):

How to sort the reviews of each app.

## `maxReviewPages` (type: `integer`):

How many review pages to fetch per app. Each page holds up to 150 reviews. Leave it empty to fetch every review an app has - this can be thousands of pages for a popular app, and each page is charged.

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

Maximum number of items that you want as output, across every query and mode combined. Clear it to collect everything.

## `customMapFunction` (type: `string`):

Function that takes each of the objects as argument and returns data that will be mapped by the function itself.

## Actor input object example

```json
{
  "startUrls": [
    "https://play.google.com/store/apps/details?id=com.tinybuildgames.helloneighbor&hl=tr&gl=US",
    "https://play.google.com/store/search?q=hello&c=apps&hl=tr&gl=US",
    "https://play.google.com/store/apps/developer?id=Supercell"
  ],
  "search": "workout",
  "country": "US",
  "lang": "en",
  "price": "all",
  "includeReviews": false,
  "onlyIncludeReviews": false,
  "reviewsSort": "NEWEST",
  "maxReviewPages": 2,
  "maxItems": 1000,
  "customMapFunction": "(object) => { return {...object} }"
}
```

# Actor output Schema

## `overview` (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 = {
    "startUrls": [
        "https://play.google.com/store/apps/details?id=com.tinybuildgames.helloneighbor&hl=tr&gl=US",
        "https://play.google.com/store/search?q=hello&c=apps&hl=tr&gl=US",
        "https://play.google.com/store/apps/developer?id=Supercell"
    ],
    "search": "workout",
    "country": "US",
    "lang": "en",
    "maxReviewPages": 2,
    "maxItems": 1000,
    "customMapFunction": (object) => { return {...object} }
};

// Run the Actor and wait for it to finish
const run = await client.actor("epctex/google-play-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 = {
    "startUrls": [
        "https://play.google.com/store/apps/details?id=com.tinybuildgames.helloneighbor&hl=tr&gl=US",
        "https://play.google.com/store/search?q=hello&c=apps&hl=tr&gl=US",
        "https://play.google.com/store/apps/developer?id=Supercell",
    ],
    "search": "workout",
    "country": "US",
    "lang": "en",
    "maxReviewPages": 2,
    "maxItems": 1000,
    "customMapFunction": "(object) => { return {...object} }",
}

# Run the Actor and wait for it to finish
run = client.actor("epctex/google-play-scraper").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 '{
  "startUrls": [
    "https://play.google.com/store/apps/details?id=com.tinybuildgames.helloneighbor&hl=tr&gl=US",
    "https://play.google.com/store/search?q=hello&c=apps&hl=tr&gl=US",
    "https://play.google.com/store/apps/developer?id=Supercell"
  ],
  "search": "workout",
  "country": "US",
  "lang": "en",
  "maxReviewPages": 2,
  "maxItems": 1000,
  "customMapFunction": "(object) => { return {...object} }"
}' |
apify call epctex/google-play-scraper --silent --output-dataset

```

## MCP server setup

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

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/IoELF3jVX017BQTbM/builds/SrO1GhikYcVxJx4Cf/openapi.json
