# Package Publisher Data — npm, PyPI & Docker Hub (`foxlabs/package-registry-publisher-data`) Actor

Check which public package registries a company publishes to. Searches npm, PyPI and Docker Hub for an organization's packages and returns package names, versions, descriptions, licences, download or pull counts, maintainers, repository links and last-publish dates.

- **URL**: https://apify.com/foxlabs/package-registry-publisher-data.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 package records

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

## Package Publisher Data — npm, PyPI & Docker Hub

Whether a company publishes SDKs on npm, PyPI or Docker Hub — and how recently — is a precise read on whether it has a real developer-facing product. This actor checks all three registries for one organization name and returns what it finds, with versions, licences, npm download and dependent counts, package status and last-publish dates.

**No API key · Official source · Pay only for delivered rows · Same schema across the series**

### What data do you get?

| Field | Description |
|---|---|
| `companyName` | Organization the package belongs to |
| `status` | `active`, or `inactive` when the registry marks the package deprecated, yanked, inactive or archived |
| `statusRaw` | The registry's own wording: PyPI development status (*5 - Production/Stable*), *yanked*, *deprecated*, or the Docker Hub repository status (*archived*) |
| `registryName` | `npm`, `pypi` or `docker` |
| `packageName` | Package or image name |
| `version` | Latest published version |
| `description` | Package description |
| `licenseName` | Declared licence (npm and PyPI; Docker Hub has none) |
| `downloadsLastMonth` | npm downloads over the last month |
| `dependentCount` | npm packages that depend on this one |
| `keywords` | Package keywords |
| `publishers` | Maintainer or publisher names |
| `website`, `websiteSource` | The organization's website: the domain carrying its name that its package homepages point to most often — else, with a GitHub token, the website on its GitHub profile when it is such a domain |
| `email`, `emailSource` | Contact address: the latest publisher or first maintainer (npm), the maintainer or author (PyPI); else the email the organization's website publishes on its own domain; else the GitHub profile's public email |
| `phone`, `phoneSource` | The phone the organization's website states (`tel:` link or JSON-LD, homepage or contact page) |
| `githubOrganization`, `githubName`, `githubUrl`, `githubLocation`, `githubEmail`, `githubVerified`, `githubPublicRepos`, `githubFollowers`, `githubCreatedAt` | The organization's public GitHub profile — only when you give a `githubToken`. `city` and `countryName` come from its location when it reads as *City, State* or *City, Country* |
| `maintainers` | Every maintainer with a reachable address, as `{name, email}` |
| `repositoryUrl` | Source repository link where declared |
| `homepage` | Package homepage |
| `pullCount` | Docker Hub pull count, where applicable |
| `starCount` | Docker Hub star count, where applicable |
| `lastPublishedAt` | Most recent publish or update |
| `sourceUrl` | Package page on the registry |

Every row also carries `query` (what you asked for), `scrapedAt` (ISO timestamp) and, when a
lookup fails, `error` explaining why.

**Not applicable to a package.** The company-register fields shared across this series
(`registrationNumber`, `taxNumber`, `legalForm`, `incorporatedOn`, `dissolvedOn`, `address`,
`postalCode`, `industry`, `industryCode`, `employees`, `capital`, `officers`) stay `null` on
every row; `city` and `countryName` are filled only from a GitHub profile.

### Example output

A real row from the prefilled run (addresses masked here; the dataset carries them in full):

```json
{
  "country": "GLOBAL",
  "registry": "npm / PyPI / Docker Hub",
  "companyName": "stripe",
  "status": "active",
  "registryName": "npm",
  "packageName": "stripe",
  "version": "22.6.2",
  "description": "Stripe API wrapper",
  "licenseName": "MIT",
  "downloadsLastMonth": 72377456,
  "dependentCount": 2672,
  "keywords": ["stripe", "payment processing", "credit cards", "api"],
  "publishers": ["GitHub Actions", "stripe-bindings"],
  "website": "https://stripe.com",
  "email": "d•••@stripe.com",
  "maintainers": [{ "name": "stripe-bindings", "email": "d•••@stripe.com" }],
  "repositoryUrl": "git+https://github.com/stripe/stripe-node.git",
  "homepage": "https://github.com/stripe/stripe-node",
  "lastPublishedAt": "2026-09-09",
  "sourceUrl": "https://www.npmjs.com/package/stripe"
}
```

