npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

google-play-scraper-ts

v0.2.0

Published

TypeScript rewrite of google-play-scraper with throttling, retries, and strong typing.

Readme

google-play-scraper-ts

npm version npm downloads

Fully typed Node.js client for Google Play Store metadata inspired by google-play-scraper. The rewrite keeps the familiar API while adding:

  • TypeScript-first types and emitted declaration files
  • Fixtures & offline tests (no live traffic unless LIVE=1)
  • Retries + memoization out of the box
  • Dual CJS/ESM builds with verified parity

Installation

npm install google-play-scraper-ts

Requires Node.js 18 or newer.

Quick start

CommonJS:

const gplay = require('google-play-scraper-ts').default;

const app = await gplay.app({ appId: 'com.spotify.music' });
console.log(app.title, app.score);

ESM / TypeScript:

import gplay, { constants } from 'google-play-scraper-ts';

const topFreeMusic = await gplay.list({
  collection: constants.collection.TOP_FREE,
  category: constants.category.MUSIC_AND_AUDIO,
  num: 10,
});

Common conventions

  • lang controls the Play Store UI language (default 'en').
  • country controls geotargeting and pricing (default 'us').
  • Methods that accept requestOptions forward requestOptions.headers to the underlying HTTP client so you can inject cookies or additional headers (see per-method notes—suggest currently ignores the option for API parity).
  • Every function returns a Promise; runtime errors (invalid inputs, store shape changes, HTTP failures) surface as rejected promises.

Proxy routing

  • The HTTP client can route calls through HTTP(S) proxies keyed by country (ISO alpha-2, case-insensitive). If no per-country match is found, the optional default proxy is used instead of a direct connection.
  • Configure proxies once via configureProxies(settings) or construct an isolated client with createPlayStoreApi({ proxies }). All methods automatically opt into proxy usage—the country argument (or gl query param) determines the lookup.
  • HTTPS proxies are supported; pass protocol: 'https' or use an https:// URL. To work with debugging gateways that present expired/self-signed certificates, set rejectUnauthorized: false (defaults to true).
import { createPlayStoreApi } from 'google-play-scraper-ts';

const gplay = createPlayStoreApi({
  proxies: {
    default: { host: 'proxy.example', port: 8080 },
    perCountry: {
      us: { host: 'us-proxy.internal', port: 8080, auth: { username: 'user', password: 'secret' } },
      fr: { url: 'http://fr-proxy.internal:8080' },
      br: { url: 'https://br-proxy.internal:10443', rejectUnauthorized: false },
    },
  },
});

const result = await gplay.app({ appId: 'com.spotify.music', country: 'us' });

Already instantiated clients can be updated in-place with configureProxies(newSettings); pass null or omit entries to clear previously configured proxies.

API reference

All methods are asynchronous and resolve to typed results. The library mirrors the original google-play-scraper surface while enriching the responses with strict TypeScript types.

Shared types

AppDetails

Identification & copy

| Field | Type | Description | | --- | --- | --- | | appId | string | Requested package identifier. | | url | string | Canonical details URL including hl/gl query params. | | title | string | App title localized to lang. | | summary | string | Short marketing blurb. | | description | string | Plain-text description (HTML stripped). | | descriptionHTML | string | Raw HTML description from the store. | | genre | string \| undefined | Primary genre label. | | genreId | string \| undefined | Primary genre identifier. | | categories | Array<{ name: string; id: string }> | All category tags discovered in the payload. |

Ratings & installs

| Field | Type | Description | | --- | --- | --- | | installs | string \| undefined | Play Store installs text (e.g. '10,000,000+'). | | minInstalls | number \| undefined | Minimum installs inferred by the store. | | maxInstalls | number \| undefined | Maximum installs inferred by the store. | | score | number \| undefined | Average rating (0–5). | | scoreText | string \| undefined | Rating text shown in the UI. | | ratings | number \| undefined | Total number of ratings. | | reviews | number \| undefined | Total number of textual reviews. | | histogram | Record<1 \| 2 \| 3 \| 4 \| 5, number> | Star histogram counts (missing buckets default to 0). |

Pricing & monetisation

| Field | Type | Description | | --- | --- | --- | | free | boolean | true when the app can be installed without payment. | | price | number | Current price in the store currency (e.g. 0, 3.99). | | originalPrice | number \| undefined | Pre-discount price in the store currency, when available. | | discountEndDate | number \| undefined | Unix timestamp (ms) for the promotion end, if provided. | | currency | string \| undefined | ISO-4217 currency code. | | priceText | string | Human readable price (falls back to 'Free'). | | offersIAP | boolean | Whether the listing advertises in-app purchases. | | IAPRange | string \| undefined | Raw in-app purchase price range from the store. | | isAvailableInPlayPass | boolean | Indicates Google Play Pass availability. |

