# npm & PyPI Package Stats Scraper: Downloads & Trends (`mersetti/package-registry-stats`) Actor

Get metadata and download statistics for npm and PyPI packages: daily/weekly/monthly downloads, week-over-week growth, daily history, versions, release dates, license, repository and dependencies. Search npm by keyword or author and monitor download changes between runs.

- **URL**: https://apify.com/mersetti/package-registry-stats.md
- **Developed by:** [Max Prozapas](https://apify.com/mersetti) (community)
- **Categories:** Developer tools, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## npm & PyPI Package Stats — Downloads, Versions & Trends

Get **download statistics and metadata for any npm or PyPI package** in one clean dataset: daily, weekly and monthly
downloads, **week-over-week growth**, optional **daily download history**, latest version, number of versions,
first and latest release dates, days since the last release, license, homepage, GitHub repository, keywords,
author, maintainers count and number of dependencies.

Paste package names or URLs (`react`, `@apify/log`, `https://pypi.org/project/requests/`), or **discover npm packages
by keyword, author or maintainer** with npm search. Turn on **monitor mode** to see how downloads and versions changed
since the previous run. No API key, no login, no browser.

### Why this package stats scraper

- **npm and PyPI in one run**: JavaScript/Node.js and Python packages side by side, with the same output fields.
- **Download numbers that matter**: last day, last week, last month, previous week and week-over-week growth in %.
- **Daily download history** (up to 365 days for npm, ~180 days for PyPI) for charts and trend analysis.
- **Release health**: versions count, first release, latest release, days since last release, deprecated flag.
- **Discovery**: npm search with `keywords:`, `author:` and `maintainer:` qualifiers, up to 1,000 packages.
- **Monitoring**: change in weekly downloads and new releases since the last run. Filter out small changes.
- **Scoped packages** (`@scope/name`), package URLs and version suffixes (`react@18`, `requests==2.31`) just work.
- **Official public sources**: registry.npmjs.org, api.npmjs.org, pypi.org JSON API and pypistats.org.
- **Polite and robust**: bulk requests (128 npm packages per call), low concurrency, retries with backoff on 429,
  respects the pypistats.org rate limit. One failing package never stops the run.

### Use cases

- **Developer tool market research**: size a market by the downloads of every library in a category
  (`keywords:orm`, `keywords:testing`, `keywords:llm`), find the leaders and the fastest-growing challengers.
- **Competitor tracking for open source and devtools companies**: follow your SDK against competitors' SDKs every
  week, catch their new releases and adoption spikes.
- **VC, investor and dev-rel dashboards**: feed weekly downloads and growth of portfolio or target projects into
  Google Sheets, Looker Studio, Notion or your data warehouse.
- **Dependency due diligence**: before adopting a package, check whether it is maintained (days since release,
  versions, maintainers), deprecated, how popular it is and how many dependencies it pulls in.
- **AI agents and LLM tools**: give an agent structured, fresh package data (via the Apify API or MCP) to answer
  "which library should I use?" or to enrich code-generation and research workflows.

### Input

| Field | Description |
|---|---|
| `registry` | Default registry for bare names: `npm` (default) or `pypi`. URLs and `npm:` / `pypi:` prefixes override it, so one run can mix both. |
| `packages` | Package names or URLs, e.g. `react`, `@apify/log`, `https://www.npmjs.com/package/express`, `requests`, `https://pypi.org/project/numpy/`, `pypi:flask`. |
| `searchQuery` | npm search text with optional qualifiers: `keywords:scraper`, `author:sindresorhus`, `maintainer:apify`. Found packages are added to `packages`. npm only. |
| `maxSearchResults` | Max packages taken from the search (default 50, up to 1,000). |
| `includeHistory` | Add `dailyDownloads` (array of `{ date, downloads }`). |
| `historyDays` | Days of history (default 30, max 365; PyPI keeps about 180 days). |
| `monitorMode` | Add changes since the previous run (weekly downloads, new release). |
| `monitorId` | Separate saved state per watchlist (default `default`). |
| `minChangePercent` | Monitor mode: output only packages whose weekly downloads changed by at least this % or that have a new release (default 0 = all). |

#### Example input: compare frameworks across npm and PyPI

```json
{
  "registry": "npm",
  "packages": ["react", "vue", "@apify/log", "pypi:requests", "pypi:numpy"],
  "includeHistory": true,
  "historyDays": 30
}
```

#### Example input: market map of an npm category

```json
{
  "searchQuery": "keywords:scraper",
  "maxSearchResults": 200
}
```

#### Example input: weekly competitor watch

```json
{
  "registry": "pypi",
  "packages": ["langchain", "llama-index", "haystack-ai", "dspy"],
  "monitorMode": true,
  "monitorId": "llm-frameworks",
  "minChangePercent": 10
}
```

### Output

One row per package. Real example output (PyPI, with `includeHistory: true, historyDays: 7`):

```json
{
  "registry": "pypi",
  "name": "requests",
  "url": "https://pypi.org/project/requests/",
  "description": "Python HTTP for Humans.",
  "latestVersion": "2.34.2",
  "license": "Apache-2.0",
  "homepage": null,
  "repositoryUrl": "https://github.com/psf/requests",
  "keywords": [],
  "author": "Kenneth Reitz",
  "maintainersCount": 3,
  "organization": null,
  "versionsCount": 160,
  "firstReleaseAt": "2011-02-14T08:49:42.641Z",
  "latestReleaseAt": "2026-05-14T19:25:26.443Z",
  "daysSinceLastRelease": 152,
  "dependenciesCount": 4,
  "deprecated": null,
  "requiresNode": null,
  "requiresPython": ">=3.10",
  "downloadsLastDay": 51084441,
  "downloadsLastWeek": 310931263,
  "downloadsLastMonth": 1202777296,
  "downloadsPreviousWeek": 304164213,
  "weeklyGrowthPercent": 2.2,
  "downloadsPeriodEnd": "2026-10-13",
  "downloadsSource": "pypistats.org",
  "dailyDownloads": [
    { "date": "2026-10-07", "downloads": 49420957 },
    { "date": "2026-10-08", "downloads": 50831095 },
    { "date": "2026-10-09", "downloads": 48758092 },
    { "date": "2026-10-10", "downloads": 32720021 },
    { "date": "2026-10-11", "downloads": 32364605 },
    { "date": "2026-10-12", "downloads": 45752052 },
    { "date": "2026-10-13", "downloads": 51084441 }
  ],
  "downloadsWarning": null,
  "input": "pypi:requests",
  "scrapedAt": "2026-10-14T10:31:08.523Z"
}
```

Real example output (npm, growth and history fields omitted for brevity):

```json
{
  "registry": "npm",
  "name": "vue",
  "url": "https://www.npmjs.com/package/vue",
  "description": "The progressive JavaScript framework for building modern web UI.",
  "latestVersion": "3.5.44",
  "license": "MIT",
  "homepage": "https://vuejs.org/",
  "repositoryUrl": "https://github.com/vuejs/core",
  "keywords": ["vue"],
  "author": "Evan You",
  "maintainersCount": 2,
  "versionsCount": 597,
  "firstReleaseAt": "2013-12-07T06:09:46.299Z",
  "latestReleaseAt": "2026-10-13T07:45:32.806Z",
  "daysSinceLastRelease": 1,
  "dependenciesCount": 5,
  "deprecated": null,
  "organization": null,
  "requiresNode": null,
  "requiresPython": null,
  "downloadsLastDay": 3372286,
  "downloadsLastWeek": 17643787,
  "downloadsLastMonth": 75864263,
  "downloadsPeriodEnd": "2026-10-12",
  "downloadsSource": "api.npmjs.org",
  "input": "vue",
  "scrapedAt": "2026-10-14T10:31:08.523Z"
}
```

Field notes:

- `weeklyGrowthPercent` compares the last 7 days with the 7 days before (from daily data). `downloadsPreviousWeek` is
  the previous week's total.
- `dependenciesCount` counts runtime dependencies of the latest version (npm `dependencies`; PyPI `requires_dist`
  without optional extras).
- `maintainersCount` is a count only. **No e-mail addresses are collected.** `author` is a name only.
- `organization` is the npm scope (`@apify/log` → `apify`) or the PyPI organization that owns the project.
- `deprecated` holds npm's deprecation message (or the PyPI yank reason) of the latest version.
- `downloadsSource` tells where the numbers come from: `api.npmjs.org`, `pypistats.org`, or `npm search index`
  (a fallback used when the npm downloads API is unavailable; it provides weekly and monthly totals only).
- `downloadsWarning` explains missing numbers, e.g. a temporary outage of the downloads API.
- Search results also have `dependents` and `searchScore` from npm search.

In monitor mode every row additionally has:

```json
{
  "firstSeenInMonitor": false,
  "previousCheckedAt": "2026-10-14T10:19:15.237Z",
  "previousLatestVersion": "2.5.4",
  "newRelease": false,
  "previousDownloadsLastWeek": 100000000,
  "downloadsLastWeekChange": 115951378,
  "downloadsLastWeekChangePercent": 116,
  "previousDownloadsLastMonth": 802155590,
  "downloadsLastMonthChange": 0,
  "downloadsLastMonthChangePercent": 0
}
```

A per-package report (output or not, errors such as `Package not found on npm`, warnings) and the npm search totals
are saved to the `SUMMARY` record of the run's key-value store.

### How monitoring works

- State is saved in the named key-value store `package-stats-monitor` under `state-<monitorId>`.
- The first run for a package outputs it with `firstSeenInMonitor: true` and empty "previous" fields.
- Later runs compare with the **last reported** values. Packages skipped by `minChangePercent` keep their old
  baseline, so slow but steady growth is reported once it adds up to the threshold.
- A new latest version is always reported (`newRelease: true`), regardless of `minChangePercent`.
- If the run's cost limit is reached, unreported packages are compared again in the next run.

### Pricing

**Pay per package**: you pay a small fee for each package row in the dataset (event `package`). Packages that do not
exist, cannot be parsed, or are skipped by `minChangePercent` in monitor mode are free. Set a maximum cost per run and
the Actor stops cleanly when it is reached.

### FAQ

**Where does the data come from?**
From the official public APIs: the npm registry (`registry.npmjs.org`), npm download counts (`api.npmjs.org`),
npm search, the PyPI JSON API (`pypi.org/pypi/<name>/json`) and pypistats.org (PyPI download counts from the public
BigQuery dataset, without mirrors).

**Why are npm and PyPI download numbers not directly comparable?**
They count different things. npm counts tarball downloads from the npm registry (including CI installs). PyPI numbers
come from pypistats.org and exclude known mirrors. Use them to compare packages within the same ecosystem and to
follow trends over time.

**Can I get downloads for a specific date range?**
Turn on `includeHistory` and set `historyDays` (up to 365 for npm, about 180 for PyPI). You get one value per day in
`dailyDownloads` and can aggregate it any way you need.

**Does it work with scoped npm packages?**
Yes. `@scope/name`, `https://www.npmjs.com/package/@scope/name` and `@scope/name@1.2.3` are all supported. Scoped
packages are queried one by one (the npm downloads API does not support them in bulk), unscoped ones in bulk.

**Can I search PyPI?**
PyPI has no public search API, so `searchQuery` works for npm only. For PyPI, pass package names or URLs.

**Why is `downloadsLastDay` or `weeklyGrowthPercent` sometimes null?**
The npm downloads API occasionally has outages. The Actor then falls back to npm search totals (weekly and monthly)
and explains the gap in `downloadsWarning`. Very new packages also have less than two weeks of data.

**How fresh are the numbers?**
npm and pypistats.org publish download counts once per day, usually for the previous UTC day.
`downloadsPeriodEnd` shows the last day included.

### Limitations

- Download counts are published daily by the registries, not in real time.
- PyPI daily history is limited to what pypistats.org keeps (about 180 days).
- pypistats.org allows about 60 requests per minute, so PyPI runs process about 25-30 packages per minute.
- Private packages and private registries are not supported.

### Legal

The Actor reads only public data from the official npm and PyPI APIs and pypistats.org, the same data shown on the
package pages. It does not log in or bypass any protection, and it does not collect personal data such as e-mail
addresses (maintainers are counted, authors are reported by name only, as published by the package owner). Package
names, descriptions and other metadata belong to their authors. Make sure your use of the data complies with
applicable laws and the terms of npm and PyPI.

### Related tools

- [Career Sites Jobs Feed (11 ATS)](https://apify.com/mersetti/ats-jobs-feed)
- [Substack to Markdown for AI & RAG](https://apify.com/mersetti/substack-to-markdown)
- [Shopify Store Products & Price Change Monitor](https://apify.com/mersetti/shopify-products-monitor)

# Actor input Schema

## `registry` (type: `string`):

Default registry for package names without a prefix. URLs (npmjs.com, pypi.org) and prefixes ("npm:react", "pypi:requests") override it, so one run can mix both registries.

## `packages` (type: `array`):

Package names or URLs. npm: "react", "@apify/log" (scoped), "https://www.npmjs.com/package/express". PyPI: "requests", "numpy", "https://pypi.org/project/flask/", or "pypi:requests" in an npm run. Version suffixes ("react@18", "requests==2.31") are ignored.

## `searchQuery` (type: `string`):

Discover npm packages by text, with optional qualifiers: "keywords:scraper", "author:sindresorhus", "maintainer:apify", "web scraping". Found packages are added to the list above. npm only (PyPI has no public search API).

## `maxSearchResults` (type: `integer`):

Maximum number of packages taken from the npm search, best matches first.

## `includeHistory` (type: `boolean`):

Adds dailyDownloads: an array of { date, downloads } for the last N days (see below).

## `historyDays` (type: `integer`):

Number of days in dailyDownloads. npm: up to 365 days. PyPI: pypistats.org keeps about 180 days.

## `monitorMode` (type: `boolean`):

Remembers the downloads and latest version of every package between runs and adds the change since the previous run: previousDownloadsLastWeek, downloadsLastWeekChange, downloadsLastWeekChangePercent, previousLatestVersion and newRelease. Schedule the Actor (e.g. weekly) to track adoption trends.

## `monitorId` (type: `string`):

Name of the saved state. Use different IDs for different watchlists.

## `minChangePercent` (type: `integer`):

Monitor mode only. Skip packages whose weekly downloads changed less than this percentage since the last reported run and that have no new release. Skipped packages are not charged. 0 = output every package.

## Actor input object example

```json
{
  "registry": "npm",
  "packages": [
    "react",
    "vue",
    "@apify/log",
    "pypi:requests",
    "pypi:numpy"
  ],
  "maxSearchResults": 50,
  "includeHistory": false,
  "historyDays": 30,
  "monitorMode": false,
  "monitorId": "default",
  "minChangePercent": 0
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "packages": [
        "react",
        "vue",
        "@apify/log",
        "pypi:requests",
        "pypi:numpy"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mersetti/package-registry-stats").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 = { "packages": [
        "react",
        "vue",
        "@apify/log",
        "pypi:requests",
        "pypi:numpy",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("mersetti/package-registry-stats").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 '{
  "packages": [
    "react",
    "vue",
    "@apify/log",
    "pypi:requests",
    "pypi:numpy"
  ]
}' |
apify call mersetti/package-registry-stats --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mersetti/package-registry-stats"
        }
    }
}
```

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/8fcuadiofcTkDeSiq/builds/H688dQVEljMVeA806/openapi.json
