# npm and PyPI Package Stats (`vizellie/package-stats-npm-pypi`) Actor

Download counts, latest version, release dates and release cadence for npm and PyPI packages. One row per package, with a daily download series for the dates you choose and an optional comparison with the previous period. $0.001 per package.

- **URL**: https://apify.com/vizellie/package-stats-npm-pypi.md
- **Developed by:** [Vi Zellie](https://apify.com/vizellie) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 package stats rows

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 and PyPI Package Stats

Download counts and release facts for npm and PyPI packages, in one dataset. One row per package.

Useful for a briefing or a dashboard. Download counts include automated installs, so treat a spike as a question, not a verdict.

### Who it is for

Developer relations teams comparing adoption, open source maintainers watching their own packages, investors reading a library before a conversation, and people building tools that need the numbers in a dataset.

### What you get

- One row per package, with the ecosystem and the name you asked for.
- Download counts for the latest published day, week and month. Those windows can lag by a day or two, and they are not the same as the date range below.
- A daily series for the dates you set, and the total of that series.
- The latest version and the day it was released.
- Release cadence: how many releases fell in the last year, the median days between them, the gap since the previous release, and how many fell inside your dates.
- The licence, when the package publishes a short identifier.
- The number of dependencies declared on the latest version. Python extras are not counted.
- The repository link, when it points at GitHub, GitLab, Bitbucket, Codeberg or SourceHut. A personal site is left empty.
- A trend against the previous period, when you turn that comparison on. The trend is a direction (`up`, `down` or `flat`) and a percent change. It is left empty when either period was not published in full.
- A note on the row when a figure was missing. The other figures on that row are still saved.

### Tutorial

1. Set **Packages**. Each item needs a name and an ecosystem, `npm` or `pypi`. A scoped npm name is written `@scope/name`.
2. Leave **From** and **To** empty for the 30 days ending yesterday, or set both. The range can be at most 180 days.
3. Leave **Comparison** on None, or choose Previous period to compare that range with the same number of days just before it.
4. Leave **Max packages** at 25 unless you want a shorter run. The ceiling is 40.
5. Run the Actor. Open the Packages view. The `OUTPUT` record says how many rows were saved and names any package that was not found.
6. Schedule it if you want a standing feed. Packages that were not saved are not charged.

### Example input

```json
{
  "packages": [
    { "name": "express", "ecosystem": "npm" },
    { "name": "requests", "ecosystem": "pypi" }
  ],
  "dateFrom": "2026-10-01",
  "dateTo": "2026-10-02",
  "comparisonMode": "previous-period",
  "maxPackages": 25
}
```

### Example output

```json
{
  "ecosystem": "npm",
  "name": "express",
  "latestVersion": "4.21.2",
  "latestReleaseDate": "2025-12-01",
  "downloadsDaily": 1800000,
  "downloadsWeekly": 12000000,
  "downloadsMonthly": 50000000,
  "seriesStart": "2026-10-01",
  "seriesEnd": "2026-10-02",
  "seriesDownloads": 3600000,
  "series": [
    { "date": "2026-10-01", "downloads": 1800000 },
    { "date": "2026-10-02", "downloads": 1800000 }
  ],
  "releasesLastYear": 6,
  "medianDaysBetweenReleases": 48,
  "daysSincePreviousRelease": 30,
  "releasesInRange": 0,
  "licence": "MIT",
  "dependencyCount": 31,
  "repositoryUrl": "https://github.com/expressjs/express",
  "trendMode": "previous-period",
  "trendDirection": "up",
  "trendChangePercent": 12.5,
  "trendCurrentDownloads": 3600000,
  "trendPreviousDownloads": 3200000,
  "partial": false,
  "note": ""
}
```

The numbers above show the shape of a row. A real run fills them from the sources below.

### Pricing

You pay a small fee when the run starts, then a fee for each package row that is saved. The daily series is part of that row. The dataset itself is not charged again.

| What | Price |
|---|---|
| Run start | $0.00005 |
| Package stats row | $0.001 |

Packages past **Max packages**, and names that are not published, are not charged. A row that is saved with a missing figure is charged, and the note says what was missing.

**Examples**

- Ten package rows: 10 × $0.001 = **$0.010**, plus the $0.00005 start fee.
- A run that saves nothing: **$0.00005**.

Use **Max packages** and Apify's maximum cost per run to cap a single run. The pricing tab on the store is the price you are charged if it ever differs.

### Scheduling and integrations

Save a task and schedule it when you want a weekly check. The dataset can go to Google Sheets, Slack, email, Make or Zapier, or you can read it from the Apify API. The rows are small enough to pass to an AI agent over MCP.

### Sources and licences

npm figures come from the public npm registry and the public npm download counts. The npm Open Source Terms allow commercial use, and they allow reading the public registry through its public interface. They also forbid an unreasonable volume of requests. This Actor caps each run and waits when a source asks it to slow down. This Actor is not affiliated with npm.

The licence on a row is the identifier the package publishes. It is not a licence for the download counts. Download counts describe installs, including continuous integration, not a count of people.

PyPI project facts come from the public PyPI interface, under the PyPI Terms of Service. Those terms forbid using the interface to sell personal information. Python download totals and the daily series come from PyPI Stats, a public project of the Python Software Foundation. The project asks callers not to pull every package, because the figures update once a day and the service is small. The daily series covers about half a year, so a Python row can be shorter than the dates you asked for. The project code is under the Apache License 2.0. This Actor is not affiliated with the Python Software Foundation.

Known mirror copies are left out of the Python daily series.

### No personal data

This Actor does not collect personal data.

- It asks for package names, a date range and a comparison mode. It does not ask for account names or contact details.
- Author, maintainer and contributor fields are dropped, including names and emails.
- Raw responses are not stored.
- Logs and error messages do not include personal data or the addresses that were called.

### FAQ

**Why do the daily count and the series differ?**
Daily, weekly and monthly are the latest windows the source has published. The series is the date range you set. A package can be busy last month and quiet in the week you asked about.

**What if a name is wrong?**
That package is skipped. The run summary names it. Other packages are still saved. If nothing could be saved, the run stops with that message and no package rows are charged.

**Why is a Python series missing days?**
PyPI Stats keeps about half a year of daily points, and a day that was not published is left out. The note on the row says when the series is incomplete. The trend is empty unless both periods are complete.

**Where do the figures come from?**
See Sources and licences above. For a binding claim about a project, read the project itself.

### Next additions

- More package ecosystems.
- More signals about each project.

### Feedback

Missing a field? Open an issue on the Actor's Issues tab.

# Actor input Schema

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

Packages to look up. Each item needs a name and an ecosystem, npm or pypi. npm names can be scoped, for example @scope/name. At most 40. Duplicates are skipped.

## `dateFrom` (type: `string`):

First day of the download series, as YYYY-MM-DD. Leave empty, together with To, for the 30 days ending yesterday. Not before 2015-01-10.

## `dateTo` (type: `string`):

Last day of the download series, inclusive. Leave empty, together with From, for the 30 days ending yesterday. Not after today. The range can be at most 180 days.

## `comparisonMode` (type: `string`):

Previous period compares the series total with the same number of days immediately before From. None leaves the trend empty.

## `maxPackages` (type: `integer`):

Most packages read in one run, from 1 to 40. Default 25. Packages past the cap are not charged.

## Actor input object example

```json
{
  "packages": [
    {
      "name": "express",
      "ecosystem": "npm"
    },
    {
      "name": "requests",
      "ecosystem": "pypi"
    }
  ],
  "dateFrom": "2026-09-01",
  "dateTo": "2026-09-30",
  "comparisonMode": "none",
  "maxPackages": 25
}
```

# Actor output Schema

## `packages` (type: `string`):

One row per package, with downloads, the latest version, cadence, licence, dependency count, repository and trend.

## `packageDetails` (type: `string`):

The same rows, including the daily series and the date windows.

## `summary` (type: `string`):

The OUTPUT record in the default key-value store.

# 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": [
        {
            "name": "express",
            "ecosystem": "npm"
        },
        {
            "name": "requests",
            "ecosystem": "pypi"
        }
    ],
    "comparisonMode": "none"
};

// Run the Actor and wait for it to finish
const run = await client.actor("vizellie/package-stats-npm-pypi").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": [
        {
            "name": "express",
            "ecosystem": "npm",
        },
        {
            "name": "requests",
            "ecosystem": "pypi",
        },
    ],
    "comparisonMode": "none",
}

# Run the Actor and wait for it to finish
run = client.actor("vizellie/package-stats-npm-pypi").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": [
    {
      "name": "express",
      "ecosystem": "npm"
    },
    {
      "name": "requests",
      "ecosystem": "pypi"
    }
  ],
  "comparisonMode": "none"
}' |
apify call vizellie/package-stats-npm-pypi --silent --output-dataset

```

## MCP server setup

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

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/0qH4ZNWwfUoVlbr4f/builds/R32xnS8PAiIbn5lFe/openapi.json