Media & merchandising

| Field | Type | Description | | --- | --- | --- | | icon | string \| undefined | Square icon URL. | | headerImage | string \| undefined | Feature graphic URL. | | screenshots | string[] | Screenshot URLs (order preserved). | | video | string \| undefined | Promotional video URL. | | videoImage | string \| undefined | Poster image for the promo video. | | previewVideo | string \| undefined | Short autoplay preview clip URL. | | comments | string[] | Up to five highlighted user quotes. | | recentChanges | string \| undefined | “What’s new” text block. |

Developer & policies

| Field | Type | Description | | --- | --- | --- | | developer | string \| undefined | Developer display name. | | developerId | string \| undefined | Developer identifier extracted from profile link. | | developerInternalID | string \| undefined | Internal developer ID used by Play. | | developerEmail | string \| undefined | Contact email. | | developerWebsite | string \| undefined | Website URL. | | developerAddress | string \| undefined | Mailing address. | | developerLegalName | string \| undefined | Legal entity name. | | developerLegalEmail | string \| undefined | Legal contact email. | | developerLegalAddress | string \| undefined | Legal contact address (line breaks normalised). | | developerLegalPhoneNumber | string \| undefined | Legal contact phone number. | | privacyPolicy | string \| undefined | Privacy policy URL. |

Platform & release information

| Field | Type | Description | | --- | --- | --- | | released | string \| undefined | Store-published release date text. | | updated | number \| undefined | Last update timestamp in ms since epoch. | | version | string | Latest version string (falls back to 'VARY'). | | androidVersion | string | Minimum Android version, normalised (e.g. '5.0' or 'VARY'). | | androidVersionText | string | Raw minimum version text from the listing. | | androidMaxVersion | string \| undefined | Maximum supported Android version if provided. | | contentRating | string \| undefined | Content rating label (e.g. 'Teen'). | | contentRatingDescription | string \| undefined | Additional content rating guidance. | | adSupported | boolean | true if the listing discloses ads. | | available | boolean | Indicates overall availability in the requested country. | | preregister | boolean | true when the app is only available for preregistration. | | earlyAccessEnabled | boolean | true for early access / beta listings. |

AppSummary

Returned by list, search, developer and similar methods when fullDetail is false.

| Field | Type | Description | | --- | --- | --- | | appId | string \| undefined | Package identifier. | | title | string \| undefined | App title. | | url | string \| undefined | Absolute Play Store URL. | | icon | string \| undefined | Icon URL. | | developer | string \| undefined | Developer display name. | | developerId | string \| undefined | Developer profile identifier (when exposed). | | summary | string \| undefined | Short synopsis. | | price | number \| undefined | Current price in the store currency (0 for free apps). | | priceText | string \| undefined | Price label ('FREE', '$0.99', …). | | currency | string \| undefined | Currency code. | | free | boolean \| undefined | true when no payment required. | | score | number \| undefined | Average rating. | | scoreText | string \| undefined | Rating text. |

Review

| Field | Type | Description | | --- | --- | --- | | id | string \| undefined | Review identifier used by the Play Store. | | userName | string \| undefined | Reviewer display name. | | userImage | string \| undefined | Avatar image URL. | | date | string \| null | ISO string of the review timestamp (null if unavailable). | | score | number \| undefined | Star rating (1–5). | | scoreText | string \| null \| undefined | Rating represented as text. | | url | string \| undefined | Deep link back to the review on Play. | | title | null | Placeholder (Play no longer serves review titles). | | text | string \| undefined | Review body. | | replyDate | string \| null | ISO timestamp of the developer reply. | | replyText | string \| null | Developer reply content. | | version | string \| null | App version cited in the review. | | thumbsUp | number \| undefined | Helpfulness vote count. | | criterias | Array<{ criteria: unknown \| null; rating: unknown \| null }> \| undefined | Per-criteria ratings when supplied by Play (e.g. for games). |

PermissionItem

| Field | Type | Description | | --- | --- | --- | | permission | string | Permission string (e.g. 'CAMERA'). | | type | 0 \| 1 | Section identifier (0 = constants.permission.COMMON, 1 = constants.permission.OTHER). |

DataSafetyItem

| Field | Type | Description | | --- | --- | --- | | data | string \| undefined | Data point name (e.g. 'email'). | | optional | boolean \| null \| undefined | Whether the data is optional (null when Google has no flag). | | purpose | string \| null \| undefined | Usage purpose label (e.g. 'ads'). | | type | string \| undefined | Category grouping (e.g. 'Personal info'). |

