# Chrome Web Store Extension Stats Tracker - Users & Ratings (`neverempty/chrome-extension-stats-tracker`) Actor

Users, average rating, exact rating count, version, last update, category, size and Featured badge for any Chrome Web Store extension, one row each. Turn monitoring on and later runs return an extension only when its users, rating or version changed, with the previous values.

- **URL**: https://apify.com/neverempty/chrome-extension-stats-tracker.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 extension returneds

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

## Chrome Web Store Extension Stats Tracker

Give it Chrome Web Store extension URLs or IDs. It returns **one row per extension** with the numbers on the public extension page: users, average rating, the **exact rating count**, version, last update, category, size and whether the extension carries the Featured badge.

Turn on **monitoring mode** and schedule it: later runs return an extension **only when its users, rating, rating count or version changed**, together with the previous values and the differences. A day on which nothing moved produces no extension rows.

Built for extension developers who watch their own extension and their competitors: who shipped a new version, whose rating count is climbing, who crossed the next user tier.

### What you get

| Field | Example | Notes |
|---|---|---|
| `name` | `uBlock Origin Lite` | As the store names it |
| `developer` | `Raymond Hill (gorhill)` | The "Offered by" line |
| `users` | `21000000` | See "How the store reports users" below |
| `ratingAverage` | `4.5` | Rounded to 2 decimals; `null` when the extension has no ratings |
| `ratingCount` | `3641` | The exact count from the page data (the page itself shows "3.6K ratings") |
| `version` | `2026.930.1227` | |
| `lastUpdated` | `2026-09-30` | UTC date; `lastUpdatedAt` has the full timestamp |
| `category` | `Privacy & Security` | `categoryPath` has the store's own path, e.g. `make_chrome_yours/privacy` |
| `size` | `9.2MiB` | As written on the page |
| `isFeatured` | `true` | The Featured badge |
| `url` | `https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh` | The address the store redirects to |
| `extensionId` | `ddkjiahejlhfcafbddmgiahcphecmpfh` | |

Also in every row: `shortDescription`, `developerWebsite` (when the store lists one), `languagesCount`, `iconUrl`, `input` (the line you gave), `scrapedAt`.

In monitoring mode: `change` (`first-check` or `changed`), `changedFields`, `previousUsers`, `previousRatingAverage`, `previousRatingCount`, `previousVersion`, `previousCheckedAt`, `usersChange`, `ratingCountChange`, `ratingAverageChange`.

#### How the store reports users

The Chrome Web Store publishes an exact user count only below 1,000 (for example `484`). From 1,000 up it publishes a rounded figure with one or two leading digits (`20000`, `2000000`, `21000000`), and that rounded figure is what `users` contains. Nothing is estimated. A large extension can therefore show the same `users` value for weeks. `ratingCount` is exact at every size, so it is the number that moves first.

### Input

```json
{
    "extensions": [
        "https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh",
        "mnjggcdmjocbbbhaepdhchncahnbgone"
    ],
    "monitoringMode": false
}
```

- `extensions` - extension page URLs or 32-letter extension IDs. Query parameters such as `?hl=` are dropped. Old `chrome.google.com/webstore/detail/...` links are accepted: the ID is taken from them and the current address is requested. Names are not searched.
- `maxExtensions` - with monitoring off, stop after this many rows (default 100). A run reads at most 500 lines.
- `monitoringMode` - return only what changed since the previous monitoring run.
- `monitorMode` - `any-change` (default), `users-change`, `rating-change` or `version-change`.
- `resetMonitoringState` - forget the remembered figures.

### Monitoring mode

1. The first monitoring run returns every extension once (`change: "first-check"`) and remembers its figures.
2. Every later run reads each extension again and returns it only if a watched figure differs from the remembered one (`change: "changed"`, with `changedFields` and the previous values).
3. When nothing changed, the dataset holds a single `no-change` row that is not charged.

Figures are remembered per extension on your account, not per list, so adding an extension to the list returns only the new one. The remembered figure of an extension is updated when that extension is returned, so a difference is always measured from the last row you received. Do not run two schedules with the same extension at the same moment: the two runs can overwrite each other's memory.

### Pricing

| Event | When |
|---|---|
| `extension-returned` | Each extension row in the dataset |
| `extension-checked` | Monitoring mode only: each extension that was read and compared |

