# NPM & PyPI DevTools Package & Maintainer Intelligence (`afkdigital/npm-pypi-devtools-package-maintainer-intelligence`) Actor

Official NPM/PyPI metadata, public maintainer fields, NPM keyword search and comparable 7-day download growth. PyPI list mode; optional stats. JSON/CSV for Clay/n8n.

- **URL**: https://apify.com/afkdigital/npm-pypi-devtools-package-maintainer-intelligence.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 successful unique packages

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 DevTools Package & Maintainer Intelligence

Get structured public package metadata for developer-tool research, dependency inventories and ecosystem monitoring. Search the official NPM Registry or supply explicit NPM/PyPI names, then export JSON or CSV to Clay, n8n or your own analytics workflow.

**Price: $0.005 per successful unique package saved ($5 per 1,000).** No start fee, no charge for missing packages, filtered-out candidates, duplicate names or failed metadata lookups. Standard run platform usage is included. Deduplication is per run, including resumed runs, by ecosystem plus canonical package name. New runs are separately billable. Statistics can be null while the metadata row is still a successful billable result.

### Quick start

NPM defaults return five packages: eslint, typescript, prettier, vite and webpack.

```json
{"ecosystem":"npm","packageNames":["eslint","typescript","prettier","vite","webpack"],"maxResults":5,"maxCostUsd":0.025}
```

NPM discovery:

```json
{"ecosystem":"npm","keywords":["eslint"],"minWeeklyDownloads":1000,"maxResults":5,"maxCostUsd":0.025}
```

Reliable PyPI metadata mode (no statistics account or external statistics calls):

```json
{"ecosystem":"pypi","packageNames":["ruff","pytest","requests"],"maxResults":3,"pypiStatsSource":"none","maxCostUsd":0.015}
```

Optional PyPI statistics (third-party API; maximum ten names/results):

```json
{"ecosystem":"pypi","packageNames":["ruff"],"maxResults":1,"pypiStatsSource":"pypistats","maxCostUsd":0.005}
```

### Feature support

| Feature | NPM | PyPI |
| --- | --- | --- |
| Explicit package names | Yes, including scoped packages | Yes; required |
| Keyword discovery | Official registry search | Unsupported; nonempty keywords fail |
| Latest version and declared metadata | Official Registry JSON | Official PyPI JSON |
| Weekly downloads | Official NPM download API | Optional PyPI Stats; otherwise null |
| Comparable weekly growth | Two adjacent complete seven-day windows | Same, if PyPI Stats supplies the daily data |
| Download/growth filters | Applied to available stats | Requires explicit pypistats mode |
| Missing requested filter value | Explicit run error | Explicit run error |
| Author as verified maintainer | Never | Never |

The Actor does not download, install or execute any analyzed package, wheel, sdist, tarball, repository or install script. It reads JSON metadata only. It does not probe public contact addresses.

### Inputs

- `ecosystem`: `npm` or `pypi`. Run them separately; mixed ecosystem input is rejected.
- `keywords`: NPM-only array of up to five search strings, each 3–100 characters. Joined with spaces using the official registry search syntax. Up to 300 candidates are considered, in source relevance order; no exhaustive-search guarantee.
- `packageNames`: up to 100 names, processed first. PyPI normalization collapses runs of hyphens, underscores and dots and ignores case. Explicit NPM names are lowercased and scoped names are supported.
- `minWeeklyDownloads`: optional inclusive threshold, at least zero.
- `minGrowthPercent`: optional inclusive percentage; negative values allowed. Omitting it means no growth filtering, rather than a zero-growth threshold.
- `maxResults`: 1–100 successful unique rows, default 5. PyPI Stats mode permits at most 10.
- `maxCostUsd`: additional package event budget, default $0.10 and maximum $0.50. Rounded down to whole $0.005 rows; zero performs no package work. Apify's per-run maximum total charge also applies.
- `pypiStatsSource`: `none` by default, or explicitly `pypistats`. NPM requires `none`.

Unknown inputs, invalid names/types and unsupported filter combinations raise descriptive errors. Filters are never silently dropped. When a requested numeric filter cannot be evaluated because a package's statistics are unavailable, the run fails explicitly; earlier successfully saved rows remain available and billable.

