npm Package Metadata API: PyPI & crates.io, $2/1K avatar

npm Package Metadata API: PyPI & crates.io, $2/1K

Pricing

from $1.40 / 1,000 package records

Go to Apify Store
npm Package Metadata API: PyPI & crates.io, $2/1K

npm Package Metadata API: PyPI & crates.io, $2/1K

Look up npm, PyPI and crates.io packages by name or keyword. One flat row per package with latest version, license, dependencies, release dates, download counts and links. Public registry APIs, $2.00 per 1,000 packages.

Pricing

from $1.40 / 1,000 package records

Rating

0.0

(0)

Developer

Don Mangu

Don Mangu

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Share

Package metadata API for npm, PyPI and crates.io that returns one flat row per package: latest version, license, dependencies, release dates, download counts and links, ready for a spreadsheet, a database or an AI agent. List package names or type a keyword, set a maximum and run. Results cost $2.00 per 1,000 packages, and the three-package sample run costs less than one cent. I built this Actor as an independent developer. It is not affiliated with or endorsed by npm, the Python Software Foundation, PyPI or crates.io.

Sample output

One row from an npm package metadata lookup for express. Values were read from the registry on 2026-10-04 and change over time. Dependencies are cut to the first two for space.

{
"registry": "npm",
"name": "express",
"inputName": "express",
"status": "ok",
"message": null,
"url": "https://www.npmjs.com/package/express",
"latestVersion": "5.2.1",
"latestReleaseDate": "2025-12-01T20:49:43.268Z",
"firstReleaseDate": "2010-12-29T19:38:25.450Z",
"versionCount": 289,
"license": "MIT",
"description": "Fast, unopinionated, minimalist web framework",
"keywords": ["express", "framework", "sinatra", "web", "http"],
"homepageUrl": "https://expressjs.com/",
"repositoryUrl": "https://github.com/expressjs/express",
"documentationUrl": null,
"deprecated": false,
"deprecationMessage": null,
"runtimeRequirement": "node >= 18",
"packageSizeBytes": 75429,
"downloadsWeekly": 165798136,
"downloadsMonthly": 566208091,
"downloadsLast90Days": null,
"downloadsTotal": null,
"dependentsCount": 108273,
"dependencyCount": 28,
"optionalDependencyCount": 0,
"devDependencyCount": 16,
"peerDependencyCount": 0,
"dependencies": [
{ "name": "accepts", "range": "^2.0.0", "kind": "runtime", "condition": null },
{ "name": "body-parser", "range": "^2.2.1", "kind": "runtime", "condition": null }
],
"sourceUrl": "https://registry.npmjs.org/express",
"fetchedAt": "2026-10-04T07:50:26.667Z"
}

How to get npm, PyPI and crates.io package metadata

  1. Open the Actor and add your Package names. Write one name per entry, for example express, @types/node or django.
  2. Put npm:, pypi: or crates: in front of a name to pick its registry, for example pypi:requests or crates:serde. Names without a prefix use the registries under Advanced options (npm by default).
  3. Or type Search text, for example http client, to find packages on npm and crates.io. Found packages are added after your listed names.
  4. Set Maximum packages. The prefill is 10, so the first run is cheap. One run returns up to 1,000 rows.
  5. Click Start. Rows appear in the dataset as they are saved. Use the Overview table, or export to JSON, CSV or Excel.

You can also call the Actor from code through the Apify API. The example below checks six packages from three registries.

curl -X POST "https://api.apify.com/v2/acts/conserving_celerytop~package-registry-metadata-api/run-sync-get-dataset-items" \
-H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"packageNames": ["npm:react", "npm:next", "pypi:django", "pypi:fastapi", "crates:tokio", "crates:axum"], "maxItems": 6}'

What package metadata you get

