PyPI Package Scraper — Downloads, Releases, Vulnerabilities avatar

PyPI Package Scraper — Downloads, Releases, Vulnerabilities

Pricing

from $1.40 / 1,000 result items

Go to Apify Store
PyPI Package Scraper — Downloads, Releases, Vulnerabilities

PyPI Package Scraper — Downloads, Releases, Vulnerabilities

Python package intelligence for AI agents and due diligence: downloads (day/week/month, by Python version and OS), release history and cadence, parsed requirements, license (PEP 639), development status, known vulnerabilities (OSV), owners, project URLs.

Pricing

from $1.40 / 1,000 result items

Rating

0.0

(0)

Developer

Samat Makatov

Samat Makatov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Categories

Share

One clean JSON per Python package: downloads (day/week/month, by Python version and OS), release history and cadence, parsed requirements, normalized license, development status, known vulnerabilities, owners and project links. Built for AI agents, dependency due diligence and developer-tool market research. Paste names or whole requirements.txt lines (requests>=2.31,<3). Plus two feed modes that watch PyPI for new releases and new projects in your niche. No API key, no proxy, no browser.

Use cases

  • Dependency due diligence — before adopting a library, get license (PEP 639 expression, field or classifier), maintenance cadence, wheel availability, Python support, owners and open vulnerabilities in one item.
  • Supply-chain / security review — paste the pins of your requirements.txt (name==version) to see the OSV advisories (CVE/GHSA aliases, fixed-in) for exactly the versions you ship; flag yanked releases. The security dataset view shows them side by side.
  • Competitor & market tracking — compare monthly downloads, Python 3.x adoption and OS split of your SDK vs. alternatives; schedule weekly and diff.
  • Abandonment alerts — daysSinceLastRelease, releasesLast365d, developmentStatus (Inactive/Mature) feed a "stale dependency" monitor.
  • Launch tracking — newPackages + keywords (e.g. mcp, agent, llm) surfaces projects launching in your category.
  • Release monitoring — recentUpdates + keywords catches new versions of an ecosystem (e.g. langchain, openai) minutes after upload.

Input

FieldTypeDefaultNotes
modestringpackagespackages · recentUpdates · newPackages (exactly these values: Apify refuses any other before the run starts)
packagesstring[]—Project names; name==1.2.3 or name@1.2.3 pins a version; a range (>=2.31,<3, ~=1.26, ==4.2.*) returns the newest release in it. Lines pasted from requirements.txt work (extras, markers, comments, --hash are dropped). Names are PEP 503-normalized (case/-/_/. insensitive); the same name + version twice is fetched and charged once. Required in packages mode.
keywordsstring[][]Feed modes only: keep items whose name/summary contains any keyword (case-insensitive).
maxItemsinteger100Feed modes only (1–200). The PyPI feeds carry the latest ~40 entries.
includeDownloadsbooleantrueDay/week/month downloads from pypistats.org (mirrors excluded), ~3 s per package (see Limits).
downloadsBreakdownbooleanfalseAdds downloadsByPythonMinor30d and downloadsBySystem30d (last 30 days). +2 pypistats requests per package.
downloadsHistoryDaysinteger00–180. Adds downloadsHistory daily series + total. +1 request.
includeMirrorsbooleanfalseCount mirror traffic in breakdown/history.
includeVulnerabilitiesbooleantruevulnerabilities[] from PyPI (OSV). vulnerabilityCount always present.
includeReleasesbooleantruerecentVersions[] (newest first).
versionsLimitinteger100–200 entries in recentVersions.
includeClassifiersbooleantrueRaw Trove classifiers[].
descriptionCharsinteger00 = omit; N = first N chars of the long description (README).
fieldsstring[][]Top-level columns to keep, in this order; id and name are always kept. Case, -, _ and spaces do not matter; unknown names are ignored with a warning (status + SUMMARY), and the run fails when no name is a column.

Reference

Sources and what they provide

SourceEndpointUsed for
PyPI JSON APIhttps://pypi.org/pypi/{name}/json, /pypi/{name}/{version}/jsonmetadata, releases, files, vulnerabilities, ownership
PyPI RSShttps://pypi.org/rss/updates.xml, /rss/packages.xmlrecentUpdates, newPackages modes
pypistats.org/api/packages/{name}/recent, /python_minor, /system, /overalldownloads (updated once a day, 180-day depth)