### Output and provenance

Each row contains `package_name`, `ecosystem`, `latest_version`, `maintainer_names`, `maintainer_emails`, `homepage_url`, `repository_url`, `license`, `weekly_downloads`, `previous_weekly_downloads`, `growth_percent`, both periods' inclusive start/end dates, `statistics_source`, `statistics_status`, `statistics_fetched_at`, `maintainer_source`, `maintainer_verified`, `author_name`, `source_url` and `fetched_at`.

NPM maintainer fields come only from the official latest-release JSON maintainer list. These are release declarations and may not reflect current registry access rights. Using the bounded latest-release endpoint avoids downloading complete multi-version metadata histories. PyPI maintainer fields come only from release metadata's maintainer and maintainer_email fields, which can be stale or absent. **Author fields are kept separately and never promoted into maintainers.** Empty contact arrays mean the selected source field contains no usable public value. Emails are parsed only from explicit public maintainer contact fields; no inferred addresses, author-email fallback, profile scraping or enrichment.

`maintainer_verified` is always false: these are source declarations, not verification of current project authority, personal identity, inbox deliverability or consent to contact. A project is not classified as a commercial company, customer, revenue-generating business or sales lead.

Package license values describe the package's declared licensing and may be incomplete or stale. They do not license the registry API or metadata service. Read source service terms separately.

### Download counts and growth

NPM uses `https://api.npmjs.org/downloads/range/last-month/{package}`. The official API supplies daily counts. We choose the latest available completed UTC day and require all seven daily values for weekly downloads and all fourteen for growth. Source data more than four days behind returns null statistics. Dates in the output identify the exact inclusive windows:

`growth_percent = (current_weekly_downloads - previous_weekly_downloads) / previous_weekly_downloads * 100`

A zero previous count produces null growth, never infinity or an invented percentage. A missing day is not interpreted as zero. Missing statistics remain null, not zero. A current complete week can be available while the previous week or growth is null. We make no “fast-growing” claim unless two complete comparable periods support the reported growth. Download counts are not unique users, revenue, installations in use or commercial adoption. Counts and mirror conventions differ between ecosystems; do not treat them as directly equivalent.

PyPI JSON's deprecated downloads fields are always -1 and are not used. Optional PyPI Stats uses `https://pypistats.org/api/packages/{package}/overall?mirrors=false`, retaining only `without_mirrors` daily rows. This is an independent third-party service using PyPI download data, not the official metadata API. It updates daily and has application-wide IP rate limits without a published fixed numeric allowance on its API page. The Actor uses a dedicated persistent daily cache and at most ten packages per run. Shared-IP limits, delays or outages can produce null statistics and explicit warnings. Repeated runs on the same UTC day reuse cached successful responses.

The official scalable alternative is `bigquery-public-data.pypi.file_downloads`. **This Actor does not query BigQuery.** Direct BigQuery integration would require an authorized Google Cloud project/credentials and can incur billed query costs beyond free allowances. No Google account or BigQuery charges are needed for this Actor.

### Operational limits

Serial metadata requests use at least 1.05 seconds between starts, at most three attempts for safe GET/PUT operations, 15-second per-request timeouts, bounded exponential backoff and Retry-After handling. Responses are limited to 32 MiB; large metadata fails explicitly. One run has a 210-second HTTP deadline, stops starting package work after 195 seconds, and caps HTTP attempts at 500. Platform timeout is 240 seconds. Large or heavily filtered searches may return fewer than maxResults; maxResults is a ceiling.

Only fixed HTTPS metadata hosts are allowed. Redirects and package artifact requests are refused. Repository and homepage URLs are returned as metadata and are never visited. Dataset writes are not blindly retried after uncertain network failures; restart reconciliation reads existing ecosystem/name keys to prevent duplicate rows and billing.

`SUMMARY` in the key-value store records missing packages, statistics warnings, filtered counts, output count, elapsed time and request count. Failed lookups never become billable Dataset error rows. If every metadata lookup fails, the run fails.

### Clay, n8n and exports

Use the Output links or Dataset Export button to obtain JSON and CSV:

- `https://api.apify.com/v2/datasets/DATASET_ID/items?format=json&clean=true`
- `https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&clean=true`