app(options): Promise<AppDetails>

Fetch detailed information about a single application.

| Option | Type | Default | Description | | --- | --- | --- | --- | | appId | string | – | Required package identifier (com.spotify.music). | | lang | string | 'en' | Store UI language. | | country | string | 'us' | Store country / pricing market. | | requestOptions | { headers?: Record<string, string> } | – | Extra HTTP headers passed to Play (cookies, locale overrides, …). |

Resolves to an AppDetails object as documented above.

list(options): Promise<AppSummary[] \| AppDetails[]>

Retrieve curated charts (Top Free, Top Paid, Grossing, …). Set fullDetail to fetch the richer AppDetails payload for each entry.

| Option | Type | Default | Description | | --- | --- | --- | --- | | collection | constants.collection | constants.collection.TOP_FREE | Chart to fetch. | | category | constants.category | constants.category.APPLICATION | App category filter. | | age | constants.age \| string \| undefined | – | Optional age-range filter. | | lang | string | 'en' | Locale for metadata. | | country | string | 'us' | Region / storefront. | | num | number | 500 | Maximum entries (Play caps at ~500). | | fullDetail | boolean | false | When true, fetch each item with app(). |

Returns an array of AppSummary entries, or AppDetails entries when fullDetail is enabled.

search(options): Promise<AppSummary[] \| AppDetails[]>

Search the Play Store catalogue.

| Option | Type | Default | Description | | --- | --- | --- | --- | | term | string | – | Search query (required). | | lang | string | 'en' | Metadata language. | | country | string | 'us' | Storefront country. | | num | number | 20 | Maximum results (hard limit 250). | | fullDetail | boolean | false | When true, hydrate each match via app(). | | price | 'all' \| 'free' \| 'paid' | 'all' | Price filter. | | requestOptions | { headers?: Record<string, string> } | – | Forwarded headers for the search request. |

Results mirror the modern /store/search layout (including single-result pages). When fullDetail is enabled each result is re-fetched via app().

Need the legacy “global” feed? Use searchGlobal(options) to call the former /work/search endpoint. Its options match search(), but expect geo-neutral results unless you route traffic through a country proxy.

searchGlobal(options): Promise<AppSummary[] \| AppDetails[]>

Back-compat wrapper around the legacy /work/search flow. Accepts the same options as search() and returns identical payload shapes. Pagination still uses the shared batchexecute endpoint, but the initial page reflects Google’s global results regardless of the country parameter.

suggest(options): Promise<string[]>

Fetch autocomplete suggestions from Play.

| Option | Type | Default | Description | | --- | --- | --- | --- | | term | string | – | Partial query (required). | | lang | string | 'en' | Suggestion language. | | country | string | 'us' | Storefront country. | | requestOptions | { headers?: Record<string, string> } | – | Reserved for API compatibility (currently ignored). |

Resolves to an array of suggestion strings ordered by relevance.

developer(options): Promise<AppSummary[] \| AppDetails[]>

List applications published by a developer profile.

| Option | Type | Default | Description | | --- | --- | --- | --- | | devId | string | – | Developer identifier (numeric or slug). | | lang | string | 'en' | Metadata language. | | country | string | 'us' | Storefront country. | | num | number | 60 | Maximum apps to collect. | | fullDetail | boolean | false | Set to true for AppDetails results. |

Returns developer listings as AppSummary entries, or full details when fullDetail is enabled.

reviews(options): Promise<{ data: Review[]; nextPaginationToken: string \| null }>

Pull paginated user reviews for a given app.

| Option | Type | Default | Description | | --- | --- | --- | --- | | appId | string | – | Target package identifier. | | sort | constants.sort | constants.sort.NEWEST | Sorting mode (NEWEST, RATING, HELPFULNESS). | | lang | string | 'en' | Review language. | | country | string | 'us' | Locale used for review localisation. | | num | number | 150 | Maximum reviews to gather before stopping. | | paginate | boolean | false | When false, auto-follow tokens until num reviews or exhaust data. | | nextPaginationToken | string \| null | null | Pass a token from a previous response to resume manually. |

When paginate is true, the method fetches a single page and returns the next token (if any). The data array contains Review entries.

similar(options): Promise<AppSummary[] \| AppDetails[]>

Retrieve apps from the “Similar” / “Related” clusters for a given listing.

| Option | Type | Default | Description | | --- | --- | --- | --- | | appId | string | – | Reference package identifier. | | lang | string | 'en' | Metadata language. | | country | string | 'us' | Storefront country. | | fullDetail | boolean | false | Hydrate each result via app() when true. |

Returns up to ~60 related apps as AppSummary or AppDetails entries.