FieldMeaning
registry, name, inputName, urlThe registry, the package name as the registry spells it, the name you typed, and the package page
status, messageok, not_found or error, with a short message when the row is not ok
latestVersion, latestReleaseDate, firstReleaseDate, versionCountNewest version, when it was published, when the package first appeared, and how many versions exist
licenseThe license as the package declares it, usually an SPDX id such as MIT or MIT OR Apache-2.0
description, keywordsThe short description and up to 30 keywords
homepageUrl, repositoryUrl, documentationUrlLinks the package lists, with git addresses turned into plain https links
deprecated, deprecationMessagenpm deprecation notice, a yanked release, or a crates.io yanked version
runtimeRequirementMinimum runtime the package states: node, python or rust
packageSizeBytesnpm unpacked size, crate file size, or the PyPI source archive size
downloadsWeekly, downloadsMonthlynpm downloads in the last 7 and 30 days
downloadsLast90Days, downloadsTotalcrates.io downloads in the last 90 days and in total
dependentsCountNumber of npm packages that depend on this one
dependencyCount, optionalDependencyCount, devDependencyCount, peerDependencyCount, dependenciesCounts and the list of runtime dependencies of the latest version, each with name, version range, kind and condition
sourceUrl, fetchedAtThe registry address that was read and the time of the read

Each registry publishes different download windows, so a field the registry does not offer is null. PyPI publishes no download counts through its JSON API, so its download fields are always null.

Author and maintainer names and e-mail addresses are never read or saved.

Pricing

This Actor uses pay per event. One event is one package row saved to the dataset.

PlanPrice per packagePrice per 1,000 packages
Free$0.0020$2.00
Bronze$0.0018$1.80
Silver$0.0016$1.60
Gold$0.0014$1.40

There is also a start fee of $0.00005 per run.

Worked example: a license audit of 500 packages on the Free plan saves 500 rows, which is 500 x $0.002 = $1.00, plus $0.00005 for the start. The three-package sample run costs 3 x $0.002 = $0.006 plus the start. A name that is not found is saved as one not_found row and counts as one package. A run that stops because the registry is down saves an error row and is not charged for it. You can set a maximum charge for a run, and the Actor stops when it is reached.

Input example

{
"packageNames": ["npm:express", "pypi:requests", "crates:serde"],
"maxItems": 10
}

Search by keyword on npm and crates.io:

{
"searchQuery": "http client",
"registries": ["npm", "crates"],
"maxItems": 40
}

Output example

A crates.io row for serde (shortened): latestVersion 1.0.229, license "MIT OR Apache-2.0", firstReleaseDate 2014-12-05, downloadsTotal 1,478,260,695, downloadsLast90Days 340,421,315, dependencyCount 1 (serde_core), optionalDependencyCount 1. A PyPI row for requests has latestVersion 2.34.2, license "Apache-2.0", runtimeRequirement "python >=3.10", versionCount 160 and null download fields.

What you can do with package metadata

  • Audit the licenses of the packages your team uses, across JavaScript, Python and Rust in one table.
  • Track new releases and deprecated packages by running the Actor on a schedule and comparing rows.
  • Build a dependency inventory with version ranges for each package.
  • Compare package age, release count and download counts when you choose between libraries.
  • Feed registry facts to an AI agent or a dashboard through the Apify API.

Run it every day

Switch on Only new since the last run and the Actor saves a row only for a package that has a version it has not delivered to you before. Use it to watch a list of packages for new releases.

  1. Fill the input with your package names. Set Maximum packages to at least the number of rows you expect to save in one run.
  2. Switch on Only new since the last run and enter a Monitor name, for example my-stack. Use one name per saved task.
  3. Click Save as task and run it once. The first run saves one row for every package and becomes the baseline.
  4. In the Apify Console, open Schedules, click Create new, pick your Task, and enter a cron expression for once a day, for example 0 6 * * *.

Each later run looks up every package, then saves only the rows whose latestVersion is new. Rows skipped as already delivered are not charged. In this mode Maximum packages caps the rows saved, not the names looked up, so a long list is never cut. If more rows are new than the maximum, the rest come with the next run. A name that is not found is saved once. A row for a registry that could not be read is saved without a charge and is not remembered, so the next run tries that package again.

The memory lives in your own key-value store, named pkg-monitor-<monitor name>. It holds ids only, such as npm|express|5.1.0: registry, package name and version. If you change the registries or the search text, the Actor starts a separate memory for that setting, and its first run saves every package again. You can add and remove names without losing the memory.

