# WordPress Plugin Directory Scraper (`isaac_ship/wordpress-plugin-directory-scraper`) Actor

Extract the WordPress.org plugin directory with derived maintenance signals — staleness, support resolution rate and an opportunity score that surfaces popular but neglected plugins.

- **URL**: https://apify.com/isaac\_ship/wordpress-plugin-directory-scraper.md
- **Developed by:** [Isaac Shipman](https://apify.com/isaac_ship) (community)
- **Categories:** Developer tools, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 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/actors/running/actors-in-store.md#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

### What does WordPress Plugin Directory Scraper do?

It extracts the **[WordPress.org plugin directory](https://wordpress.org/plugins/)** — roughly **65,000 plugins** — as a clean, structured dataset, and adds the **maintenance and opportunity signals the official API doesn't give you**: how long since the last release, whether the plugin is stale, how much of its support queue actually gets resolved, and a single `opportunity_score` that ranks *popular but neglected* plugins.

Run it with no configuration at all and you get the 100 most-installed plugins with every derived signal attached. Change one number and you get the whole directory.

Because it runs on Apify you get scheduled runs, a REST API, webhooks, and export to JSON, CSV, Excel or HTML without writing any glue code.

### Why use WordPress Plugin Directory Scraper?

- **Find a gap before you build.** Sort by `opportunity_score` to surface plugins with a large install base, a mediocre rating, no release in years and an unanswered support forum. That is a market, not a data dump.
- **Competitive research.** Pull every plugin for a tag or an author and compare install counts, ratings, versions and WordPress compatibility side by side.
- **Track abandonment.** `days_since_update` and `is_stale` turn "is this plugin still maintained?" into a filterable column across the whole directory.
- **Security and compatibility inventories.** `version`, `tested`, `requires` and `requires_php` for every plugin, refreshed on a schedule.
- **Lead lists for agencies.** Authors of large, ageing plugins are people who may need help.

### How to use WordPress Plugin Directory Scraper

1. Click **Try for free** / **Start**.
2. Leave everything on its default to get the 100 most popular plugins, or pick a **Mode** — search a keyword, filter by tag or author, or name exact plugin slugs.
3. Set **Max results**. 100 is the default; the whole directory is about 65,000.
4. Optionally set **Minimum active installs** to ignore the long tail of tiny plugins.
5. Click **Save & Start**, then open the **Output** tab or download the dataset as JSON, CSV, HTML or Excel.

To keep a dataset current, schedule the Actor daily or weekly — the directory changes constantly.

### Input

Configure it on the **Input** tab, or via the API:

| Field | Type | Default | Notes |
|---|---|---|---|
| `mode` | enum | `browse` | `browse`, `search`, `tag`, `author`, or `slugs` |
| `browseType` | enum | `popular` | Used with `browse`: `popular`, `new`, `beta`, `recommended` |
| `searchQuery` | string | — | Required for `mode: search` |
| `tag` | string | — | Required for `mode: tag`, e.g. `woocommerce` |
| `author` | string | — | Required for `mode: author`, e.g. `automattic` |
| `slugs` | string\[] | — | Required for `mode: slugs`, e.g. `["akismet"]` |
| `maxResults` | integer | `100` | Hard cap on records pushed |
| `minActiveInstalls` | integer | `0` | Skip plugins below this install count |
| `includeFullDescription` | boolean | `false` | Adds the full HTML description; ~9× larger records |

A missing required field fails the run immediately with a clear message rather than quietly falling back to a different mode.

```json
{
  "mode": "browse",
  "browseType": "popular",
  "maxResults": 5000,
  "minActiveInstalls": 10000
}
```

### Output

One record per plugin. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

```json
{
  "slug": "health-check",
  "name": "Health Check & Troubleshooting",
  "version": "1.7.1",
  "author_name": "WordPress.org",
  "author_profile_url": "https://profiles.wordpress.org/wordpressdotorg/",
  "requires": "4.4",
  "tested": "6.6.5",
  "requires_php": "5.6",
  "rating": 72,
  "rating_out_of_5": 3.6,
  "num_ratings": 181,
  "support_threads": 6,
  "support_threads_resolved": 0,
  "support_resolution_rate": 0.0,
  "active_installs": 300000,
  "downloaded": 5336798,
  "last_updated": "2024-07-25 9:43pm GMT",
  "days_since_update": 742,
  "is_stale": true,
  "opportunity_score": 77.98,
  "opportunity_popularity": 0.7824,
  "opportunity_rating_gap": 0.28,
  "opportunity_staleness": 1.0,
  "opportunity_support_gap": 1.0,
  "tags_list": ["health check"],
  "short_description": "Health Check identifies common problems, and helps you troubleshoot plugin and theme conflicts.",
  "homepage": "https://wordpress.org/plugins/health-check/",
  "download_link": "https://downloads.wordpress.org/plugin/health-check.1.7.1.zip",
  "scraped_at": "2026-08-06T05:10:52.867062+00:00"
}
```

#### Data fields

| Field | Meaning |
|---|---|
| `slug` | Plugin slug, as in the `wordpress.org/plugins/<slug>/` URL |
| `name` | Display name, HTML entities decoded |
| `version` | Latest released version |
| `author_name`, `author_profile_url` | Author name and WordPress.org profile |
| `requires`, `tested`, `requires_php`, `requires_plugins` | Compatibility requirements |
| `rating`, `rating_out_of_5`, `num_ratings`, `ratings` | Rating as a 0–100 percentage, converted to stars, plus the star histogram |
| `support_threads`, `support_threads_resolved`, `support_resolution_rate` | Support forum volume and resolved share (`null` when there are no threads) |
| `active_installs` | Active installs — **a bucketed approximation**, see caveats |
| `downloaded` | All-time download count |
| `last_updated`, `added`, `days_since_update`, `is_stale` | Release recency; `is_stale` is `true` past 365 days |
| `opportunity_score` + four components | See below |
| `tags_list` | Plugin tags as an array |
| `short_description`, `description` | Summary always; full HTML body only with `includeFullDescription` |
| `homepage`, `download_link`, `donate_link`, `icons` | Links and assets |
| `scraped_at` | ISO-8601 UTC timestamp of the run |

### How the opportunity score works

`opportunity_score` is 0–100, higher meaning "more attractive to compete with". Four components, each normalised to 0–1, are combined with fixed weights:

| Component | Definition | Weight |
|---|---|---|
| `opportunity_popularity` | `log10(active_installs)` scaled against the 10,000,000 top bucket — log, because install counts are bucketed powers of ten | 0.35 |
| `opportunity_staleness` | `days_since_update` over a 730-day ceiling, capped at 1 | 0.30 |
| `opportunity_rating_gap` | `1 - rating/100`; a neutral 0.5 below 5 ratings, where the average is noise | 0.20 |
| `opportunity_support_gap` | `1 - support_resolution_rate`; a neutral 0.5 when there are no threads | 0.15 |

All four components are emitted on every record, so if you disagree with the weighting you can recompute the score yourself without re-running anything. Popularity and staleness carry most of the weight because the thing worth finding is a plugin lots of people rely on that nobody is looking after.

### Caveats worth reading

- **`active_installs` is bucketed by WordPress.org, not exact.** It reports `10`, `100`, `1000`, `10000`, ... — a value of `1000000` means "1 million or more". Treat it as an order of magnitude, never a precise count. This is a limitation of the source data, not of this Actor.
- **`rating` is a 0–100 percentage** in the source API. `rating_out_of_5` is the star equivalent.
- **The directory total drifts while you page through it.** Plugins are added and removed live, so consecutive requests report slightly different totals. Records are de-duplicated by `slug`, so a long run never emits the same plugin twice — but the exact set of plugins you get for a large `maxResults` will differ slightly run to run.
- **`support_resolution_rate` is `null`, not `0`, when a plugin has no support threads.** No threads means no signal, not perfect support.
- **Ratings and install counts are self-reported by WordPress.org** and reflect what the directory publishes, nothing more.

### Cost

The Actor is pay-per-result: you're charged per plugin record pushed to the dataset, with no charge for pages that produce nothing. A 1,000-plugin run is a fixed, predictable cost, and the Apify free tier is enough to try the default 100-plugin run. Current pricing is shown on the Actor's Store page.

To keep costs down: set `maxResults` to what you actually need, use `minActiveInstalls` to skip the long tail, and leave `includeFullDescription` off unless you need the HTML body — it makes records roughly 9× larger for no extra signal.

### Tips

- **Looking for a gap?** `mode: browse`, `browseType: popular`, `minActiveInstalls: 10000`, `maxResults: 5000`, then sort the dataset by `opportunity_score` descending.
- **Auditing one niche?** `mode: tag` with the tag slug from any plugin page's sidebar.
- **Monitoring competitors?** `mode: slugs` with the handful you care about, on a daily schedule, and watch `version` and `days_since_update`.
- `browse: beta` and `browse: recommended` are short curated lists — a run will legitimately return only a handful of records.

### FAQ

**Is scraping the WordPress.org plugin directory legal?** This Actor uses the official, public, unauthenticated WordPress.org plugins API — no HTML scraping, no login, no circumvention. The data is published by WordPress.org for exactly this purpose. It contains no personal data beyond the public author names and profile URLs the directory itself displays. You are responsible for how you use the output.

**Why did I get fewer results than `maxResults`?** The query ran out of matches. `beta` and `recommended` are small lists, and searches or tags with few plugins finish early. The run log says so explicitly.

**One of my slugs came back missing.** Slugs that don't exist on WordPress.org are logged as a warning and skipped; the rest of the run continues.

**Can I get plugin download statistics over time, or theme data?** Not from this Actor. Open an issue on the **Issues** tab if you need it.

### Support

Found a bug or need a field that isn't here? Open an issue on the **Issues** tab of this Actor — that's the fastest route. Custom variants of this Actor can be built on request.

# Actor input Schema

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

How to pick which plugins to extract. **Browse** walks a WordPress.org list, **Search** runs a keyword search, **Tag** and **Author** filter the directory, and **Slugs** fetches specific plugins you name.

## `browseType` (type: `string`):

Which WordPress.org list to walk. Only used when Mode is *Browse*. `popular` paginates through the whole directory, most-installed first.

## `searchQuery` (type: `string`):

Keyword to search the directory for. Required when Mode is *Search*.

## `tag` (type: `string`):

Plugin tag slug, e.g. `woocommerce`, `seo`, `security`. Required when Mode is *Tag*.

## `author` (type: `string`):

WordPress.org author username, e.g. `automattic`. Required when Mode is *Author*.

## `slugs` (type: `array`):

Exact plugin slugs to fetch, as they appear in the wordpress.org/plugins/<slug>/ URL. Required when Mode is *Slugs*. Slugs that do not exist are logged and skipped.

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

Hard cap on the number of plugin records pushed to the dataset. The full directory is roughly 65,000 plugins.

## `minActiveInstalls` (type: `integer`):

Skip plugins below this install count. WordPress.org reports installs in buckets (10, 100, 1000, 10000, ...), so use a bucket boundary. Set to 10000 to look only at plugins with real traction.

## `includeFullDescription` (type: `boolean`):

Add each plugin's full HTML description. This makes records roughly 9x larger and slows the run considerably — leave it off unless you need the body text.

## Actor input object example

```json
{
  "mode": "browse",
  "browseType": "popular",
  "searchQuery": "seo",
  "tag": "woocommerce",
  "author": "automattic",
  "slugs": [
    "akismet",
    "woocommerce"
  ],
  "maxResults": 100,
  "minActiveInstalls": 0,
  "includeFullDescription": false
}
```

# 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 = {
    "slugs": [
        "akismet",
        "woocommerce"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("isaac_ship/wordpress-plugin-directory-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 = { "slugs": [
        "akismet",
        "woocommerce",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("isaac_ship/wordpress-plugin-directory-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 '{
  "slugs": [
    "akismet",
    "woocommerce"
  ]
}' |
apify call isaac_ship/wordpress-plugin-directory-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,isaac_ship/wordpress-plugin-directory-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/lZhY67XlPsUtFkBGB/builds/0SeOdJWUxVXSFSXe5/openapi.json