### Input

```json
{
  "queries": ["stripe","hashicorp","vercel"],
  "maxResultsPerQuery": 30,
  "maxConcurrency": 3,
  "includeRaw": false
}
```

| Input | What it does |
|---|---|
| `queries` | Organization or npm scope names (`stripe`, `hashicorp`, `google`). Registries are checked in parallel for each name. |
| `maxResultsPerQuery` | Caps how many rows one query may produce. |
| `maxConcurrency` | How many queries run at once. Lower it if the source starts throttling. |
| `includeRaw` | Attaches the source's untouched record under `raw`, for fields this actor does not map. |
| `requestDelayMs` | Politeness delay between requests. |
| `proxyConfiguration` | Optional. All three registries answer datacenter IPs directly. |
| `githubToken` | Optional. A GitHub personal access token (no scopes needed; only public data is read). With it, the GitHub organization the packages' repositories point to adds its profile fields. Without it, GitHub is not called. Stored encrypted and sent only to api.github.com. |

### What people use it for

- **Developer-product qualification** — a company with maintained SDKs on all three registries has a real API product; one with none does not.
- **Technographic enrichment** — package languages and dependencies say what a company builds with.
- **Ecosystem mapping** — find every package published under an organization scope, with licences.

### Notes and limits

- npm is searched by scope and by maintainer, so both `@stripe/...` packages and unscoped ones the organization maintains come back.
- PyPI has no organization index, so the actor probes the obvious project names and then verifies provenance: a project is only attributed to the company when the company owns something on it — an address at its domain, a homepage on its domain, a repository under its account, or its name as author. That avoids crediting an unrelated project that happens to share the name (Hive Solutions' `stripe-api` is not Stripe's).
- **Maintainer addresses are personal data.** npm shows them on every package page and PyPI in each project's metadata; this actor returns them as published, skipping bot and no-reply addresses. If you store or contact them, data-protection and anti-spam rules (GDPR, CAN-SPAM) apply to you.
- Package status comes from each registry's own signal: npm's search leaves deprecated packages out altogether (so npm rows are live packages), PyPI's from the *Development Status* classifier and yanked releases, Docker Hub's from the repository status.

### Where the data comes from

