# China Android App Store Checker (`evanlin/china-android-app-store-checker`) Actor

Check if any app is listed on China's major Android stores — Tencent MyApp (应用宝), Huawei AppGallery, Xiaomi, vivo — with real download counts, ratings and versions in one call. $0.015 per package name. Input: package names or app names. Output: one record per app with a plain-English summary.

- **URL**: https://apify.com/evanlin/china-android-app-store-checker.md
- **Developed by:** [Evan Lin](https://apify.com/evanlin) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 package checkeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## China Android App Store Checker

**Is this app in China's Android app stores, and how big is it?**

Give it a package name (or an app name) and get, in one call, whether the app is listed on the four major Chinese Android stores — **Tencent MyApp (应用宝)**, **Huawei AppGallery**, **Xiaomi** and **vivo** — plus **real download counts**, ratings, current version, last update, developer and category. One record per app, with a plain-English summary you can hand straight to a user.

Google Play does not operate in China. Chinese Android distribution is split across vendor stores with no shared API, all in Chinese. This Actor is the missing lookup.

```json
{
  "packageName": "com.duolingo",
  "listedAnywhere": true,
  "listedStores": ["tencent_myapp", "xiaomi", "vivo", "huawei"],
  "totalDownloads": 343235921,
  "maxDownloadsStore": "huawei",
  "latestVersion": "6.96.2-china",
  "summary": "Listed on 4 of 4 major Chinese Android stores (Tencent MyApp, Xiaomi, vivo, Huawei AppGallery). ~343M total downloads (highest: Huawei AppGallery).",
  "stores": {
    "tencent_myapp": { "status": "listed", "appName": "多邻国", "downloads": 4541364, "rating": 4.6, "version": "6.96.2-china", "updatedAt": "2026-09-10", "developer": "Duolingo, Inc.", "category": "Education", "storeUrl": "https://sj.qq.com/appdetail/com.duolingo" },
    "huawei":        { "status": "listed", "appName": "多邻国", "appNameEn": "Duolingo", "downloads": 308000000, "downloadsSource": "bucket", "rating": 4.0, "ratingCount": 51230, "storeUrl": "https://appgallery.huawei.com/app/C10169556" },
    "xiaomi":        { "status": "listed", "appName": "多邻国", "downloads": null, "rating": 5.0, "ratingCount": 1893 },
    "vivo":          { "status": "listed", "appName": "多邻国-英语日语法语", "downloads": 30694557, "rating": 4.9 }
  }
}
```

### Who is this for

**App intelligence and ASO tools.** Chinese Android is a blind spot in most app-analytics products. Feed thousands of package names per day and get a stable, normalised feed of store presence, versions and download counts to put on your dashboards.

**Brand protection and anti-piracy.** Chinese stores host many look-alikes. Check your official package, then use `includeSimilar` to get every app whose name resembles yours, with package names and download counts, for review.

**Developers and publishers looking at China, and anyone doing due diligence.** Answer "is our competitor in China, and how big are they?" or "does this company's claimed 100M downloads hold up?" in a single run. Tencent MyApp and vivo publish absolute install counts — numbers no other public source gives you.

### Quick start

**One app** — the default input already checks three packages (one global hit, one foreign-only app, one Chinese giant):

```json
{ "packageNames": ["com.duolingo", "com.spotify.music", "com.tencent.mm"] }
```

**By name, when you don't know the package** — the best match is resolved and reported with a confidence score:

```json
{ "appNames": ["Duolingo", "微信"] }
```

**Batch** — up to 1,000 package names per run; results stream into the dataset as they complete:

```json
{ "packageNames": ["com.a.app", "com.b.app", "..."], "maxConcurrency": 10 }
```

Use the Actor from the Apify Console, via API, through [Apify MCP](https://mcp.apify.com) from Claude / ChatGPT / Cursor, or from n8n, Make and Zapier with the Apify integration.

### Input

| Field | Type | Description |
|---|---|---|
| `packageNames` | string\[] | Android package names, e.g. `com.duolingo`, or Google Play / store URLs containing one. 1–1000 items, deduplicated. Invalid names are reported in the output and not charged. |
| `appNames` | string\[] | App display names, e.g. `Duolingo`. Use only when the package name is unknown. Resolved via store search; the record carries `matchConfidence` and the top `candidates`. |
| `stores` | string\[] | Subset of `tencent_myapp`, `huawei`, `xiaomi`, `vivo`. Default: all four. |
| `includeSimilar` | boolean | Also return other apps whose names resemble the checked app in `candidates` (brand-protection use). Default `false`. |
| `assertListedEverywhere` | string\[] | Package names that must come back `listed` on every selected store, otherwise the run fails naming the store. Use in a scheduled run as a health check for your pipeline. |
| `maxConcurrency` | integer | Package names processed in parallel (1–20, default 8). |
| `proxyConfiguration` | object | Optional. Stores are reachable directly from Apify; enable Apify Proxy only if you see `BLOCKED` errors. |

### Output

One dataset item per app. Top-level fields answer most questions on their own; `stores.*` carries the per-store detail.

| Field | Description |
|---|---|
| `packageName` | The package that was checked. |
| `queriedBy`, `query`, `matchConfidence` | How the record was requested. `matchConfidence` is `1` for package queries and `0–1` for name queries. |
| `listedAnywhere` | `true` if at least one store lists the app. |
| `listedStores`, `failedStores` | Store ids that list the app / that could not be checked. |
| `totalDownloads`, `maxDownloadsStore` | Sum of the per-store download counts that are available, and where the largest figure comes from. |
| `latestVersion` | Highest version string across listing stores. |
| `summary` | One English sentence describing the result. |
| `stores.<id>.status` | `listed`, `not_listed` or `fetch_failed`. Never ambiguous: "not found" and "could not check" are different values. |
| `stores.<id>.appName`, `appNameEn` | Display name (Chinese) and, where the store provides one, English. |
| `stores.<id>.version`, `updatedAt` | Current version and last update date (`YYYY-MM-DD`). |
| `stores.<id>.rating`, `ratingCount` | Rating on a 0–5 scale and number of ratings. |
| `stores.<id>.downloads`, `downloadsSource` | Install count as an integer. `exact` when the store publishes the number, `bucket` when it publishes a range such as "1亿+" (the lower bound is used). |
| `stores.<id>.category`, `categoryOriginal` | English category and the store's original Chinese label. |
| `stores.<id>.tagline`, `description` | The store's one-line tagline (where it has one) and its description, whitespace-normalised and capped at 600 characters. Chinese, as published. |
| `stores.<id>.developer`, `sizeBytes`, `storeUrl`, `iconUrl`, `storeAppId` | Developer name, APK size, canonical store page, icon and the store's own app id. |
| `stores.<id>.error`, `message` | On `fetch_failed`: `TIMEOUT`, `BLOCKED`, `STORE_DOWN` or `PARSE_ERROR`. |
| `candidates` | For name queries and `includeSimilar`: other matching apps with `packageName`, `appName`, `downloads`, `similarity`. |
| `fetchedAt`, `billable` | Timestamp of the check and whether the record was charged. |

Fields a store cannot provide are `null`, never omitted. The dataset's **Overview** view flattens the per-store columns for CSV/Excel export.

### Pricing

**$0.015 per package name** checked across all selected stores. A package with a determinate answer from only one or two of four stores is charged as a partial check at $0.005; a package where every store failed is free. Invalid package names and unresolvable app names are free.

| Package names | Cost |
|---|---|
| 10 | $0.15 |
| 1,000 | $15 |
| 100,000 | $1,500 |

### Accuracy notes

Things that make naive per-store scrapers return confidently wrong answers, and how this Actor handles them:

- **Huawei AppGallery serves a different catalogue depending on the caller's IP.** From outside China the search results are the international store, where WeChat does not exist. Every Huawei request here pins the mainland catalogue (`zone=CN`), so results do not depend on where the run executes.
- **Tencent MyApp answers some unlisted foreign apps with a substitute page** for a different package instead of a 404. The Actor checks that the package on the page is the one you asked for; a substitute is reported as `not_listed`.
- **vivo's search does not surface some major apps by package name.** The Actor retries with the display names learned from the other stores before concluding `not_listed`.
- **Huawei has no package-name lookup at all.** Lookups go through display-name search and accept only an exact package match, so a look-alike never counts as your app.
- **"Not listed" and "could not check" are different values.** A blocked or changed store is reported as `fetch_failed` with an error code and is not charged — never silently as `not_listed`.

A scheduled canary run checks known apps on every store daily; a store change is detected the same day.

### Coverage and limitations

- **Stores:** Tencent MyApp (应用宝), Huawei AppGallery, Xiaomi App Store, vivo App Store. Together they cover the large majority of Chinese Android distribution. OPPO has no public web catalogue and is not included.
- **Xiaomi's web catalogue is incomplete.** Some apps that are installable from the Xiaomi store on a phone (Taobao and Xiaohongshu, for example) are absent from app.mi.com. `not_listed` for Xiaomi means "not in the public web catalogue".
- **Downloads:** Tencent MyApp and vivo publish exact counts; Huawei publishes a rounded figure (reported with `downloadsSource: "bucket"`); Xiaomi does not publish install counts on the web. `totalDownloads` therefore understates true reach and should be read as a lower bound.
- **Huawei lookups are name-based.** AppGallery has no public package-name search, so the Actor searches by the display names learned from the other stores (and by the package's last segment) and accepts only an exact package match. An app listed *only* on Huawei under an unusual name can be missed; `matchConfidence` and `storeUrl` let you verify.
- **Name queries can match look-alikes.** A wrapper called "Spotify" is not Spotify. The `summary` states which package was matched and with what confidence; check `packageName` before trusting the result.
- **Data is live**, fetched at run time with no caching. Ratings and download counts move daily.
- **Not covered:** iOS (use an App Store Actor), APK downloads, reviews, OPPO.

### FAQ

**Why isn't OPPO included?** Its web store is a download landing page without an app catalogue. Supporting it would require reverse-engineering the on-device client, which is out of scope.

**Some stores say `BLOCKED` — what now?** Lower `maxConcurrency` or enable Apify Proxy (datacenter) in `proxyConfiguration`. The stores are lenient today; if you hit limits at scale, spread runs over time.

**Can I check iOS China?** No — the iOS App Store has an official public API and other Actors cover it.

**How fresh is the data?** Every run fetches live pages. There is no cache.

**A store's page changed and results look wrong.** Open an issue on the Actor page. A daily canary run watches for layout changes; fixes typically ship within 48 hours.

### Changelog

- **1.0** — Initial release: four stores, package and name lookup, similar-app candidates, pay-per-event billing.

### Development

```bash
npm install            # also points git hooks at .githooks
npm run start:dev      # run locally with storage/key_value_stores/default/INPUT.json
npm run check          # type check + unit tests (offline, fixtures only)
npm run test:live      # canary against the real stores
npm run fixtures       # regenerate test fixtures after a store changes its markup
npm run deploy         # check → live canary → apify push → smoke run on the platform
```

`git push` runs `npm run check` through the pre-push hook. `npm run deploy` is the only path to `apify push`: it requires the live canaries to pass first, and after the push it runs the new build on the platform with a fixed input and fails if the run fails — a deploy is not done until the platform has executed it.

# Actor input Schema

## `packageNames` (type: `array`):

Android package names, e.g. com.duolingo, or Google Play URLs (https://play.google.com/store/apps/details?id=com.duolingo). Use this when you know the package. 1–1000 items. Duplicates are removed.

## `appNames` (type: `array`):

App display names, e.g. 'Duolingo' or '微信'. Use when you don't know the package name. The best match is resolved to a package name; the result includes a matchConfidence score (0–1) and the other candidates.

## `stores` (type: `array`):

Which stores to query. Omit to query all four.

## `includeSimilar` (type: `boolean`):

For app-name queries, also return other apps whose name resembles the query (potential clones / counterfeits) in a `candidates` array, without applying the match threshold.

## `assertListedEverywhere` (type: `array`):

Package names that must be reported as listed on every selected store. If any is not, the run fails with a message naming the store — use this in a scheduled canary to detect store layout changes.

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

How many package names to process in parallel. Lower this if you see BLOCKED errors.

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

Proxy to use. Chinese stores are reachable directly from most regions; enable Apify Proxy (datacenter) only if you see BLOCKED errors.

## Actor input object example

```json
{
  "packageNames": [
    "com.duolingo",
    "com.spotify.music",
    "com.tencent.mm"
  ],
  "stores": [
    "tencent_myapp",
    "huawei",
    "xiaomi",
    "vivo"
  ],
  "includeSimilar": false,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `records` (type: `string`):

One record per checked app: packageName, listedAnywhere, listedStores, totalDownloads, latestVersion, a plain-English summary, and per-store detail under stores.tencent\_myapp / huawei / xiaomi / vivo. Records for invalid package names or unresolved app names carry an `error` field instead.

## `recordsCsv` (type: `string`):

The same records as CSV with per-store columns flattened (e.g. stores.huawei.downloads), for spreadsheets and BI tools.

# 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 = {
    "packageNames": [
        "com.duolingo",
        "com.spotify.music",
        "com.tencent.mm"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("evanlin/china-android-app-store-checker").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 = {
    "packageNames": [
        "com.duolingo",
        "com.spotify.music",
        "com.tencent.mm",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("evanlin/china-android-app-store-checker").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 '{
  "packageNames": [
    "com.duolingo",
    "com.spotify.music",
    "com.tencent.mm"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call evanlin/china-android-app-store-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,evanlin/china-android-app-store-checker"
        }
    }
}
```

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/keXC6Oh1MlAKPbJIY/builds/IADlydnCF5AVUNVzq/openapi.json
