DEV Community

Miłosz Rusznica
Miłosz Rusznica

Posted on Originally published at mrdesigngarwolin.pl

How to check if a WordPress plugin is abandoned with one API call

An abandoned WordPress plugin gives you no warning in the admin panel. The plugins screen is an update notification list, not a health report, and a plugin nobody maintains never ships an update. So it sits in that list looking exactly like a healthy plugin.

I did not want to repeat that as folklore, so I went to check it in core. Then I built the two-minute check that actually works.

What the plugins screen actually renders

The file behind that screen is wp-admin/includes/class-wp-plugins-list-table.php. On the master branch of WordPress/WordPress it is 1,744 lines. I grepped it for every word you would expect a warning to contain:

git clone --depth 1 https://github.com/WordPress/WordPress.git
cd WordPress
grep -nEi 'no longer|closed|abandon|unmaintain|removed from the directory|major releases|has not been tested' \
  wp-admin/includes/class-wp-plugins-list-table.php
# no output
Enter fullscreen mode Exit fullscreen mode

Zero matches. Same grep against wp-admin/includes/update.php: zero matches.

There is exactly one hit anywhere near this, in wp-admin/includes/plugin-install.php, line 832:

'<strong>Warning:</strong> This plugin <strong>has not been tested</strong> with your current version of WordPress.'
Enter fullscreen mode Exit fullscreen mode

Read where that lives. It belongs to the install flow, the details view you get while adding a new plugin. If a plugin is already installed and running your site, nobody ever opens that screen again.

The warning exists. It just renders on the one page an owner of a finished site never visits.

One endpoint, three answers

Everything you need is in the public directory API. No key, no auth:

https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=SLUG
Enter fullscreen mode Exit fullscreen mode

Three shapes come back, and the difference between them is the whole check.

Answer 1: still in the directory

curl -s "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=search-everything" \
  | jq '{last_updated, tested}'
Enter fullscreen mode Exit fullscreen mode
// trimmed to the two fields that decide this
{
  "last_updated": "2017-11-28",
  "tested": "4.7.35"
}
Enter fullscreen mode Exit fullscreen mode

Search Everything is still listed and still downloadable. Last code change: November 2017. Tested up to WordPress 4.7.35, while current WordPress is 7.1. Ten thousand sites run it.

That gap is wide enough that the directory page prints its own banner:

This plugin hasn't been tested with the latest 3 major releases of WordPress. It may no longer be maintained or supported and may have compatibility issues when used with more recent versions of WordPress.

Note the location again. That banner is on wordpress.org, not in your admin.

Answer 2: closed

curl -s -o body.json \
  -w 'HTTP %{http_code}\n' \
  "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=widget-logic"
# HTTP 404
cat body.json
Enter fullscreen mode Exit fullscreen mode
// trimmed
{
  "error": "closed",
  "reason_text": "Guideline Violation"
}
Enter fullscreen mode Exit fullscreen mode

Two things worth pinning down here.

First, a trap for anyone scripting this: the closed response arrives with HTTP 404. Branch on the status code alone and you cannot tell "pulled from the directory" from "never existed". Read the body.

Second, the human side. Widget Logic was closed on 2026-04-14, and the closure notice states plainly: "This closure is permanent." More than 100,000 sites are still running it four months later. Not one of those admin panels mentions it.

Answer 3: not found

{ "error": "Plugin not found." }
Enter fullscreen mode Exit fullscreen mode

This plugin was never in the directory. It is commercial, or custom built for that site. The directory has nothing to tell you, so the question moves to the vendor or to your own repo.

Check the whole list, not the one plugin you suspect

The check is cheap enough to run across everything installed:

#!/usr/bin/env bash
API="https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]="

wp plugin list --field=name | while read -r slug; do
  body=$(curl -s "${API}${slug}")
  err=$(printf '%s' "$body" | jq -r '.error // empty')

  case "$err" in
    "")
      printf '%-28s OK       last_updated=%s tested=%s\n' "$slug" \
        "$(printf '%s' "$body" | jq -r '.last_updated')" \
        "$(printf '%s' "$body" | jq -r '.tested')"
      ;;
    "closed")
      printf '%-28s CLOSED   reason=%s\n' "$slug" \
        "$(printf '%s' "$body" | jq -r '.reason_text // "not disclosed yet"')"
      ;;
    *)
      printf '%-28s NOT IN DIRECTORY\n' "$slug"
      ;;
  esac

  sleep 1
done
Enter fullscreen mode Exit fullscreen mode

Twenty plugins, one coffee, once a year.

reason_text is a triage signal, not trivia

The plugin developer FAQ states the closure reasons directly: "Plugins are closed for guideline violations, security issues, or by author requests."

That maps cleanly onto your calendar:

  • Security Issue: a hole was found. Take it off today.
  • Guideline Violation: rules were broken. Find out what happened, plan the swap.
  • Author Request: the author asked out. You have room for a calm migration.

Two caveats from the same FAQ, and both matter more than the mapping.

The reason is only published after 60 days, and even then "only in the broadest terms". A plugin closed last month tells you nothing about why. Treat that silence as urgent rather than harmless.

And closures are not always a dramatic event. The directory can close a plugin on its own:

If a plugin has never been used within 6 months (i.e. no code has been pushed to SVN), SVN is broken for upwards of 12 months, or a plugin's readme indicates it's deprecated, we may close without notification.

The case that breaks the heuristic

If you audit only on tested, you will miss the most honest case of all.

Database Backup for WordPress (wp-db-backup) has 60,000 installs and is tested up to 6.9.7. That version sits inside the last three major releases, so the directory banner has no reason to fire. Everything looks current.

The author's own description says:

Database Backups for WordPress is no longer actively maintained... No additional releases, including security releases, will be made available.

A tested header tells you somebody bumped a number. The description is where authors say they are done. Read both.

Deactivating is not removing

This is where people lose sites. Deactivating a plugin does not take its files off the server.

Core says as much, in wp-admin/includes/class-wp-site-health.php at lines 462 and 475:

'You should remove inactive plugins'
'Inactive plugins are tempting targets for attackers. If you are not going to use a plugin, you should consider removing it.'
Enter fullscreen mode Exit fullscreen mode

Take a backup before you delete anything. Deleting through the admin can run the plugin's uninstall routine, and that is not something you undo.

What the numbers say, and what they do not

Patchstack's "State of WordPress Security in 2026", covering 2025:

  • 11,334 new vulnerabilities were found in the WordPress ecosystem in 2025
  • 91% of new vulnerabilities were found in plugins
  • 46% of vulnerabilities did not receive a patch by the time of disclosure
  • the weighted median time to first exploit is 5 hours

Be careful with the second number, because it is the one everybody misquotes. That 91% covers plugins as a whole, actively maintained ones included. The report does not isolate abandoned plugins, so neither will I.

The line you can draw is narrower, and it comes from the fourth number. Forty-six percent unpatched at disclosure describes a race that a maintained plugin can still win, because someone is on the other end shipping a fix within those five hours. For a plugin with no maintainer, unpatched is not a delay in that race. It is the finish line.

Takeaways

  • The plugins screen answers "did anything arrive", not "is this healthy".
  • One endpoint, three answers. The closed answer arrives as HTTP 404, so read the body, not the status.
  • tested is a header somebody bumped. The description is where maintainers announce they are done.
  • Deactivated still means present on disk.
  • Run the check across every plugin, on a schedule, not on a hunch.

Originally published on mrdesigngarwolin.pl - my Polish blog about WordPress, web design, and SEO for small businesses.

Top comments (0)