Worked cost example on the Free plan, with the prices in the Pricing section ($0.002 per row, $0.00005 for the start). Say you watch 200 packages and on a day 6 of them release a new version. That day costs 6 x $0.002 = $0.012 plus $0.00005, so $0.01205. Thirty such days cost 30 x $0.01205 = $0.3615. The first run costs 200 x $0.002 = $0.40 plus the start fee. A day with no new version costs only the start fee of $0.00005. The same 200 packages run daily without Only new save 200 rows every day: 200 x $0.002 = $0.40 plus $0.00005, so $0.40005 a day and 30 x $0.40005 = $12.0015 in 30 days. The 6 releases per day is an example, not a measured value.

Bulk lists

You can give the Actor a long list of package names from a file or a Dataset instead of typing them. You can use the typed list, a Dataset and a file at the same time. The Actor joins them and removes duplicates.

  • Dataset with the list: pick an Apify Dataset in the Console, or paste its ID. In Field in the dataset, enter the name of the field that holds the package name, for example package. Entries can carry a prefix, for example pypi:requests.
  • List file URL: the web address of a .txt, .csv or .json file. A .txt file has one name per line. A .csv file uses the first column. A .json file is an array of strings or objects. The first row is skipped when it is a header such as name, id or package. The file can be up to 10 MB.

Up to 1,000 names per run, as with typed names. The price is the same: $0.002 per package row on the Free plan. A name that is not found is one row, as before. Without Only new, Maximum packages still limits the run (the default is 20), so raise it to the number of names in your list. If it is lower, the run message says how many names were left out.

FAQ

Where does the package metadata come from?

From the public APIs of the three registries: registry.npmjs.org and its search endpoint for npm, pypi.org/pypi/{name}/json for PyPI, and the crates.io API plus the crates.io sparse index for crates.io. The Actor uses no login, no cookies, no proxy and no browser.

The Actor reads public package metadata through the registries' documented APIs. The npm Open Source Terms (last updated March 10, 2022) say: "You may search for, download, publish, and manage Packages using software other than CLI via application programming interfaces that npm publicly documents or makes available for public use." The PyPI Acceptable Use Policy says: "Scraping does not refer to the collection of information through our API." The crates.io data access page offers its API on two conditions, "A maximum of 1 request per second" and "A user-agent header that identifies your application", and this Actor follows both. The same page asks clients to try the sparse index first, and the Actor reads dependencies from it. The Actor saves no personal data about people: author and maintainer names and e-mail addresses are dropped. Package licenses apply to the packages themselves, so check them before you reuse package contents. This is not legal advice.

How fast is it, and what are the limits?

Each registry has its own pace. crates.io is read at most once per 1.1 seconds, PyPI at about 1.4 requests per second, and npm at a light pace with three lookups at a time. In practice 1,000 npm packages take about three minutes, 1,000 PyPI packages about 12 minutes and 1,000 crates about 18 minutes. One run saves up to 1,000 rows. Run the Actor again for more.

Why are the download fields empty for some rows?

Each registry offers different numbers. npm gives 7 and 30 day downloads and a dependents count. crates.io gives total and 90 day downloads. PyPI gives none through its JSON API. For a few npm packages, such as deprecated ones, the npm search does not list the package, and the Actor then asks the npm downloads API. If that also has no answer, the fields stay null.

Why do I get a not_found row?

The registry has no package with that name, or the npm package was unpublished. Check the spelling and the registry prefix. A name without a prefix is looked up in the registries you chose under Advanced options.

What does an error row mean?

The registry did not answer after three tries, for example because of an outage. The row says so, and it is not charged. Run the Actor again later. If a registry fails five times in a row, the Actor stops reading that registry and ends the run with the rows it has saved.

Troubleshooting

  • The run ended at once with "Input needs a change": the message names the entry to fix, such as a name with a space or a maximum above 1,000.
  • A crates.io run is slower than an npm run: crates.io asks clients to stay at one request per second.
  • A run stopped with the status charge_limit: the maximum charge you set was reached, and the saved rows are complete.
  • A run was interrupted and restarted: it continues from the saved state and does not save or charge a package twice.

If this saved you time, a rating helps others find it.