permissions(options): Promise<string[] \| PermissionItem[]>

Inspect runtime permissions declared in the Play listing.

| Option | Type | Default | Description | | --- | --- | --- | --- | | appId | string | – | Target package identifier. | | short | boolean | false | When true, return a flat list of permission strings. Otherwise return PermissionItem objects grouped by type. | | lang | string | 'en' | Language for permission descriptions (when available). | | country | string | 'us' | Storefront country. |

datasafety(options): Promise<{ sharedData: DataSafetyItem[]; collectedData: DataSafetyItem[]; securityPractices: Array<{ practice?: string; description?: string }>; privacyPolicyUrl?: string }>

Fetch the Google Play “Data safety” disclosure for an app.

| Option | Type | Default | Description | | --- | --- | --- | --- | | appId | string | – | Target package identifier. | | lang | string | 'en' | Disclosure language. |

The result contains separate sharedData and collectedData arrays of DataSafetyItems, a list of high-level securityPractices, and an optional privacyPolicyUrl.

categories(): Promise<string[]>

Scrape the public category directory and return category identifiers (e.g. 'APPLICATION', 'GAME_TRIVIA').

memoized(options?: { maxAge?: number; max?: number }): PlayStoreApi

Wrap the API with an in-memory cache.

| Option | Type | Default | Description | | --- | --- | --- | --- | | maxAge | number | 300_000 | Cache TTL in milliseconds. | | max | number | 1_000 | Maximum cache size; oldest entries are evicted first. |

The returned object has the same surface as the default export (including a memoized method) but memoises every call.

Constants helper

The default export re-exports structured enums under constants for convenience:

  • constants.collectionTOP_FREE, TOP_PAID, GROSSING.
  • constants.category – All Play categories (apps, games, family variants, etc.).
  • constants.sort – Review sort orders (NEWEST, RATING, HELPFULNESS).
  • constants.age – Age-range filters used by the family charts.
  • constants.permission – Permission buckets (COMMON, OTHER).
  • constants.clusters – Internal cluster identifiers used by list parsing utilities.

Releases

  • Preflight checks before releasing:
    • npm run release:preflight (lint, tests, type verification)
  • Publish (requires npm login and 2FA):
    • Patch: npm run release:patch
    • Minor: npm run release:minor
    • Major: npm run release:major
  • Notes
    • prepack builds automatically before pack/publish.
    • For scoped packages (e.g. @your-scope/google-play-scraper-ts), publish with --access public (already included in release scripts).
    • To validate after publish: install in a clean folder and require/import both CJS/ESM entries.

Debugging & Fixtures

  • Verbose debugging for similar() resolution:
    • Set GP_DEBUG=1 to print minimal breadcrumbs when the service-request cluster id is missing (e.g., container keys available).
  • UI tests (manual exploration):
    • Build the library: npm run build
    • Start the UI server: LIVE=1 node ui-tests/server.js
    • Open the playground and use the forms to call each method.
  • Record live fixtures for similar() (optional):
    • LIVE=1 npm run build && node scripts/record-similar-fixture.js --appId com.spotify.music --lang en --country us --out tests/fixtures/similar
    • Re-run tests with fixture replay: USE_FIXTURE=1 npm test
  • constants.sort – Review sort order values
  • constants.age, constants.permission, plus other helpers

Retries & throttling

  • Retries – The internal HTTP client retries 5xx responses with exponential backoff (configurable via helper APIs).
  • Caching – Use memoized() to deduplicate hot requests. For full control, combine with your own throttling or caching layer.

TypeScript support

  • Published package includes type declarations generated from the TypeScript sources.
  • npm run verify:types compiles tests/types/usage.ts against the generated .d.ts to guarantee correctness.

Running the docs & tests

npm run lint
LIVE=1 npm test    # only needed when you want to exercise live tests (default suite uses fixtures)
npm run test        # builds both ESM and CJS bundles before running mocha
npm run docs        # generates TypeDoc output in /docs
npm run verify:types

By default the test suite blocks outbound HTTP requests. Set LIVE=1 to allow network calls when you want to run ad-hoc live checks.

Manual UI testing playground

Looking for the interactive UI harness? It now lives in a standalone repository with prebuilt assets and GitHub Pages hosting:

The hosted version always targets the latest npm release of this package. It includes the search-mode toggle (modern /store/search vs. legacy /work/search) and the enhanced proxy configuration panel (per-country entries, auth, TLS validation overrides), plus a direct Play Store link for each query so you can inspect the underlying page.

Contributing

Issues and pull requests are welcome! Please review AGENTS.md for repository conventions (linting, commit style, release workflow).