# PyPI Scraper: Package Stats, Downloads & Releases (`recordsdata/pypi-package-stats-scraper`) Actor

Scrape PyPI packages by name: version, license, dependencies, repository, day/week/month download counts, full dated release history, and newest-package feeds. Export CSV, Excel, JSON, XML. No login or API key. Example: requests had 163 releases and 1.2B downloads in a month.

- **URL**: https://apify.com/recordsdata/pypi-package-stats-scraper.md
- **Developed by:** [RecordsData](https://apify.com/recordsdata) (community)
- **Categories:** Developer tools, Open source
- **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.
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

<p align="center">
  <img src="https://api.apify.com/v2/key-value-stores/AAm3a1h3Z9nYfrvh9/records/banner?v=3" alt="RecordsData" width="100%" />
</p>

## 🐍 PyPI Scraper: Package Stats, Downloads & Releases

> PyPI Scraper reads the official pypi.org JSON API, the PyPI Simple index, pypistats.org and the monthly top-15,000 list. Give it package names, a top-N count, name-search texts or a release-monitoring date, and get version, license, maintainers, dependencies, Python versions, vulnerability counts and day, week and month downloads. Optional per-version release history and both PyPI RSS feeds. Export CSV, Excel, JSON or XML. No login, no API key. Checked 2026-10-10. Billed per row delivered, never for errors.

PyPI Scraper turns a list of package names, the top downloaded packages, or a name search into one clean table. In a cloud run on 2026-10-10, a top-40 pull (ranks 2000 to 2039) returned 40 of 40 packages with all three download columns in 42 seconds, straight from registry data rather than parsed HTML.

### 🐍 What does PyPI Scraper do?

- Scrape PyPI package metadata: name, latest version, summary, author and maintainer with emails, license, Python requirement and homepage, repository, documentation and issue links.
- Get PyPI download stats per package: last day, week and month (from pypistats.org).
- Pull the top N most downloaded PyPI packages (up to 15,000, from the community monthly list) with `topRank`, or search package names (case and separator insensitive substring over the Simple index).
- Monitor releases with `onlyNewSince`: only packages, release rows and feed items newer than a date or a window such as 7d. Also read the newest-packages and latest-updates feeds.
- Export the full release history, one row per version, with release date, file count, wheel and sdist sizes, Python tags, pre-release and yanked flags.
- Read dependencies, extras, classifiers, Python versions and known vulnerability counts.
- Accept plain names, `https://pypi.org/project/<name>/` URLs or requirements.txt lines such as `django>=5`.

### 📦 What data can you extract from PyPI?

| Field | Row type | Example |
|---|---|---|
| name / packageName | package, release | requests |
| version | package, release | 2.34.2 |
| summary, author, authorEmail | package | Python HTTP for Humans. |
| maintainer, maintainerEmail | package (when listed) | a maintainer name |
| license | package | Apache-2.0 |
| requiresPython, pythonVersions | package, release | >=3.10 |
| repository, homePage, documentationUrl, issuesUrl, changelogUrl, projectUrls | package | https://github.com/psf/requests |
| dependencies, dependenciesCount, requiredDependenciesCount, extras | package | charset_normalizer<4,>=2 |
| classifiers, developmentStatus, keywords | package | Programming Language :: Python :: 3.13 |
| vulnerabilityCount, vulnerabilityIds | package | 0 |
| downloadsLastDay / Week / Month | package | 1202777296 |
| topRank, topDownloads30d | package (topN mode) | rank in the monthly top-15,000 list |
| versionCount, firstReleaseDate, latestReleaseDate | package | 163, 2026-05-14T19:25:26Z |
| latestSdistSizeBytes, latestSmallestWheelBytes | package | size in bytes |
| longDescription | package (opt-in) | cut at 20,000 characters |
| releasedAt, fileCount, hasWheel, hasSdist, sdistSizeBytes, smallestWheelBytes, pythonTags, isPrerelease, yanked, yankedReason | release | 2026-05-14T19:25:26Z, 2, true |
| feed, title, publishedAt | feed-item | newest-packages, ISO UTC time |
| scrapedAt, url | all | ISO timestamp, PyPI link |

The dataset schema lists 54 keys (53 data columns plus `error`) in one fixed order, with four views: Overview, Packages, Releases and Feed. Optional columns are left out when PyPI has no value for them, so you never get empty arrays or placeholder text.

### 🧾 Sample output of the PyPI scraper

Record from a cloud run on 2026-10-07 (shortened). This is the older, trimmed shape: newer columns such as authorEmail, vulnerabilityCount and topRank are not shown.

```json
{
  "recordType": "package",
  "name": "requests",
  "version": "2.34.2",
  "url": "https://pypi.org/project/requests/",
  "summary": "Python HTTP for Humans.",
  "author": "Kenneth Reitz",
  "license": "Apache-2.0",
  "requiresPython": ">=3.10",
  "dependencies": ["charset_normalizer<4,>=2", "idna<4,>=2.5", "urllib3<3,>=1.26", "certifi>=2023.5.7"],
  "downloadsLastDay": 50777141,
  "downloadsLastWeek": 304164213,
  "downloadsLastMonth": 1202777296,
  "versionCount": 163,
  "latestReleaseDate": "2026-05-14T19:25:26.443000Z",
  "yanked": false,
  "scrapedAt": "2026-10-07T19:41:37.367Z"
}
```

A release-history row from the same run looks like this:

```json
{
  "recordType": "release",
  "packageName": "requests",
  "version": "2.34.2",
  "releasedAt": "2026-05-14T19:25:26.443000Z",
  "fileCount": 2,
  "hasWheel": true,
  "yanked": false,
  "scrapedAt": "2026-10-07T19:41:37.550Z"
}
```

### 💰 How much does it cost to scrape PyPI?

Pay per event, charged only for rows saved. Live prices read on 2026-10-10, by Apify plan tier:

| Event | What is billed | Free | Bronze | Silver | Gold and up |
|---|---|---|---|---|---|
| package-record | One package (also top-N and search rows) | $0.015 | $0.01313 | $0.01125 | $0.010 |
| release-record | One released version | $0.010 | $0.010 | $0.010 | $0.010 |
| feed-record | One newest-package or latest-update item | $0.010 | $0.010 | $0.010 | $0.010 |

Example: 1,000 packages cost 1,000 x $0.015 = $15.00 on the Free tier ($10.00 on Gold). A top-5 pull costs 5 x $0.015 = $0.075 (cloud run on 2026-10-10: 5 package-record events charged). A package plus 29 release rows costs $0.015 + 29 x $0.010 = $0.305 (same day: 1 package-record and 29 release-record events). Packages skipped by `onlyNewSince`, not-found names, empty results and error rows are free; a monitoring run with nothing new charged 0 events.

### 🚀 How to scrape PyPI in 3 steps

1. Open the actor and paste package names or pypi.org URLs into **Package names**, or set **Top downloaded packages** or **Search package names**.
2. Toggle **Release history** or a **feed** if you need those rows, optionally set **Only releases since**, and set **Max Items**.
3. Click **Start**, then download the dataset as CSV, Excel, JSON or XML.

### ⚙️ Input

| Field | Type | Default | Meaning |
|---|---|---|---|
| packageNames | array | requests, numpy, flask | Names, pypi.org URLs or requirements.txt lines |
| topN, topStartRank | integer | 0 (off), 1 | N most downloaded packages, max 15,000; first rank to take |
| searchQueries | array | empty | Name-substring search, at least 3 letters or digits |
| searchMaxResultsPerQuery | integer | 20 | Max matches per text, up to 1,000 |
| includeDownloadStats, includeReleaseHistory | boolean | true, false | Day/week/month downloads; one row per released version |
| includeNewestPackagesFeed, includeLatestUpdatesFeed | boolean | false | Newest packages RSS (about 40 rows); latest updates RSS (about 100 rows) |
| includeDescription | boolean | false | Add the long project description |
| onlyNewSince | string | empty | UTC date, or 24h, 7d, 2w, 3mo, 1y, today, yesterday |
| maxItems | integer | 10 (prefill) | Cap on total rows (free users: 10) |

```json
{
  "packageNames": ["requests", "numpy", "flask"],
  "includeDownloadStats": true,
  "includeReleaseHistory": true,
  "maxItems": 100
}
```

Other examples: `{"topN": 100, "maxItems": 100}` and `{"packageNames": ["django"], "includeReleaseHistory": true, "onlyNewSince": "7d"}`.

### 📤 Output

One dataset with three row types (`package`, `release`, `feed-item`) told apart by `recordType`. Empty values are never emitted; `vulnerabilityCount: 0` and `yanked: false` are real values. If nothing is found or newer than `onlyNewSince`, the run ends with a status message and no charge. Invalid input gives one free error row. If PyPI is unreachable and zero rows could be delivered, the run fails.

Limits to know:

- Search matches package names only, not summaries or keywords.
- The top list comes from a community project whose repository states no licence; it holds public PyPI download statistics, so check it fits your use.
- pypistats.org allows 60 requests per minute per IP, so a top pull with download stats runs at about 1 package per second (40 packages in 42 seconds). Turn download stats off for roughly 3 to 6 packages per second.
- A scan with `onlyNewSince` is bounded: it stops after 600 skipped packages plus 400 per delivered row. Measured worst-case free scans cost $0.0003 to $0.0004.
- Default memory is 512 MB (peak sampled 149 to 313 MB). Small cloud runs (1 to 5 rows) succeeded 16 of 16 times, median 2.4 seconds.

### ⚖️ PyPI scraper vs alternatives

Store pages read on 2026-10-10. "Not stated" means the listing does not show it.

| | RecordsData PyPI Scraper | hipersoft/pypi-scraper | logiover/pypi-package-scraper |
|---|---|---|---|
| Price per 1,000 | $15 Free, $10 Gold | from $1.875 | from $1.50 |
| Top downloaded list | Yes, up to 15,000 | Not stated | Yes, up to 5,000 |
| Name search | Yes, names only | No, exact names | Not stated in their listing |
| Release history | All versions, with sizes | 10 most recent, optional | Latest version only |
| Release monitoring, feeds | `onlyNewSince`, two feeds | Not stated in their listing | Scheduling suggested only |
| Output columns | 53 | 23 | 16 |

We cost more per package row than every rival here; the extra buys full release rows, both RSS feeds, vulnerability counts and a monitoring mode. automly/pypi-package-metadata-api charges $0.50 per 1,000 and lists maintainers and version rows, with no search or top list. ef12/pypi-package-scraper starts at $2 and offers keyword search (20 results per query). hipersoft has one field we lack, `dependenciesRaw`. If you only need names and versions, a cheaper actor or the free PyPI JSON API is enough.

### 🧰 PyPI scraper use cases

Ranking the most downloaded Python packages, daily release monitoring, dependency and license audits for a requirements list, security reviews of yanked, stale or vulnerable dependencies, download trend tracking, and supply-chain monitoring through the latest-updates feed.

### 🔌 Run the PyPI scraper via API and integrations

```bash
curl -X POST "https://api.apify.com/v2/acts/recordsdata~pypi-package-stats-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"packageNames":["requests","flask"],"maxItems":10}'
```

Works with the Apify API, JavaScript and Python clients, schedules, webhooks, Zapier, Make, n8n and Google Sheets.

### 🤖 PyPI Scraper MCP server for AI agents

AI agents can call this Actor through the Apify MCP server at `https://mcp.apify.com?tools=RecordsData/pypi-package-stats-scraper`.

Add this to your MCP client configuration (Claude Desktop, Cursor, VS Code or any remote MCP client):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=RecordsData/pypi-package-stats-scraper"
    }
  }
}
```

Example agent prompt:

> Use the PyPI Scraper to get the license, latest version, dependencies and last-month downloads for requests, httpx and aiohttp, then tell me which has the most downloads. Then list the django releases from the last 7 days.

Tool description: choose this Actor when an agent needs facts about Python packages on PyPI: version, license, dependencies, download counts, the top downloaded list, name search, dated release history, only-new release monitoring or the newest-packages and latest-updates feeds. It takes the same fields as the Input table above.

### 🛡️ Is it legal to scrape PyPI?

The actor reads PyPI's public JSON API, Simple index and RSS feeds, the public top-packages list and pypistats.org's public API, at a polite rate and with a descriptive user agent. It collects package metadata only, not private data. Review PyPI and pypistats terms for your own use case.

### ❓ Frequently asked questions

#### Where do the PyPI Scraper download counts come from?

The PyPI Scraper takes day, week and month download counts from pypistats.org, which publishes PyPI download statistics, while package metadata such as version, license and dependencies comes from the official pypi.org JSON API. Each package row carries downloadsLastDay, downloadsLastWeek and downloadsLastMonth when pypistats.org answers.

#### Can I paste PyPI URLs instead of package names into the PyPI Scraper?

Yes. The PyPI Scraper normalizes URLs in the form https://pypi.org/project/name/ to the plain package name, so you can mix names and pypi.org project URLs in the same Package names list. You can also paste requirements.txt lines such as django>=5, one per entry, and get one row per Python package. Names differing only by case or separators merge into one row.

#### How complete is the PyPI Scraper release history?

The PyPI Scraper returns every version that PyPI lists for the package when Release history is turned on, one row per version with file count, wheel availability and file sizes. The release date is the first uploaded file of that version. Very old versions without uploaded files have no release date, so the releasedAt field is left out for them.

#### Why did my PyPI Scraper run return 0 results?

Either the package names were not found on PyPI, so check the spelling, or no data option was enabled in the PyPI Scraper input, or nothing was newer than your onlyNewSince date. Not-found names produce no billed rows, and the status message says so. Fix the input and run the PyPI Scraper again.

#### Am I charged for failed lookups in the PyPI Scraper?

No. The PyPI Scraper saves error and not-found rows without a billable event, so you pay only for package, release and feed rows that were delivered to the dataset. You can also set a maximum cost per run in Apify, and the PyPI Scraper stops cleanly when that limit is reached.

#### Can the PyPI Scraper list the top downloaded PyPI packages?

Yes. Set topN (up to 15,000) and optionally topStartRank. The PyPI Scraper reads the community monthly top-15,000 list, then adds the usual metadata and download columns plus topRank and topDownloads30d. Each row is billed as a package-record. The list is refreshed monthly, and its repository states no licence.

#### How does PyPI Scraper release monitoring work?

Set onlyNewSince to a UTC date or a window such as 7d. The PyPI Scraper then keeps only packages whose latest release is newer, release rows uploaded since then and feed items published since then. Skipped packages are free, and a run with nothing new ends with a status message and zero charged events.

#### Why are download counts missing for a package in the PyPI Scraper?

pypistats.org rate limits requests, at 60 per minute per IP. The PyPI Scraper spaces its calls with a one second gap and honours the rate-limit headers. If pypistats.org still refuses, the download columns are left out of that row with a log warning, and the package row is still delivered and billed as normal with its metadata, license and dependencies.

#### Can I try the PyPI Scraper for free?

Yes. Free Apify users get a 10-row preview of the PyPI Scraper, enough to check package metadata, download counts and release rows on your own package list. Paid Apify plans unlock larger runs and lower per-row prices on the Bronze, Silver and Gold tiers, down to $0.010 per package row on Gold.

#### Can I export PyPI Scraper results to Excel or CSV?

Yes. The PyPI Scraper writes one Apify dataset that you can download as CSV, Excel, JSON, XML or HTML from the Storage tab or through the API. Package, release and feed rows are told apart by the recordType field, so you can filter them in a spreadsheet or load them into a database.

### 🔗 Want more developer data? Other RecordsData scrapers

- [Hacker News Search Scraper](https://apify.com/RecordsData/hackernews-search-scraper)
- [Wikipedia Articles Scraper](https://apify.com/RecordsData/wikipedia-articles-scraper)
- [SEC EDGAR Filings Scraper](https://apify.com/RecordsData/sec-edgar-filings-scraper)

### 💬 Support

Found a bug or missing field? Open the **Issues** tab on this actor's page or write to contact.punkrecordsdata@gmail.com. Custom solutions available on request.

Last updated: 2026-10-11

# Actor input Schema

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

PyPI package names, pypi.org project URLs or requirements.txt lines to scrape for metadata, license, dependencies and download counts (for example requests, numpy, https://pypi.org/project/flask/ or django>=5). Leave empty if you use the top list, a name search or a feed below.

## `topN` (type: `integer`):

Add the N most downloaded PyPI packages of the last 30 days, taken from the community monthly top-15,000 list (refreshed on the 1st of each month). 0 = off. Adds the columns topRank and topDownloads30d. With download stats on, expect about 1 package per second (pypistats.org allows 60 requests per minute); turn download stats off for roughly 3 to 6 packages per second.

## `topStartRank` (type: `integer`):

First rank to take from the top list. 1 = the most downloaded package. Use 101 with Top downloaded packages = 100 to get ranks 101 to 200 (handy to split a large pull into several runs).

## `searchQueries` (type: `array`):

Texts to look for inside package names, for example scraper or web-scrap. Matching ignores case and treats - \_ and . alike (PyPI name rules), so django-rest also finds Django_REST. At least 3 letters or digits per text. Matches are ordered exact name first, then by monthly downloads rank, then by shorter name. The search covers names only (PyPI's own search page is behind a bot challenge and has no public API).

## `searchMaxResultsPerQuery` (type: `integer`):

How many matching packages to take for each search text, best ranked first.

## `includeDownloadStats` (type: `boolean`):

Attach last-day / last-week / last-month download counts from pypistats.org to each package. This is the slowest part of a run: pypistats.org allows 60 requests per minute, so about 1 package per second.

## `includeReleaseHistory` (type: `boolean`):

One extra row per released version with date, file count, wheel and source availability, file sizes, Python tags, pre-release flag and yank reason. Each release row is billed like a release record.

## `includeNewestPackagesFeed` (type: `boolean`):

The latest brand-new packages published on PyPI (RSS, about 40 rows): name, summary, author email, publish time.

## `includeLatestUpdatesFeed` (type: `boolean`):

The latest version updates across all of PyPI (RSS, about 100 rows): name, new version, summary, author email, publish time.

## `includeDescription` (type: `boolean`):

Add the long description of the latest release (the README text shown on PyPI, cut at 20,000 characters) as longDescription on package rows. Makes rows much larger.

## `onlyNewSince` (type: `string`):

Release monitoring. Keep only packages whose latest release was uploaded at or after this moment, release rows uploaded since then, and feed items published since then. Packages and rows that are older are skipped and never billed. Use a UTC date (2026-10-01), a date and time (2026-10-01T12:00:00Z) or a relative value: 24h, 7d, 2w, 3mo, 1y, today, yesterday. Empty = no filter. To keep the free skipping cheap, a run stops after 600 skipped or unknown packages plus 400 for every package row delivered.

## `maxItems` (type: `integer`):

Maximum number of rows to return across packages, releases and feed items. Free users are limited to 10 rows (preview); paid users can set up to 1,000,000. Also acts as a spend cap, since each row is billed.

## Actor input object example

```json
{
  "packageNames": [
    "requests",
    "numpy",
    "flask"
  ],
  "topN": 0,
  "topStartRank": 1,
  "searchQueries": [],
  "searchMaxResultsPerQuery": 20,
  "includeDownloadStats": true,
  "includeReleaseHistory": false,
  "includeNewestPackagesFeed": false,
  "includeLatestUpdatesFeed": false,
  "includeDescription": false,
  "onlyNewSince": "",
  "maxItems": 10
}
```

# Actor output Schema

## `overview` (type: `string`):

Key fields per row

## `fullData` (type: `string`):

Complete dataset with all fields

# 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": [
        "requests",
        "numpy",
        "flask"
    ],
    "searchQueries": [],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("recordsdata/pypi-package-stats-scraper").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": [
        "requests",
        "numpy",
        "flask",
    ],
    "searchQueries": [],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("recordsdata/pypi-package-stats-scraper").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": [
    "requests",
    "numpy",
    "flask"
  ],
  "searchQueries": [],
  "maxItems": 10
}' |
apify call recordsdata/pypi-package-stats-scraper --silent --output-dataset

```

## MCP server setup

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

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/gqSkqzLlKvIPEGnCX/builds/UKld7hQgietoGRKSI/openapi.json
