# Google Play Scraper (`dami_studio/google-play-apps-scraper`) Actor

Google Play scraper for apps by keyword, category or package ID. Each row has the title, developer and contact, rating and star histogram, install band, price, in-app purchase range, content rating, version, last update, icon and screenshots. $0.25 per 1,000 apps.

- **URL**: https://apify.com/dami_studio/google-play-apps-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Developer tools, Integrations
- **Features:** Limited permissions
- **Stats:** 4 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.24 / 1,000 google play apps

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

**Scrape Google Play app listings by keyword, category or package ID, one row per app**: title,
developer, rating and ratings count, star histogram, price, in-app purchase range, install band,
content rating, version, last update, developer contact, icon and screenshots. No Google account and
no API key.

Two things to know before you start. Install counts are Google's own band, so you get `1,000,000+`
and never an exact figure, though `realInstalls` carries Play's closer estimate where it publishes
one. And a search term reads the first page of results, which is roughly the screenful a person
sees, not the whole catalogue.

| | |
|---|---|
| **Input** | Search terms, package IDs or Play links, and a store category |
| **Output** | One row per app: title, developer and contact, rating and histogram, installs, price and in-app purchases, content rating, version, dates, description, icon and screenshots |
| **Ceiling** | 2,000 apps per run |
| **Account needed** | None, and no Google API key |
| **Price** | $0.25 per 1,000 apps on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 20,000 |

### 🔍 What Google Play Apps Scraper does

Three ways in, and you can mix them in one run.

**Search terms** work the way the store's own search box does. Type `budget tracker` and you get the
apps Play puts in front of somebody who typed that.

**Package IDs or Play links** go straight to the listing. `com.whatsapp` and
`https://play.google.com/store/apps/details?id=com.whatsapp` both work, in the same list.

**A store category** returns what Play features on that category page: its top free, top grossing,
top paid and trending shelves, in Play's own order.

Whichever way you came in, every app is then read from its own listing, so the row is the same shape
each time. Package IDs are fetched first and the rest of your row budget is split evenly between the
search terms and the category, so five terms and 50 rows give ten of each rather than fifty of the
first.

### 📋 What data you get from each Google Play app

| What you get | Field |
|---|---|
| The app and its listing | `appId`, `url`, `title`, `summary`, `description`, `descriptionHtml` |
| Developer, with the contact details they publish | `developer`, `developerId`, `developerUrl`, `developerLegalName`, `developerEmail`, `developerWebsite`, `developerAddress`, `privacyPolicy` |
| Rating, counts and the star breakdown | `score`, `scoreText`, `ratings`, `reviews`, `histogram` |
| Installs, as Play's band and its own estimate | `installs`, `minInstalls`, `realInstalls` |
| Price and how the app makes money | `price`, `currency`, `priceText`, `free`, `offersIAP`, `inAppPurchaseRange`, `containsAds` |
| Category and content rating | `genre`, `genreId`, `contentRating`, `contentRatingDescription` |
| Release history at a glance | `released`, `releasedAt`, `updated`, `updatedText`, `recentChanges`, `version`, `androidVersion`, `androidMinSdk` |
| Artwork | `icon`, `headerImage`, `screenshots`, `video` |
| How the row was reached, and where | `query`, `searchType`, `available`, `country`, `language`, `scrapedAt` |

### ▶️ How to scrape Google Play apps

