# MetaCPAN Perl Module Scraper - CPAN Distributions & Deps (`ninhothedev/metacpan-scraper`) Actor

$0.5/1K 🔥 MetaCPAN scraper! Perl distributions — version, author, license & dependencies. No key. JSON, CSV, Excel or API in seconds. Audit Perl deps & discover modules ⚡

- **URL**: https://apify.com/ninhothedev/metacpan-scraper.md
- **Developed by:** [ninhothedev](https://apify.com/ninhothedev) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## MetaCPAN Perl Module Scraper

Scrape **Perl distributions and modules from MetaCPAN** — version, author, license, abstract, release date, repository, bug tracker, download URL and dependency data — with **no API key, no login and no proxy required**.

This Actor talks directly to the public MetaCPAN API (`fastapi.metacpan.org`), the same backend that powers [metacpan.org](https://metacpan.org). It returns one clean, flat row per CPAN release, ready for CSV, JSON, Excel or your data warehouse.

***

### What this Actor does

| | |
|---|---|
| **Source** | MetaCPAN public API (CPAN — the Comprehensive Perl Archive Network) |
| **API key** | Not needed |
| **Proxy** | Not needed (datacenter-clean) |
| **Output** | One item per release (distribution at its latest version) |
| **Speed** | ~50 releases per request, paginated automatically |
| **Cost** | Roughly **$0.5 per 1,000 releases** |

***

### Features

- **Keyword search** — query CPAN like you would on metacpan.org and get the latest release of every matching distribution.
- **Direct distribution lookup** — pass exact distribution names (`Mojolicious`, `DBI`, `Plack`) and get their current release.
- **Dependency intelligence** — total dependency count plus the explicit list of `runtime` / `requires` modules.
- **Licensing data** — the declared license list (`perl_5`, `artistic_2`, `mit`, …) for compliance and OSS audits.
- **Repository & issue links** — GitHub/GitLab repository, bug tracker and homepage, pulled from the distribution metadata.
- **Only latest releases** — results are filtered to `status:latest`, so you never get stale versions polluting your dataset.
- **Robust by design** — Chrome-impersonating HTTP client with a plain-`urllib` fallback, retries with exponential backoff, and graceful handling of missing fields.

***

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `search` | `search` (keyword queries) or `distributions` (exact names) |
| `queries` | array | `["JSON", "Mojolicious"]` | Keywords or module names to search, used in `search` mode |
| `distributions` | array | — | Exact distribution names, used in `distributions` mode (use dashes: `List-Util`) |
| `maxItems` | integer | `100` | Maximum releases to push (max `1000`) |

#### Example — search mode

```json
{
  "mode": "search",
  "queries": ["JSON", "async", "web framework"],
  "maxItems": 300
}
```

#### Example — distributions mode

```json
{
  "mode": "distributions",
  "distributions": ["Mojolicious", "DBI", "Plack", "Moose"],
  "maxItems": 100
}
```

***

### Output

Every item is one CPAN release:

```json
{
  "name": "Mojolicious-9.48",
  "distribution": "Mojolicious",
  "version": "9.48",
  "author": "SRI",
  "abstract": "Real-time web framework",
  "license": ["artistic_2"],
  "date": "2026-07-14T12:53:44+00:00",
  "status": "latest",
  "maturity": "released",
  "main_module": "Mojolicious",
  "download_url": "https://cpan.metacpan.org/authors/id/S/SR/SRI/Mojolicious-9.48.tar.gz",
  "repository": "https://github.com/mojolicious/mojo",
  "bugtracker": "https://github.com/mojolicious/mojo/issues",
  "homepage": "https://mojolicious.org",
  "dependency_count": 5,
  "runtime_requires": ["IO::Socket::IP", "Pod::Simple"],
  "provides_count": 112,
  "url": "https://metacpan.org/dist/Mojolicious",
  "source": "metacpan",
  "scraped_at": "2026-07-28T12:00:00+00:00"
}
```

#### Field reference

| Field | Description |
|---|---|
| `name` | Full release name including version (`JSON-4.11`) |
| `distribution` | Distribution name without version (`JSON`) |
| `version` | Release version string |
| `author` | PAUSE ID of the uploading author (`ISHIGAKI`) |
| `abstract` | Short description, capped at 2,000 characters |
| `license` | List of declared licenses |
| `date` | Release timestamp, normalised to ISO 8601 UTC |
| `status` | Release status (always `latest` in search mode) |
| `maturity` | `released` or `developer` |
| `main_module` | Primary module namespace (`JSON::XS`) |
| `download_url` | Direct tarball URL on cpan.metacpan.org |
| `repository` | Source repository web URL |
| `bugtracker` | Issue tracker URL |
| `homepage` | Project homepage |
| `dependency_count` | Number of declared dependencies across all phases |
| `runtime_requires` | Module names required at runtime |
| `provides_count` | Number of modules the distribution provides |
| `url` | Canonical metacpan.org page |
| `source` | Always `metacpan` |
| `scraped_at` | UTC timestamp of the scrape |

All fields are nullable — missing metadata comes back as `null` rather than breaking your pipeline.

***

### Use cases

- **Perl dependency research** — map which distributions depend on which runtime modules, spot heavy dependency trees, and audit supply chains before adopting a library.
- **Package discovery** — find every CPAN distribution matching a topic (`JSON`, `ORM`, `async`) with authors, versions and activity dates in one table.
- **Open-source intelligence** — track license distribution across the Perl ecosystem, find abandoned distributions by release date, or identify the most prolific PAUSE authors.
- **Developer tooling** — feed internal package registries, dashboards, dependency scanners, changelog bots or documentation portals with live CPAN metadata.

***

### Pricing

Pay-per-event style economics: expect roughly **$0.5 per 1,000 releases** scraped. A typical 100-release run finishes in seconds on the 512 MB default memory setting.

***

### Related Actors

If you're mapping package ecosystems beyond Perl, these pair well:

- [Hex.pm Scraper](https://apify.com/ninhothedev/hex-pm-scraper) — Elixir & Erlang packages
- [pkg.go.dev Scraper](https://apify.com/ninhothedev/pkg-go-dev-scraper) — Go modules
- [npms Search Scraper](https://apify.com/ninhothedev/npms-search-scraper) — npm / JavaScript packages
- [crates.io Scraper](https://apify.com/ninhothedev/crates-io-scraper) — Rust crates

***

### Notes & limitations

- Only the **latest** release of each distribution is returned; historical versions are out of scope.
- Search results come from MetaCPAN's ElasticSearch index and are ordered by relevance.
- MetaCPAN is a public, free service — please keep run volumes reasonable.
- This Actor scrapes only publicly available, openly licensed package metadata. No personal data beyond public PAUSE author IDs is collected.

### FAQ

**Do I need a MetaCPAN account or API key?**
No. The MetaCPAN API is fully public and this Actor uses no credentials.

**Can I look up a module instead of a distribution?**
Use `search` mode with the module name (e.g. `JSON::XS`) — the matching distribution will be returned. In `distributions` mode you must use the dashed distribution name (`JSON-XS`).

**Why is `runtime_requires` empty for some distributions?**
Some distributions declare no runtime requirements, or only declare build/configure/test phase dependencies. `dependency_count` still reflects the full declared set.

# Actor input Schema

## `mode` (type: `string`):

How to collect releases. 'search' runs keyword queries against MetaCPAN and returns the latest release of every matching distribution. 'distributions' looks up specific distribution names directly (one item per name).

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

Keywords or module names to search on MetaCPAN, used when mode is 'search'. Each query returns the latest releases of matching Perl distributions, paginated until maxItems is reached. Examples: JSON, Mojolicious, DBI, async, web framework.

## `distributions` (type: `array`):

Exact CPAN distribution names to look up, used when mode is 'distributions'. Use dashes instead of double colons (List-Util, not List::Util). Each name yields one item with its latest release. Examples: Mojolicious, JSON, DBI, Plack.

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

Maximum number of releases to push to the dataset across all queries or distributions. Keeps runs cheap and predictable. Minimum 1, maximum 1000.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "JSON",
    "Mojolicious"
  ],
  "distributions": [
    "Mojolicious"
  ],
  "maxItems": 100
}
```

# 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": [
        "JSON",
        "Mojolicious"
    ],
    "distributions": [
        "Mojolicious"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ninhothedev/metacpan-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 = {
    "queries": [
        "JSON",
        "Mojolicious",
    ],
    "distributions": ["Mojolicious"],
}

# Run the Actor and wait for it to finish
run = client.actor("ninhothedev/metacpan-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 '{
  "queries": [
    "JSON",
    "Mojolicious"
  ],
  "distributions": [
    "Mojolicious"
  ]
}' |
apify call ninhothedev/metacpan-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ninhothedev/metacpan-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/q9V7DdX1FQ3feeaNR/builds/0GHtblNJ2DAVyl2Ri/openapi.json