npm, PyPI and Docker Hub all serve public package metadata through documented APIs with no key. Source: [npm registry, PyPI JSON API and Docker Hub API](https://registry.npmjs.org/)

### FAQ

#### Is this package registry publisher scraper free?

The data source is free and needs no API key — you pay only for the rows the run delivers ($0.002 each). Failed or empty lookups are never charged.

#### Do I need an API key or a login?

No. npm, PyPI and Docker Hub all serve public package metadata through documented APIs with no key.

#### What can I search by?

By organization or scope name (`stripe`, `google`, `hashicorp`). All three registries are checked for each query.

#### How current is the data?

Every run queries the source live, so results are as fresh as the source itself. All three registries answer live.

#### How fast is it, and how many queries can I run?

Queries run concurrently (3 at a time by default, tunable in the input). A prefilled run finishes in seconds; large lists scale roughly linearly and stay well inside a normal run timeout.

#### Can I export the results to CSV, Excel or JSON?

Yes. Apify datasets export to CSV, Excel, JSON, XML and HTML, and can be pulled through the API or pushed to your own storage.

#### What happens when a query returns nothing?

You still get a row, carrying your original `query` and an `error` field explaining why. Nothing is silently dropped, and you are not charged for it.

#### Is scraping this data legal?

Yes. Public package metadata is published by each registry precisely so it can be indexed and reused.

### Changelog

#### 0.1.11 — 2026-09-25 — website contacts, optional GitHub profile

- **`phone` and a fallback `email` from the organization's website**: only `mailto:`/`tel:` links and schema.org JSON-LD on the homepage or its contact page, an email only on the organization's own domain; package maintainers' addresses still come first. New `emailSource`, `phoneSource`, `websiteSource`.
- **Optional `githubToken` input.** With a token, the GitHub organization the packages' repositories point to (never a guess from the name, never a personal account) adds `githubOrganization`, `githubName`, `githubUrl`, `githubLocation`, `githubEmail`, `githubVerified`, `githubPublicRepos`, `githubFollowers`, `githubCreatedAt`, `city`/`countryName` when its location is a clear *City, State/Country*, and a website when the packages give none. Without a token nothing changes and GitHub is not called.
- No pricing change.

#### 0.1.10 — 2026-09-25 — organization website

- **`website` is filled** with the organization's own site: the domain carrying its name that its package homepages point to most often (`stripe` → https://stripe.com, `vercel` → https://vercel.com), on every row of the query. Homepages on GitHub, a registry or a product domain give none — `hashicorp`'s packages point to GitHub, so its rows stay empty. 0% → 67% in the prefilled run.
- No pricing change.

#### 0.1.9 — 2026-09-25 — maintainer contacts; stricter PyPI attribution

- **New: `email` and `maintainers`.** npm lists every maintainer with the address shown on the package page, and PyPI carries author and maintainer addresses; both are now returned (`maintainers`: `{name, email}` for each, `email`: the first). Bot and no-reply addresses — npm's trusted-publishing bot, GitHub's noreply relay — are skipped. In the prefilled run 74% of rows carry an address (every npm and PyPI row; Docker Hub publishes none).
- **PyPI attribution is stricter.** A project is credited to an organization only when the organization owns something on it — an address at its domain, a homepage on its domain, a repository under its account, or its name as author. Mentioning the name was enough before, which credited Hive Solutions' `stripe-api` to Stripe.
- No pricing change.

#### 0.1.8 — 2026-09-25 — licence, status and npm popularity filled

- **`licenseName` is filled for npm and PyPI** (1% of rows before, 72% in the prefilled run; Docker Hub publishes no licence). npm's search result carries the licence; PyPI's is read from `license_expression`, a short `license`, or the `License ::` classifier, where `stripe` declares it.
- **`status` means something now.** It was `unknown` on every row; it is `inactive` when the registry marks a package deprecated, yanked, inactive or archived (Docker Hub `stripe/veneur`: *archived*), with the registry's own wording in `statusRaw`.
- New npm fields `downloadsLastMonth` and `dependentCount`, from the same search response — no extra requests.
- The company-register fields that cannot apply to a package are labelled as such in the dataset schema.
- README example replaced with a real row. No pricing change.

***

Built by [Fox Labs](https://apify.com/foxlabs) — B2B company intelligence from public sources, as clean JSON.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/package-registry-publisher-data/changelog.md

# Actor input Schema

## `queries` (type: `array`):

Organization or npm scope names (`stripe`, `hashicorp`, `google`). Registries are checked in parallel for each name.

## `maxResultsPerQuery` (type: `integer`):

How many rows a single query may produce.

## `maxConcurrency` (type: `integer`):

How many queries to run at the same time. Lower it if the source throttles you.

## `includeRaw` (type: `boolean`):

Attach the source's untouched response under `raw`. Useful when you need a field this actor does not map.

## `requestDelayMs` (type: `integer`):

Politeness delay against a public source. Raise it for large runs.

## `proxyConfiguration` (type: `object`):

Optional. All three registries answer datacenter IPs directly.

## `registries` (type: `array`):

Which package registries to search.

## `githubToken` (type: `string`):

Optional. A GitHub personal access token — no scopes needed, only public data is read. With it, each organization's public GitHub profile (the one its packages' repositories point to) adds location, public email, domain verification, repository and follower counts, and a website when the packages give none. Without it, GitHub is not called. Stored encrypted by Apify and sent only to api.github.com.

## Actor input object example

```json
{
  "queries": [
    "stripe",
    "hashicorp",
    "vercel"
  ],
  "maxResultsPerQuery": 30,
  "maxConcurrency": 3,
  "includeRaw": false,
  "requestDelayMs": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "registries": [
    "npm",
    "pypi",
    "docker"
  ]
}
```

# Actor output Schema

## `dataset` (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 = {
    "queries": [
        "stripe",
        "hashicorp",
        "vercel"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/package-registry-publisher-data").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 = { "queries": [
        "stripe",
        "hashicorp",
        "vercel",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("foxlabs/package-registry-publisher-data").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 '{
  "queries": [
    "stripe",
    "hashicorp",
    "vercel"
  ]
}' |
apify call foxlabs/package-registry-publisher-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,foxlabs/package-registry-publisher-data"
        }
    }
}
```

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/yCXWbfaiszequQM8e/builds/NDK8nE9f4Tm7U9te3/openapi.json