In n8n, run this Actor, wait for completion, then fetch the run's default Dataset. In Clay, import the CSV or retrieve Dataset JSON with your existing authorized Apify connection. Keep API tokens secret. Arrays remain arrays in JSON; CSV exports use Apify's native array serialization.

### Source documentation and service terms

Reviewed 2026-11-02:

- [NPM Registry API](https://api-docs.npmjs.com/) and [download API specification](https://github.com/npm/registry/blob/main/docs/download-counts.md).
- [NPM Open Source Terms](https://docs.npmjs.com/policies/open-source-terms/) and [crawler policy](https://docs.npmjs.com/policies/crawlers/). Registry API 429 responses are respected; no universal fixed numeric registry allowance is assumed.
- [PyPI JSON API](https://docs.pypi.org/api/json/), [API policies](https://docs.pypi.org/api/) and [disabled XML-RPC search](https://dev.pypi.org/api-reference/xml-rpc/).
- [PyPI Terms of Service](https://policies.python.org/pypi.org/Terms-of-Service/). PyPI APIs prohibit spam-oriented data collection and selling users' personal information. Use this tool for technical package research, inventories and project maintenance. Do not use exports for spam, personal-information resale or contact-list marketing.
- [PyPI Stats API and etiquette](https://pypistats.org/api/): cache daily, avoid bulk database collection.
- [Official PyPI BigQuery datasets](https://docs.pypi.org/api/bigquery/) and [download analysis guide](https://packaging.python.org/en/latest/guides/analyzing-pypi-package-downloads/).
- [Apify pay-per-event documentation](https://docs.apify.com/actors/monetize/pay-per-event): one automatic default Dataset item event per saved unique package, $0.005. No separate synthetic start event.

Source availability and terms can change. This Actor is independent of npm, PyPI, PSF and PyPI Stats and does not imply their endorsement.

# Actor input Schema

## `ecosystem` (type: `string`):

Run one ecosystem at a time. PyPI requires explicit packageNames; it has no supported keyword search API.

## `keywords` (type: `array`):

NPM only: 1-5 search strings of 3-100 characters, joined with spaces using official registry search semantics. Explicit names are processed first, then up to 300 search candidates. PyPI rejects nonempty keywords.

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

Up to 100 names. PyPI requires this list, e.g. ruff, pytest, requests. NPM example: eslint, @typescript-eslint/parser. Duplicates/aliases emit and bill once per run. Empty NPM inputs use 5 devtools.

## `minWeeklyDownloads` (type: `integer`):

Optional inclusive filter. NPM official counts; PyPI requires pypistats mode. If stats are missing this filter raises an explicit error, never silently ignored.

## `minGrowthPercent` (type: `number`):

Optional inclusive filter from two complete adjacent 7-day periods. Negative thresholds supported. Unavailable growth (including zero prior baseline) raises an explicit error.

## `maxResults` (type: `integer`):

Maximum successful unique rows per run (not search candidates). PyPI Stats mode is capped at 10.

## `maxCostUsd` (type: `number`):

Additional per-run cap at $0.005/row, default $0.10. Your Apify maximum total charge is also respected. $0 produces no rows or charges.

## `pypiStatsSource` (type: `string`):

none: reliable metadata-only mode with null downloads/growth. pypistats: third-party PyPI Stats API, mirrors excluded, daily cache, max 10 packages per run; IP limits/outages can make stats unavailable. NPM requires none. No BigQuery queries or Google costs.

## Actor input object example

```json
{
  "ecosystem": "npm",
  "keywords": [],
  "packageNames": [],
  "maxResults": 5,
  "maxCostUsd": 0.1,
  "pypiStatsSource": "none"
}
```

# Actor output Schema

## `packagesJson` (type: `string`):

No description

## `packagesCsv` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/npm-pypi-devtools-package-maintainer-intelligence").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/npm-pypi-devtools-package-maintainer-intelligence").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 '{}' |
apify call afkdigital/npm-pypi-devtools-package-maintainer-intelligence --silent --output-dataset

```

## MCP server setup

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

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/eMFtAzbQBAfOxzhru/builds/s4jMXAhWFZThqdXp0/openapi.json