PyPI has no search API (XML-RPC search was disabled in 2021); use the feed modes with keywords, or a search engine, to discover names.

Development Status classifiers → developmentStatus

CodeLabel
1Planning
2Pre-Alpha
3Alpha
4Beta
5Production/Stable
6Mature
7Inactive

Trove classifier groups (894 classifiers as of Sep 2026 — full list: https://pypi.org/classifiers/)

GroupCountExample
Development Status7Development Status :: 5 - Production/Stable
Environment74Environment :: Web Environment
Framework185Framework :: Django :: 5.1
Intended Audience14Intended Audience :: Developers
License84License :: OSI Approved :: MIT License
Natural Language64Natural Language :: English
Operating System43Operating System :: OS Independent
Programming Language102Programming Language :: Python :: 3.12
Topic320Topic :: Software Development :: Libraries
Typing2Typing :: Typed

pythonVersions is extracted from Programming Language :: Python :: 3.N classifiers (3.0 … 3.16 exist today); requiresPython is the authoritative constraint.

pypistats categories

  • downloadsBySystem30d keys: Linux, Windows, Darwin, null (unknown installer/CI), occasionally other.
  • downloadsByPythonMinor30d keys: 2.7, 3.6 … 3.14, null.
  • downloadsHistory categories: without_mirrors (default) or with_mirrors (includeMirrors: true).

Version ranges and requirements.txt lines

  • name==1.2.3, name===1.2.3, name@1.2.3 and name 1.2.3 pin a release (yanked releases included — that is what you pinned).
  • Any other specifier (>=, <=, >, <, !=, ~=, ==1.2.*, comma-separated) is resolved like pip does (PEP 440): the newest release that satisfies it, has files and is not yanked; pre-releases only when the range names one or nothing else matches. The row then carries requestedSpecifier and that release's metadata and vulnerabilities. No release in the range → the entry is listed in SUMMARY.notFound (not charged).
  • Extras (requests[socks]), environment markers (; python_version < "3.9"), comments (# …) and per-line options (--hash=…) are dropped; markers are not evaluated. A pasted file with line breaks and \ continuations in one entry is split into lines.
  • Lines that are not packages — -r other.txt, -e …, URLs, name @ https://… — are skipped and listed in SUMMARY.invalid (not charged).

License normalization (licenseSource)

expression (PEP 639 license_expression, SPDX) → field (short license string ≤ 60 chars) → classifier (License :: … leaf) → null. Projects that paste the whole license text into the field fall back to the classifier; the raw leaves are in licenseClassifiers.

Examples

Dependency due diligence with security and adoption data

{ "packages": ["requests", "fastapi", "langchain"], "downloadsBreakdown": true, "downloadsHistoryDays": 30 }

Audit the exact versions in your lockfile

{ "packages": ["pillow==9.0.0", "urllib3==1.26.5", "cryptography==41.0.0"], "includeDownloads": false, "includeReleases": false }

Paste requirements.txt lines (ranges resolve to the newest matching release)

{ "packages": ["requests[socks]>=2.31,<3", "urllib3~=1.26", "django==4.2.* # LTS"], "includeDownloads": false, "fields": ["version", "requestedSpecifier", "latestVersion", "vulnerabilityCount"] }

Lean weekly competitor tracker (small items, cheap to diff)

{ "packages": ["openai", "anthropic", "google-genai", "mistralai"], "fields": ["version", "downloadsWeek", "downloadsMonth", "lastRelease", "releasesLast90d", "vulnerabilityCount"] }

Watch for new MCP / agent projects launching on PyPI (run every 15 min)

{ "mode": "newPackages", "keywords": ["mcp", "agent", "llm"] }

Catch new releases in the LangChain ecosystem

{ "mode": "recentUpdates", "keywords": ["langchain", "langgraph"], "maxItems": 50 }

Output

One item per package (trimmed):

{
"id": "fastapi",
"name": "fastapi",
"version": "0.141.1",
"requestedVersion": null,
"requestedSpecifier": null,
"isLatest": true,
"latestVersion": "0.141.1",
"summary": "FastAPI framework, high performance, easy to learn, fast to code, ready for production",
"license": "MIT",
"licenseSource": "expression",
"licenseExpression": "MIT",
"licenseClassifiers": [],
"developmentStatus": "Beta",
"developmentStatusCode": 4,
"requiresPython": ">=3.10",
"pythonVersions": ["3.10", "3.11", "3.12", "3.13", "3.14"],
"requires": [{ "name": "starlette", "normalized": "starlette", "specifier": ">=0.46.0", "extras": [], "marker": null, "extra": null, "url": null }],
"requiresCount": 5,
"requiresOptionalCount": 27,
"extras": ["standard", "standard-no-fastapi-cloud-cli", "all"],
"author": "Jane Doe",
"authorEmail": "jane@example.com",
"owners": [{ "user": "example-user", "role": "Owner" }],
"organization": null,
"homepage": "https://github.com/fastapi/fastapi",
"repository": "https://github.com/fastapi/fastapi",
"documentation": "https://fastapi.tiangolo.com/",
"changelog": "https://fastapi.tiangolo.com/release-notes/",
"issues": "https://github.com/fastapi/fastapi/issues",
"downloadsDay": 4012345, "downloadsWeek": 27654321, "downloadsMonth": 112345678,
"downloadsByPythonMinor30d": { "3.12": 41000000, "3.11": 30000000, "3.13": 18000000, "null": 9000000 },
"downloadsBySystem30d": { "Linux": 98000000, "Darwin": 7000000, "Windows": 6000000, "null": 1345678 },
"downloadsError": null,
"downloadsFetchedAt": "2026-09-12T23:29:51.120Z",
"vulnerabilities": [],
"vulnerabilityCount": 0,
"versionsCount": 317, "firstRelease": "2018-12-08T08:14:13.103167Z", "lastRelease": "2026-07-29T17:18:04.364385Z",
"daysSinceLastRelease": 45, "releasesLast90d": 24, "releasesLast365d": 92, "yankedVersionsCount": 0,
"latestFiles": { "count": 2, "wheels": 1, "sdist": true, "totalSize": 557753, "publishedAt": "2026-07-29T17:18:04.364385Z" },
"hasWheel": true, "yanked": false, "yankedReason": null,
"recentVersions": [{ "version": "0.141.1", "publishedAt": "2026-07-29T17:18:04.364385Z", "files": 2, "hasWheel": true, "hasSdist": true, "yanked": false, "requiresPython": ">=3.10", "totalSize": 557753 }],
"pypiUrl": "https://pypi.org/project/fastapi/",
"sourceUrl": "https://pypi.org/pypi/fastapi/json",
"fetchedAt": "2026-09-12T23:29:53.340Z"
}

Download numbers in this sample are illustrative; downloadsHistory.series entries look like { "date": "2026-09-11", "downloads": 42125636 }. When pypistats has no numbers for a package (brand-new, or it rate-limited the run) the counts are null and downloadsError says why — the item is still returned.

FieldMeaning
idPEP 503 normalized name — stable key for joins/dedupe
version, requestedVersion, requestedSpecifier, latestVersion, isLatestReturned version; pinned version if any; requested range if any; newest release on PyPI; whether they match
license, licenseSource, licenseExpression, licenseClassifiersSee License normalization
developmentStatus, developmentStatusCodeFrom the Development Status classifier (null if not declared)
requiresPython, pythonVersionsDeclared constraint; minor versions advertised via classifiers
requires[], requiresCount, requiresOptionalCount, extrasParsed PEP 508 requirements; count of mandatory vs. extra-only deps
author, authorEmail, maintainer, maintainerEmail, owners[], organizationMetadata contacts as the project published them (Name <email> lists are split into names / e-mails) and PyPI account owners/maintainers. Names and e-mails are personal data: use them only for a legitimate purpose (e.g. reporting a security issue)
homepage, repository, documentation, changelog, issues, funding, projectUrlsNormalized links (synonyms mapped, GitHub inferred) + raw map
downloadsDay/Week/Month, downloadsByPythonMinor30d, downloadsBySystem30d, downloadsHistory, downloadsError, downloadsFetchedAtpypistats data for the whole package (all versions); downloadsError explains a null (new package, rate limit); downloadsFetchedAt = when pypistats answered (a 24 h cached answer keeps its time)
vulnerabilities[], vulnerabilityCountOSV advisories for the returned version: id, aliases, summary, details, fixedIn, link, withdrawn
versionsCount, firstRelease, lastRelease, daysSinceLastRelease, releasesLast90d, releasesLast365d, yankedVersionsCountRelease cadence
latestFiles, hasWheel, yanked, yankedReasonArtifacts of the returned version
recentVersions[]Release history entries
description, descriptionContentTypeOnly when descriptionChars > 0
pypiUrl, sourceUrl, fetchedAtProvenance

Feed items (recentUpdates / newPackages): id (name@version or name), feed, name, version, summary, author (the uploader's address as the feed publishes it, when any), publishedAt, pypiUrl, sourceUrl, fetchedAt.

Dataset views: overview, health (maintenance cadence), security (version, latest version, vulnerability count and advisories, yanked) and feed. The run's SUMMARY record (linked in the run output) lists delivered rows, packages not found or skipped (not charged), pypistats rate limits and input warnings.

Use it from code / agents

curl -X POST "https://api.apify.com/v2/acts/yadroo~pypi-package-intel/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"packages":["requests","fastapi"],"fields":["version","license","downloadsMonth","vulnerabilityCount"]}'
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/pypi-package-intel').call({ packages: ['langchain', 'llama-index'], downloadsBreakdown: true });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/pypi-package-intel").call(run_input={"packages": ["pillow==9.0.0"], "includeDownloads": False})
items = client.dataset(run["defaultDatasetId"]).list_items().items

MCP: add https://mcp.apify.com to your agent (Claude, Cursor, etc.) and call the yadroo/pypi-package-intel tool with the same JSON input.

Pricing

Pay-per-event: $0.001 per run start + $0.002 per item (package or feed item). Bronze −10 %, Silver −20 %, Gold and above −30 % on the item price; the start event is the same on every plan; platform usage is included. A due-diligence run on 10 packages costs about $0.021; a newPackages poll returning 40 items about $0.081. Packages/versions that do not exist, could not be fetched or repeat an earlier entry are not items and are not charged; batch names into one run to amortize the start fee. The run stops at your Maximum cost per run and says so in its status ("Stopped at your spending limit: N rows delivered"); the packages it did not reach are listed in SUMMARY.notReached (not charged).

Limits & FAQ

  • Rate limits. PyPI's JSON API is CDN-backed and generous. pypistats.org is a volunteer-run API with per-IP rate limits that asks for at most one fetch per package and day and points bulk users to BigQuery. The actor waits 3 s between pypistats requests (one per package; +2 with downloadsBreakdown, +1 with downloadsHistoryDays), honours its Retry-After, and when it answers 429 pauses all pypistats requests and retries those packages after the others — waiting at most 2 minutes per run in total. A package whose numbers still could not be fetched is returned with null counts and a downloadsError that says so, and the status message counts them. Metadata never depends on pypistats.
  • 24 h cache. pypistats answers are kept for 24 h in the key-value store pypi-package-intel-cache of the account that runs the actor, so a re-run the same day does not ask pypistats again (downloadsFetchedAt keeps the original time). Delete the store any time; the next run rebuilds it.
  • Capacity (measured 02.10.2026). With downloads on, about 12–15 packages a minute (the 3 s pypistats pacing); without downloads, about 50 a minute (big projects such as boto3 ship multi-MB metadata) — so a 15-minute run handles roughly 180 or 700 packages. A run that reaches its timeout stops starting new packages, saves what it has read and ends with "Stopped before the run timeout: N rows saved" — raise the timeout or split the list. For thousands of packages' download numbers use pypistats' BigQuery source, not this API.
  • Freshness. Metadata is live. Downloads are computed once a day by pypistats (BigQuery) with ~1–2 days lag and are per package, not per version. Feeds are near-real-time but only expose the latest ~40 events — poll every 5–15 min to avoid gaps in busy niches.
  • Vulnerabilities are what PyPI reports (OSV / PyPA advisory database) for the returned version; pin versions to audit older releases. An empty array means "none known", not "audited safe".
  • Unknown package or version → no item (not charged); it is listed in the SUMMARY record of the default key-value store (notFound[], errors[{input, error}] for failed lookups, invalid[] for lines that are not package names, duplicateEntries[], notReached[{input, reason}] for packages a spending limit, timeout or abort stopped before they were saved, with counts) and counted in the run's status message. The run continues with the remaining names and fails only when no package could be fetched.
  • Not supported: search by keyword across PyPI (no public API), per-version download counts, reverse dependencies (needs libraries.io key or deps.dev — roadmap), downloading/inspecting wheels, evaluating environment markers.
  • Roadmap: deps.dev dependents count, pyproject.toml / lockfile upload as input.

Made by Yadroo. Sibling actors: npm-package-intel · github-repo-intel · hackernews-search · stackexchange-search