1. Open [Google Play Apps Scraper](https://apify.com/dami_studio/google-play-apps-scraper) and click
   **Try for free**.
2. Put your terms into **Search terms**, or package IDs into **App IDs or Play links**. Delete the
   examples first.
3. Set **Country** to the storefront you care about, and **Maximum rows**. Start around 20.
4. Click **Start**.
5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.

Start it with every box empty and you get one free sample row, marked `_sample: true`, to see the
shape.

### 💰 How much does it cost to scrape Google Play apps?

**$0.25 per 1,000 apps** on the Free plan. Paid plans pay a little less: $0.248 on Starter, $0.246 on Scale and $0.244 on Business. On the free plan, the $5 Apify gives you each month covers about 20,000 apps.

One row is one app. An app that turns up under two of your search terms is fetched and counted once.
Sample rows, diagnostic rows, a term that matches nothing and a package ID that does not exist are
all free.

### 📥 What you give it

```json
{
  "searchTerms": ["budget tracker"],
  "appIds": ["com.whatsapp"],
  "country": "us",
  "language": "en",
  "maxItems": 50
}
```

| Field | Default | What it is |
|---|---|---|
| `searchTerms` | none, the form opens with `budget tracker` and `habit tracker` | Words or phrases, exactly as you would type them into Play. Up to 20. |
| `appIds` | none | Package IDs like `com.whatsapp`, or full Play links. Both forms in one list. Up to 500. |
| `category` | none | A Play category such as `FINANCE` or `GAME_PUZZLE`, picked from the dropdown. |
| `country` | `us` when empty | The storefront to read. Price, currency, install band, content rating and even whether the app exists all depend on it. |
| `language` | `en` when empty | The language of the text. It changes no numbers. |
| `maxItems` | `20` when empty | Total apps for the run, up to 2,000. |
| `proxyUrls` | none | Optional. Your own servers, used exactly as given. |

**One storefront per run.** `country` is a single code, so comparing prices across five markets means
five runs, one per market.

**An unrecognised `category` is dropped rather than refused.** Alongside search terms or package IDs
you get a `BAD_INPUT` notice and the rest of the run still happens. On its own, the run has nothing
left to do and returns the free sample row, which is easy to misread as a failure.

### 📤 What you get back

A real row from a run in September 2026, with the long text and links trimmed:

```json
{
  "ok": true,
  "recordType": "app",
  "query": "budget tracker",
  "searchType": "search",
  "appId": "com.thebudgetingapp.thebudgetingapp",
  "url": "https://play.google.com/store/apps/details?id=com.thebudgetingapp...",
  "title": "Budgeting App - Spend Tracker",
  "summary": "Budget planner and expense tracker to help manage your personal finance.",
  "description": "The Budgeting App is a simple and easy-to-use budget planner and daily expense tracker designed to simplify your personal finance...",
  "descriptionHtml": "The Budgeting App is a simple and easy-to-use budget planner and daily expense tracker...",
  "developer": "The Budgeting App",
  "developerId": "4918164273133767251",
  "developerUrl": "https://play.google.com/store/apps/developer?id=4918164273133767251",
  "developerLegalName": "THE BUDGETING COMPANY LIMITED",
  "developerEmail": "...",
  "developerWebsite": "https://thebudgeting.app",
  "developerAddress": "...",
  "privacyPolicy": "https://thebudgeting.app/privacy-policy",
  "genre": "Finance",
  "genreId": "FINANCE",
  "score": 4.71,
  "scoreText": "4.7",
  "ratings": 4207,
  "reviews": 100,
  "histogram": { "1": 167, "2": 0, "3": 83, "4": 377, "5": 3573 },
  "installs": "100,000+",
  "minInstalls": 100000,
  "realInstalls": 226245,
  "price": 0,
  "currency": "USD",
  "priceText": "Free",
  "free": true,
  "offersIAP": true,
  "inAppPurchaseRange": "$2.99 - $89.99 per item",
  "containsAds": false,
  "adsLabel": null,
  "contentRating": "Everyone",
  "contentRatingDescription": null,
  "released": "Aug 13, 2022",
  "releasedAt": "2022-08-14T04:31:42.000Z",
  "updated": "2026-09-11T07:25:11.000Z",
  "updatedText": "Sep 11, 2026",
  "recentChanges": "- Budget home screen widget\n- Tracker insights - see when you'll get there...",
  "version": "7.2.2",
  "androidVersion": "11",
  "androidMinSdk": 36,
  "icon": "https://play-lh.googleusercontent.com/yIlsuOo3M2rthxd8QputTbLqQVqW0nMa...",
  "headerImage": "https://play-lh.googleusercontent.com/BESOXa68gKo6WUQAPsCa-gFs24X18cy0...",
  "screenshots": ["https://play-lh.googleusercontent.com/fduMeD1CdniW-mRkz76TN3iC5Xtl..."],
  "video": null,
  "topChartRank": null,
  "available": true,
  "country": "us",
  "language": "en",
  "scrapedAt": "2026-09-21T01:23:09.777Z"
}
```

| Field | How to read it |
|---|---|
| `appId` | The Android package ID. Stable, and the right key to join and dedupe on. |
| `searchType` | How this app was reached: `search`, `appId` or `category`. `query` holds the term, ID or category behind it. |
| `installs` | Google's band, like `1,000,000+`. `minInstalls` is the same band as a number, `realInstalls` is Play's closer estimate. |
| `score`, `scoreText` | The raw average and the rounded label Play prints. `ratings` counts stars, `reviews` counts written ones. |
| `histogram` | Star counts one to five, so you can see the shape rather than just the mean. |
| `price` | In whole currency units for the `country` you asked for, with `priceText` and `free` beside it. |
| `offersIAP`, `inAppPurchaseRange`, `containsAds` | The money labels off the listing. The range is `null` when Play shows none. |
| `version`, `androidVersion` | `null` on apps that ship a different build per device, which Play shows as varying. |
| `available` | `false` when the listing exists but the storefront has no offer for it. |
| `screenshots` | Every screenshot on the listing, in order. The row above has 24 and shows the first. |

### 🧾 Reading the output

| Row | How to spot it | Charged |
|---|---|---|
| An app | `_sample` and `_diagnostic` both absent | yes |
| The sample row | `_sample: true` | no |
| A diagnostic | `_diagnostic: true` and an `errorCode` | no |

**Filter on `_sample` and `_diagnostic`, not on `recordType`.** The free sample row also carries
`recordType: "app"`, so a filter on that alone keeps it.

| Code | What it means |
|---|---|
| `BAD_INPUT` | Something in the input could not be used. `details` says what to change. |
| `NO_RESULTS` | Play answered, and had nothing for that term or category. |
| `NOT_FOUND` | That package ID does not exist, or is not published in this `country`. |
| `NOT_SERVED` | This app was not served this run. Re-run it later. Runs before 13 October 2026 wrote `BLOCKED` here. |
| `NETWORK` | Play could not be reached or answered badly. |
| `TIME_BUDGET` | The run hit its time limit before reaching this app. Everything already delivered is complete. |
| `PROXY_INPUT_ADJUSTED` | A connection setting in your input could not be used, so the run used its own. |
| `CHARGE_ERROR` | A charge could not be recorded. The run stops handing over rows rather than carrying on quietly. |

### 💡 What people use it for

- Watching a category shelf week by week to see which apps climb and which drop off.
- Pulling the twenty apps that answer a search term, then reading their ratings, install bands and
  in-app purchase ranges side by side before building something in that space.
- Checking how an app is priced across markets by running the same package ID per `country`.
- Tracking a rival's release notes and version numbers with a scheduled run on its package ID.

One market, both stores, and what users complain about:

1. Run this on `budget tracker` and sort the rows by `realInstalls`.
2. Put the top `appId` values into
   [Google Play Reviews Scraper](https://apify.com/dami_studio/google-play-reviews-scraper) with
   `starRating` set to `1`, and read what the unhappy users say.
3. Run [App Store Scraper](https://apify.com/dami_studio/app-store-scraper) with the same
   `searchTerm` to see who owns the same market on iPhone.

### 🚧 What it does not do

- **No reviews.** Ratings, the count and the star histogram are here, but review text is a
  different actor, linked below.
- **Install counts are a band.** `1,000,000+` is what Play publishes. `realInstalls` is its own
  estimate and is not an exact number either.
- **A search reads one page per term**, so it is the front of the results, not everything Play would
  ever show for it.
- **A category returns the featured shelves**, which is what that page shows, not every app filed
  under it.
- **One storefront per run.** Five markets means five runs.
- **`version` is `null` for apps that vary by device**, because the listing says so rather than
  giving a number.
- **Rows are a snapshot.** Ratings, prices and rankings move daily, and `scrapedAt` records when the
  row was read.
- **Developer fields are whatever the developer filled in.** An e-mail or a website can be missing or
  stale, and the address is free text.
- It is not an official Google tool. Dami's Studio is independent and is not affiliated with or endorsed by Google, or by any other company named on this page.

### 🧭 Which app scraper do you need?

| If you want | Use |
|---|---|
| Google Play listings by keyword, category or package ID | This one |
| The review text for those apps | [Google Play Reviews Scraper](https://apify.com/dami_studio/google-play-reviews-scraper) |
| The same job on the iPhone App Store | [App Store Scraper](https://apify.com/dami_studio/app-store-scraper) |
| New reviews from both stores, sorted into bugs, billing and requests | [App Review Triage](https://apify.com/dami_studio/app-review-triage-monitor) |
| How often people search for an app name over time | [Google Trends Scraper](https://apify.com/dami_studio/google-trends-scraper) |

### ❓ Questions people ask

#### Do I need a Google account or a Play API key to scrape Google Play?

No. Nothing to sign up for and no quota.

#### Can I get the exact install count?

No, and nobody outside Google can. Play publishes a band. `realInstalls` is the closest number it
gives out.

#### Why did a package ID return nothing?

Usually it is not published in the `country` you asked for. You get a `NOT_FOUND` row saying so, and
it costs nothing.

#### How many apps does one search term return?

Whatever fits on Play's first page of results for it, capped by your share of `maxItems`.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/google-play-apps-scraper/api/python) has code for Python, JavaScript and
the CLI. MCP clients like Claude and ChatGPT use
`https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/google-play-apps-scraper`; coding assistants can load
[our skill
file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-search-app-stores-SKILL.md). All three
bill your Apify account at the same price.

#### Is scraping Google Play legal?

These are public store listings. Developer contact fields can hold personal data, which GDPR and
similar laws cover, so have a reason for collecting them. Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the term or package ID and the run ID. The
`errorCode` and `details` on the diagnostic row usually name the problem on their own.

# Actor input Schema

## `searchTerms` (type: `array`):

Words or phrases to search the Google Play Store for, exactly as you would type them into the store search box. Up to 20 per run. Each term contributes its share of the row limit below.

## `appIds` (type: `array`):

Android package ids such as com.whatsapp, or full Play links such as https://play.google.com/store/apps/details?id=com.whatsapp. You can mix both forms in the same list. These are fetched before anything else, one row each.

## `category` (type: `string`):

Optional. Returns the apps Google Play features on that category page - its top free, top grossing, top paid and trending shelves - in Play's own order. Leave empty if you are searching or passing app ids.

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

Two-letter country code for the storefront to read, for example us, gb, de, in, br. Google Play is a store per country: price, currency, install band, content rating and even whether the app exists depend on this.

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

Two-letter language code for the text, for example en, de, es, pt, fr. Controls the title, summary, description and changelog. It does not change any of the numbers.

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

Total number of apps to return. App ids are taken first, then whatever budget is left is split evenly between the search terms and the category. Keep it low while you are testing - you pay per row.

## `proxyUrls` (type: `array`):

Leave this empty for a normal run. Fill it in only if you want the traffic to leave through proxy servers you already pay for, one URL per line, in the form http://user:pass@host:port.

## Actor input object example

```json
{
  "searchTerms": [
    "budget tracker",
    "habit tracker"
  ],
  "country": "us",
  "language": "en",
  "maxItems": 20
}
```

# Actor output Schema

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

Every row in the default dataset: query, appId, title, developer, score, ratings, installs, priceText, offersIAP, contentRating, updated, version, url, icon, searchType, summary, description, descriptionHtml, genre, genreId, scoreText, reviews, histogram, minInstalls, realInstalls, price, currency, free, inAppPurchaseRange, containsAds, adsLabel, contentRatingDescription, released, releasedAt, updatedText, recentChanges, androidVersion, androidMinSdk, developerId, developerUrl, developerLegalName, developerEmail, developerWebsite, developerAddress, privacyPolicy, headerImage, screenshots, video, topChartRank, available, country, language, scrapedAt. An empty, blocked or unmatched run returns a single uncharged row explaining what happened instead.

# 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 = {
    "searchTerms": [
        "budget tracker",
        "habit tracker"
    ],
    "country": "us",
    "language": "en",
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/google-play-apps-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 = {
    "searchTerms": [
        "budget tracker",
        "habit tracker",
    ],
    "country": "us",
    "language": "en",
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/google-play-apps-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 '{
  "searchTerms": [
    "budget tracker",
    "habit tracker"
  ],
  "country": "us",
  "language": "en",
  "maxItems": 20
}' |
apify call dami_studio/google-play-apps-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/google-play-apps-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/oppyZOCbkcHi5FgEG/builds/qHPDnfUUxBFyXt5kI/openapi.json