With monitoring off you pay only for extension rows. In monitoring mode every extension that was read costs one check, and an extension that is returned also costs one row. Rows that explain why something was not returned are never charged: `no-such-extension`, `invalid-input`, `duplicate-input`, `unreadable`, `blocked`, `disallowed-by-robots`, `robots-unreadable`, `no-change`, `not-read`, `budget-reached`.

If the maximum total charge of a run cannot cover one more check plus its row, the run stops before reading that extension and names the lines it did not read.

### What it does not do

- **No search and no reviews.** The store's robots.txt disallows its search pages and the review pages, so this Actor reads only the extension page. It reads robots.txt at the start of every run and checks each address against it; if robots.txt cannot be read, nothing is requested.
- **No developer e-mail addresses.**
- **No proxy and no workarounds.** Requests go out directly, two seconds apart, with a User-Agent that names this Actor. If the store refuses a request or answers with a verification page, the run stops and lists what it did not read.
- **Removed and non-existent extensions look the same.** For an ID that does not exist and for an extension that was taken down, the store serves the same empty page, so both come back as `no-such-extension`.

### Notes

- The store sends an unknown or removed ID to an "empty" page with HTTP 200. On the page of a removed extension it also suggests other extensions; those figures are never read. A row is produced only when the page data starts with the ID you asked for.
- `lastUpdated` is the UTC date of the update timestamp; the store shows the date in the viewer's own time zone, which can be one day off.
- `category` is the category name as the English page writes it. If the page does not show it, `category` is `null` and `categoryPath` still carries the store's path.
- An extension given only by ID takes two requests the first time (the store redirects to the full address); in monitoring mode the address is remembered.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `extensions` (type: `array`):

Chrome Web Store extension URLs (https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh) or the 32-letter extension IDs. Only the extension page itself is requested: query parameters are dropped, and a link to the reviews or another sub-page is rejected rather than guessed. An ID the store has no page for comes back as a "no-such-extension" row that is not charged. Extension names are not searched: paste the URL or the ID.

## `maxExtensions` (type: `integer`):

With monitoring off, the run stops after this many extension rows and lists the lines it did not read in a row that is not charged. Monitoring mode ignores this and checks every extension you listed. A run reads at most 500 lines.

## `monitoringMode` (type: `boolean`):

Off = return every extension you listed, charged per extension row. On = the figures are remembered and later runs return an extension only when its users, average rating, rating count or version differ from the remembered values, together with the previous values and the differences. In monitoring mode each extension that was read is charged as one check (extension-checked), and an extension that is returned is also charged as one extension row (extension-returned). The first monitoring run returns every extension once. Figures are remembered per extension, not per list, so adding an extension does not return the others again. Do not put the same extension in two schedules that run at the same moment.

## `monitorMode` (type: `string`):

Any change = the extension is returned when users, average rating, rating count or version differ from the remembered values. The other options watch only one of them. The store rounds user counts of 1,000 and more, so a large extension can keep the same user count for weeks while its exact rating count moves.

## `resetMonitoringState` (type: `boolean`):

Clears every remembered figure for this Actor on your account, so the next monitoring run returns each extension once again. This affects all your monitoring runs, because figures are remembered per extension rather than per list.

## Actor input object example

```json
{
  "extensions": [
    "https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh",
    "mnjggcdmjocbbbhaepdhchncahnbgone"
  ],
  "maxExtensions": 100,
  "monitoringMode": false,
  "monitorMode": "any-change",
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per Chrome Web Store extension: name, developer, users, average rating, rating count, version, last update, category, size, Featured badge, URL and, in monitoring mode, the previous values and the differences. Extension IDs the store has no page for, lines that are not extension links, pages that could not be read and runs where nothing changed come back as their own rows and are not charged.

# 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 = {
    "extensions": [
        "https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh",
        "mnjggcdmjocbbbhaepdhchncahnbgone"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/chrome-extension-stats-tracker").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 = { "extensions": [
        "https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh",
        "mnjggcdmjocbbbhaepdhchncahnbgone",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/chrome-extension-stats-tracker").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 '{
  "extensions": [
    "https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh",
    "mnjggcdmjocbbbhaepdhchncahnbgone"
  ]
}' |
apify call neverempty/chrome-extension-stats-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/chrome-extension-stats-tracker"
        }
    }
}
```

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/ZACghxbiu8HLoSuBl/builds/jaDSPsuVAm3A1B7bX/openapi.json
