Patchstack

docs.patchstack.com
Websites

Patchstack is a powerful tool that helps to protect your WordPress applications from attacks and identify security vulnerabilities within all your WordPress…

llms.txt

This is the full developer documentation for Patchstack Docs

Welcome to Patchstack Docs

Patchstack is a powerful tool that helps to protect your WordPress applications from attacks and identify security vulnerabilities within all your WordPress plugins, themes, and core. It is powered by the WordPress ecosystem's most active community of ethical hackers. Patchstack is trusted by leading WordPress experts such as GoDaddy, Hostinger, Pagely, GridPane, Plesk, and others.

Learn more

Section titled “Learn more”

Get started: protect your site

Solutions for WordPress, Joomla & Drupal

API solutions for custom tools

Frequently asked questions

Patchstack API solutions

Patchstack offers two different APIs for building custom tools and integrating our data to third party solutions.

App API

Section titled “App API”

Patchstack App API can be used commercially for building custom tools and integrating third party platforms with Patchstack App; Patchstack App API enables users to run all the Patchstack App account actions remotely. For example, it allows you to retrieve protection logs, generate security reports, manage site settings, add new sites, create custom rules, and much more. This API has unlimited requests and can be used by all Developer plan users for free.

Read more here

Threat Intelligence API

Section titled “Threat Intelligence API”

Query our vulnerability database and retrieve information about all published vulnerabilities. The Extended tier covers single-product lookups, bulk-request endpoints for multiple components in one call, the /latest rolling feed, and advisory-by-id detail. Custom pricing, activated on request.

Read more here

Patchstack App API

Patchstack App API is available for the Developer and Enterprise plan users

Patchstack App API enables users to run all the Patchstack App account actions remotely over an API. It allows you to access protection logs, generate security reports, manage site settings, add new sites, create custom rules, and much more.

Documentation and endpoints

Section titled “Documentation and endpoints”

Find all the Patchstack App API endpoints with examples from the documentation here: https://api.patchstack.com/app-api/documentation

Example use cases

Section titled “Example use cases”

Some example use cases for Patchstack App API are listed below:

  • Integrate Patchstack to your email marketing software to send out monthly security reports to your customers.
  • Integrate Patchstack inside your own product and let your customers enable (and control) Patchstack directly from your platform without them leaving your service.
  • Pull IP addresses of attackers that try to exploit vulnerabilities into your DNS firewall to block them on the network layer.
  • Integrate with Enterprise SIEM/SOC tools and pull vulnerability data and logs directly into it.
  • Build any kind of automations with Zapier, IFTTT, etc.

How to get Patchstack App API key?

Section titled “How to get Patchstack App API key?”

To get the API key, log in to your Patchstack account, go to account settings and navigate to the Integrations page. On this page, you are able to generate multiple App API keys for your account with the optional ability to:

  • Bind an API key to IP addresses
  • Make an API key read-only
  • Set an expiration
  • Rotate the API key

Patchstack iFrame Widget

Patchstack iframe widget is available for Enterprise plan users

The Patchstack iframe widget is a drop-in HTML component that lets you display real-time security insights inside your own environment.

Simply plug it in your own environment and give customers deeper insights into the state of their website’s security while not giving them too much control over how Patchstack is configured on their website.

Integration

Section titled “Integration”

Prerequisites

Section titled “Prerequisites”

  • Patchstack SaaS Enterprise plan
  • Patchstack App API key
  • Your own environment where you want to display the iframe
  • Know how to communicate with an API
  • Understand the request and response parameters of /site/{site}/sso/generate
  • The Patchstack site id of the site which you want to render in the iframe widget

Fetching the iframe token

Section titled “Fetching the iframe token”

Once you have obtained a Patchstack App API key, you can query the /site/{site}/sso/generate API endpoint in order to retrieve an access token.

This access token can be passed to a special iframe URL after which the iframe will be rendered.

  • The access token is valid for 1 hour and its expiration will be pushed back by 1 hour each time it is accessed by the user through the iframe to avoid an unexpected timeout.
  • You can pass the mode payload parameter and set it to dark or light to set the theme. It defaults to dark.
  • You can pass an optional ip_address parameter to bind the loading of the iframe to the users’ IP address. Ensure the server generating the token knows the client’s IP or use a proxying mechanism.

Example integration

Section titled “Example integration”

Step 1: send the HTTP request

Section titled “Step 1: send the HTTP request”

The following curl example assumes the site identifier is 12345 of the site for which you want to fetch the access token for the iframe. Also replace the YOUR_APP_API_KEY value with your own Patchstack App API key.

curl -X POST "https://api.patchstack.com/monitor/site/12345/sso/generate" \
  -H "UserToken: YOUR_APP_API_KEY"
Step 2: parse the HTTP response

Section titled “Step 2: parse the HTTP response”

The API endpoint will respond with the raw access_token, expires_in, and iframe_url properties. You can use the iframe_url property directly for the next step.

[
  {
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLddleHAiOjE3NDI5MDg0Nzd9.KcDgyhd8sf9xdddMHKLM258drlLrYc2rX6pN166AiEM",
    "expires_in": "3600",
    "iframe_url": "https://app.patchstack.com/iframe?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLddleHAiOjE3NDI5MDg0Nzd9.KcDgyhd8sf9xdddMHKLM258drlLrYc2rX6pN166AiEM"
  }
]
Step 3: render the iframe widget

Section titled “Step 3: render the iframe widget”

Pass the iframe_url property from step 2 directly to the src attribute of the iframe and you are done! A minimal iframe example is shown below.

<!-- Replace src attribute value with the iframe_url parameter from the response -->
<iframe
  src="https://app.patchstack.com/iframe?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2ggX2lkIjoxLddleHAiOjE3NDI5MDg0Nzd9.KcDgyhd8sf9xdddMHKLM258drlLrfc2rX6pN166AiEM"
  width="1200"
  height="1080"></iframe>

Frequently asked questions

Section titled “Frequently asked questions”

Can we customize the widget colors?

Section titled “Can we customize the widget colors?”

We recommend selecting one of the two themes provided (either dark or light). However, it is also possible to inject custom CSS. This is manually set by the Patchstack Team at this time for your particular account.

If this is needed, reach out to us and provide us with the CSS snippet to inject. Note that HTML/CSS structure and styling of the iframe widget may change over time which may affect the way your custom CSS renders the elements in the iframe widget.

Can we choose what information users see?

Section titled “Can we choose what information users see?”

No, currently the information is fixed to the views below.

Are the widget height and width static?

Section titled “Are the widget height and width static?”

No. You can define the dimensions through the iframe attributes. The width is responsive, but the height would add a scrollbar if it doesn’t fit into view.

Screenshots

Section titled “Screenshots”

Vulnerabilities overview

Section titled “Vulnerabilities overview”

Software overview

Section titled “Software overview”

Protection overview

Section titled “Protection overview”

Patchstack Integration

Patchstack has made it extremely easy to be integrated into your workflow or control panel. This page will describe the possible ways to integrate us and what tools are needed for this.

Some of the integration steps are essential to follow, while some others entirely depend on how the internal infrastructure looks as well as available developer resources.

Note that this document is made for WordPress sites.

Integration

Section titled “Integration”

Prerequisites

Section titled “Prerequisites”

Considerations

Section titled “Considerations”

.htaccess file

Section titled “.htaccess file”

The Patchstack plugin writes, by default, to the .htaccess file to apply basic protection rules (for example to prevent PHP file access in certain folders). This functionality can be turned off in 2 ways, and we recommend doing this before activating Patchstack on the site.

  1. Set the constant PS_DISABLE_HTACCESS to true: add define('PS_DISABLE_HTACCESS', true); to wp-config.php
  2. Set the patchstack_disable_htaccess WordPress option to 1, e.g. WP-CLI command: wp option update patchstack_disable_htaccess 1

IP address header

Section titled “IP address header”

The Patchstack plugin tries to guess in which HTTP header the web-server is storing the real IP address of the visitor and sets it to that permanently until we refresh it when we detect an environment change. This HTTP header varies a lot between hosting providers and also depends on if services such as Cloudflare are used.

If necessary, this can be set through the WordPress option patchstack_firewall_ip_header. This must contain the full PHP-based HTTP header. For example for Cloudflare it would be HTTP_CF_CONNECTING_IP: wp option update patchstack_firewall_ip_header HTTP_CF_CONNECTING_IP

If the IP address header is invalid, we fallback to REMOTE_ADDR. Note that setting it to an invalid HTTP header could make it possible for malicious users to spoof their IP address.

Data caching strategy

Section titled “Data caching strategy”

To minimize API calls and reduce latency when displaying site information to your users, implement a caching strategy in your local datastore. This is essential for maintaining performance and reducing strain on both your infrastructure and the Patchstack API.

Error handling & status codes

Section titled “Error handling & status codes”

All Patchstack API endpoints return standard HTTP status codes. Your integration should handle these appropriately to ensure reliability and provide meaningful feedback to users. E.g. handle status codes 200, 401, 422, 500.

Flow of integration & plugin

Section titled “Flow of integration & plugin”

Patchstack works by assigning an API key to a site that has been added to the Patchstack App. This API key is then used in the WordPress plugin to activate the connection to Patchstack.

The plugin takes cares of a few things, but these are most important to know:

  • Uploads a list of software to the Patchstack API (SCA)
    • This ensures we attach the proper mitigation rules to the site
  • Uploads firewall and activity logs to the Patchstack API
    • For threat intelligence purposes
  • Fetches mitigation rules that will run in the plugin on each request
    • We only ship the mitigation rules that the site needs for optimal performance

A possible integration can be split into 5 phases:

  1. Account setup & authentication
  2. Site provisioning
  3. Plugin deployment
  4. Default Policy & Managed By Mode
  5. Management & reporting

Integration phases

Section titled “Integration phases”

Step 1: Account setup & authentication

Section titled “Step 1: Account setup & authentication”

The Patchstack App API requires an API key. Refer to this page for more instructions on how to get one. Note that you can only obtain the App API key on the developer or enterprise plan.

Once the API key has been acquired, it can be used to communicate with the Patchstack App API by setting the UserToken HTTP header to the value of the API key. The root URL of the Patchstack App API is https://api.patchstack.com/monitor/.

It’s also important to set the Content-Type HTTP header to application/json when sending request data through the body.

Step 2: Site provisioning

Section titled “Step 2: Site provisioning”

When the site should be added to Patchstack depends on your use-case scenario. If they require protection immediately then you can execute this flow the moment the website environment (the WordPress site) has been created on the infrastructure. If you upsell Patchstack then this flow can be launched upon purchase or opt-in.

1. Determine if the site already exists in Patchstack

Section titled “1. Determine if the site already exists in Patchstack”

This step is essential as it’s possible that a site may be protected already or added to Patchstack. For this, the /site/exists API endpoint can be utilized; it will check if a site with the given URL has already been added to Patchstack before you attempt to actually add the site to Patchstack.

The strict parameter dictates whether to do a strict check of the exact URL; avoids checking if different variants of the domain name (with and without www) and protocol (with and without http/https) is added.

curl -X 'POST' \
  'https://api.patchstack.com/monitor/site/exists' \
  -H 'Content-Type: application/json' \
  -H 'UserToken: <token>' \
  -d '{
  "url": "https://mywebsite.com",
  "strict": false
}'

The response will indicate whether or not the site is added already:

{
  "exists": false
}
2. Add the website to Patchstack

Section titled “2. Add the website to Patchstack”

Once we confirm that the site has not been added already, we can add it to Patchstack to obtain an API key.

curl -X 'POST' \
  'https://api.patchstack.com/monitor/site/add' \
  -H 'UserToken: <token>' \
  -H 'Content-Type: application/json' \
  -d '{
  "urls": [
    "https://mywebsite.com"
  ],
  "cms_id": 1
}'

The response will look something like below.

{
  "success": "Successfully added the site(s).",
  "count": 1,
  "sites": {
    "https://mywebsite.com": {
      "siteid": 12345,
      "oauth": {
        "id": 12333,
        "secret": "DOOs9DIyv2FMcURFtkB0eXOHMRhH7I2EsaNUb4aR",
        "apikey": "DOOs9DIyv2FMcURFtkB0eXOHMRhH7I2EsaNUb4aR-12333"
      }
    }
  },
  "lastid": 12345,
  "oauth": {
    "id": 12333,
    "secret": "DOOs9DIyv2FMcURFtkB0eXOHMRhH7I2EsaNUb4aR",
    "apikey": "DOOs9DIyv2FMcURFtkB0eXOHMRhH7I2EsaNUb4aR-12333"
  }
}

The top-level lastid and oauth fields refer to the last site in the batch and are kept for backwards compatibility. New integrations should read from the per-URL sites map.

3. Store in local datastore

Section titled “3. Store in local datastore”

Store the siteid and apikey for each URL in a datastore on your infrastructure. This avoids having to query the Patchstack App API each time you need to identify a customer’s site.

  • siteid is the canonical Patchstack site identifier. It is what the portal displays and what every per-site API endpoint expects in the URL (/site/view/{siteid}, /site/{siteid}/delete, /download/wordpress/{siteid}, etc.). Store this if you ever need to cross-reference your records against the Patchstack portal.
  • apikey is the pre-formatted plugin license key (<oauth.secret>-<oauth.id>). Pass it directly to the WordPress plugin during activation — you do not need to assemble it yourself.

siteid and oauth.id are independent fields

siteid identifies the site, oauth.id identifies the OAuth credential embedded in the plugin license key. They have always been independent fields with different purposes. Do not assume they are equal — for sites provisioned before 13 May 2026 the two values often coincided as a side-effect of how rows were inserted, but they may differ for any site provisioned since.

If you previously stored oauth.id under the assumption it equalled the site identifier, call POST /monitor/sites/list/basic to fetch [{id, url}, …] for every site on your account, match by URL, and update your stored values to the current siteid.

Step 3: Plugin deployment

Section titled “Step 3: Plugin deployment”

Now that you have the site identifier and plugin API key stored in your local datastore, you can start the flow of installing and activating the Patchstack plugin on the website.

There are 3 possible ways to do this:

  • Through WP-CLI

    • Fully automatic process
    • Recommended approach
  • Downloading pre-configured Patchstack plugin

    • Semi-automatic depending on integration
    • Not recommended → might interfere with file integrity checks
  • Manually downloading, installing and activating Patchstack

    • Fully manual process
    • Not recommended → not automated
Through WP-CLI

Section titled “Through WP-CLI”

With the WP-CLI, we need to execute 2 commands to get the Patchstack plugin running and activated.

First we execute the command below to install and activate the Patchstack plugin:

wp plugin install patchstack --activate

Then we execute the command below to activate the connection to Patchstack. It is important to inject the plugin API key (apikey from step 2) here:

wp patchstack activate DOOs9DIyv2FMcURFtkB0eXOHMRhH7I2EsaNUb4aR-12333

This command will return The Patchstack plugin has been successfully connected. if it succeeded.

Downloading pre-configured Patchstack plugin

Section titled “Downloading pre-configured Patchstack plugin”

The pre-configured Patchstack plugin simply has the plugin API key injected into the /patchstack/patchstack.php file. This API key will be used when the plugin is being activated and we detect that this injected API key is present and Patchstack has not been activated yet.

It can be downloaded through the /download/wordpress/{site} API endpoint.

curl -X 'GET' \
  'https://api.patchstack.com/monitor/download/wordpress/12345' \
  -H 'Accept: */*' \
  -H 'UserToken: <token>' \

This will serve a binary .zip file which you can download after which there are 2 options:

  1. Option 1: Upload, install and activate the plugin manually through /wp-admin/.
  2. Option 2: Unzip into /wp-content/plugins/ so it’s unzipped as /wp-content/plugins/patchstack/, then activate it manually through /wp-admin/ or programmatically activate through custom code of your own.

Note that because we inject the API key into the file, it could fail file integrity checks.

Manually downloading, installing and activating Patchstack

Section titled “Manually downloading, installing and activating Patchstack”

Another way to install the Patchstack plugin is from WordPress itself.

  1. In the WordPress admin area, navigate to Plugins > Add New > Type “Patchstack” to search.
  2. Install and activate the plugin
  3. Copy the API key from there
  4. Go to your WordPress admin, navigate to Settings > Security and insert the API key there

Step 4: Default Policy & Managed By Mode

Section titled “Step 4: Default Policy & Managed By Mode”

As an enterprise customer, you will have access to some features that allows you to customize some functionality of Patchstack.

Default Policy

Section titled “Default Policy”

It is possible that you may want certain options in Patchstack disabled, or enabled, by default upon activation of the Patchstack plugin on the website. This is possible with a default policy. To read more about default policy functionality, please head to this documentation page.

Managed By Mode

Section titled “Managed By Mode”

The managed by mode allows you to set text (HTML) that is shown to your customers when they access the Patchstack settings page on /wp-admin/ → Settings → Security. This can be used to show who manages or maintains the Patchstack security integration or who they should contact/email for any support. For more information on the managed by mode, head to the this documentation page.

Step 5: Management & reporting

Section titled “Step 5: Management & reporting”

Once Patchstack has been activated on a site, it will upload the software list to the Patchstack API and fetch the mitigation rules for any vulnerabilities present on the website which need protection.

Now it is important to show the value of Patchstack to the customer, and this can be done through 2 ways:

  1. You fetch data from the Patchstack App API and display this in your environment
  2. You embed our iframe widget
Fetching data

Section titled “Fetching data”

We have a large list of API endpoints to use where you can fetch information of a site. Some noteworthy ones are listed below as well as here:

  • Fetch basic information

    • Vulnerability counters (how many present, mitigated and resolved), total threats blocked, total software counters and how many are vulnerable/outdated
    • Attacks blocked over past 7 days
  • Fetch vulnerabilities present

    • All vulnerabilities of a site
    • Information about each individual vulnerability such as: is it fixed, is it exploited, does it have protection, what is the severity
  • Fetch firewall statistics

    • Fetch the total threats blocked (grouped by day) over a given period of days or timeframe
Embedding iframe widget

Section titled “Embedding iframe widget”

The Patchstack iframe widget is a drop-in HTML component that lets you display real-time security insights inside your own environment.

Simply plug it in your own environment and give customers deeper insights into the state of their website’s security while not giving them too much control over how Patchstack is configured on their website.

Click here for integration and more information

Noteworthy API endpoints

Section titled “Noteworthy API endpoints”

The list below are noteworthy API endpoints that might be interesting to our partners.

Frequently asked questions

Section titled “Frequently asked questions”

How to determine if a site has been activated and is connected?

Section titled “How to determine if a site has been activated and is connected?”

Utilize the /site/plugin/installed/{site} API endpoint in order to determine if a site has been connected and has done its first software synchronization. This will return true if we have a software list present, but does not check the latest ping status.

curl -X 'GET' \
  'https://api.patchstack.com/monitor/site/plugin/installed/12345' \
  -H 'UserToken: <token>' \

Which outputs the following:

{
  "activated": true
}

How to determine if a site is still connected to Patchstack?

Section titled “How to determine if a site is still connected to Patchstack?”

Utilize the /site/state/{site} API endpoint in order to determine if a site is still connected and has pinged the Patchstack API any time recently.

curl -X 'GET' \
  'https://api.patchstack.com/monitor/site/state/12345' \
  -H 'UserToken: <token>' \

Which outputs the following:

{
  "activated": true
}

What’s the difference between siteid and oauth.id? Can I assume they’re equal?

Section titled “What’s the difference between siteid and oauth.id? Can I assume they’re equal?”

They are two independent fields with different purposes — do not assume they are equal.

  • siteid is the canonical Patchstack site identifier. It is what the portal displays and what every per-site API endpoint expects (e.g. /site/view/{siteid}, /site/{siteid}/delete, /download/wordpress/{siteid}). Store this for cross-referencing your records against the Patchstack portal.
  • oauth.id is the OAuth credential identifier. Its only purpose is to form the plugin license key, which the /site/add response already returns pre-formatted as apikey (<oauth.secret>-<oauth.id>). You never need to read oauth.id as a standalone value — just pass apikey to the plugin.

For sites provisioned before 13 May 2026, siteid and oauth.id coincidentally held the same value because of how rows were inserted in our database. They may differ for any site provisioned since. If you previously stored oauth.id under the assumption it equalled the site identifier, see the callout in step 2 of integration for how to reconcile your stored values.

How can I reconcile site IDs in my datastore against the Patchstack portal?

Section titled “How can I reconcile site IDs in my datastore against the Patchstack portal?”

Call POST /monitor/sites/list/basic with the same UserToken you use for /site/add. It returns [{id, url}, …] for every site on your account, where id is the current siteid. Match by URL and update your stored values.

curl -X 'POST' \
  'https://api.patchstack.com/monitor/sites/list/basic' \
  -H 'UserToken: <token>' \

API properties

This document will provide information on the properties of the different endpoints as part of the API of the vulnerability database and will also provide an example on a potential integration to match the result set against WordPress software.

These examples will be shown using PHP, but can easily be implemented using any programming language. If you have any questions, feel free to send an email to dave.jong@patchstack.com.

Data Structure

Section titled “Data Structure”

Some of the JSON properties as part of the result set can be null so it is important to handle these properties accordingly. Note that we may speak of “product” in the result set, which is essentially the same as a “component”.

This is JSON example for 1 plugin, 1 theme and 1 WordPress core vulnerability.

{
    "vulnerabilities": [
    {
            "id": 8728,
            "product_id": 497,
            "title": "WordPress Ninja Forms plugin <= 3.6.10 - Unauthenticated PHP Object Injection vulnerability",
            "description": "Unauthenticated PHP Object Injection vulnerability discovered in WordPress Ninja Forms plugin (versions <= 3.6.10).",
            "disclosure_date": "2022-06-15 14:46:03",
            "disclosed_at": "2022-06-15T14:46:03+00:00",
            "created_at": "2022-06-17T09:00:05+00:00",
            "url": "wordpress-ninja-forms-plugin-3-6-10-unauthenticated-php-object-injection-vulnerability",
            "product_slug": "ninja-forms",
            "product_name": "Ninja Forms",
            "product_name_premium": null,
            "product_type": "Plugin",
            "vuln_type": "PHP Object Injection",
            "cvss_score": 9.8,
            "cve": [],
            "is_exploited": false,
            "patch_priority": 3,
            "affected_in": "<= 3.6.10",
            "fixed_in": "3.6.11",
            "patched_in_ranges": [
                {
                    "from_version": "3.0",
                    "to_version": "3.0.34.1",
                    "fixed_in": "3.0.34.2"
                },
                {
                    "from_version": "3.1",
                    "to_version": "3.1.9",
                    "fixed_in": "3.1.10"
                },
                {
                    "from_version": "3.2",
                    "to_version": "3.2.27",
                    "fixed_in": "3.2.28"
                },
                {
                    "from_version": "3.3",
                    "to_version": "3.3.21.3",
                    "fixed_in": "3.3.21.4"
                },
                {
                    "from_version": "3.4",
                    "to_version": "3.4.34.1",
                    "fixed_in": "3.4.34.2"
                },
                {
                    "from_version": "3.5",
                    "to_version": "3.5.8.3",
                    "fixed_in": "3.5.8.4"
                }
            ],
            "direct_url": "https://patchstack.com/database/vulnerability/ninja-forms/wordpress-ninja-forms-plugin-3-6-10-unauthenticated-php-object-injection-vulnerability"
        },
        {
            "id": 5793,
            "product_id": 3547,
            "title": "WordPress WooRockets Nitro premium theme <= 1.7.9 - Unauthenticated Arbitrary Plugin Installation vulnerability",
            "description": "Unauthenticated Arbitrary Plugin Installation vulnerability discovered by Brad Patton in WordPress WooRockets Nitro premium theme (versions <= 1.7.9).",
            "disclosure_date": "2021-11-03 00:00:00",
            "disclosed_at": "2021-11-03T00:00:00+00:00",
            "created_at": "2022-01-06T15:31:02+00:00",
            "url": "wordpress-woorockets-nitro-premium-theme-1-7-9-unauthenticated-arbitrary-plugin-installation-vulnerability",
            "product_slug": "wr-nitro",
            "product_name": "WooRockets Nitro",
            "product_name_premium": null,
            "product_type": "Theme",
            "vuln_type": "Other Vulnerability Type",
            "cvss_score": 8.2,
            "cve": [],
            "patch_priority": 3,
            "affected_in": "<= 1.7.9",
            "fixed_in": "",
            "patched_in_ranges": [],
            "direct_url": "https://patchstack.com/database/vulnerability/wr-nitro/wordpress-woorockets-nitro-premium-theme-1-7-9-unauthenticated-arbitrary-plugin-installation-vulnerability"
        },
        {
            "id": 5814,
            "product_id": 8,
            "title": "WordPress <= 5.8.2 - Authenticated Object Injection in Multisites",
            "description": "Authenticated Object Injection in Multisites discovered by Simon Scannell (SonarSource) in WordPress (versions <= 5.8.2).",
            "disclosure_date": "2022-01-06 00:00:00",
            "disclosed_at": "2022-01-06T00:00:00+00:00",
            "created_at": "2022-01-07T15:05:04+00:00",
            "url": "wordpress-5-8-2-authenticated-object-injection-in-multisites",
            "product_slug": "wordpress",
            "product_name": "WordPress",
            "product_name_premium": null,
            "product_type": "WordPress",
            "vuln_type": "Other Vulnerability Type",
            "cvss_score": 6.6,
            "cve": [
                "2022-21663"
            ],
            "is_exploited": false,
            "patch_priority": 2,
            "affected_in": "<= 5.8.2",
            "fixed_in": "5.8.3",
            "patched_in_ranges": [
                {
                    "from_version": "5.8",
                    "to_version": "5.8.2",
                    "fixed_in": "5.8.3"
                },
                {
                    "from_version": "5.7",
                    "to_version": "5.7.4",
                    "fixed_in": "5.7.5"
                },
                {
                    "from_version": "5.6",
                    "to_version": "5.6.6",
                    "fixed_in": "5.6.7"
                },
                {
                    "from_version": "5.5",
                    "to_version": "5.5.7",
                    "fixed_in": "5.5.8"
                },
                {
                    "from_version": "5.4",
                    "to_version": "5.4.8",
                    "fixed_in": "5.4.9"
                },
                {
                    "from_version": "5.3",
                    "to_version": "5.3.10",
                    "fixed_in": "5.3.11"
                },
                {
                    "from_version": "5.2",
                    "to_version": "5.2.13",
                    "fixed_in": "5.2.14"
                },
                {
                    "from_version": "5.1",
                    "to_version": "5.1.11",
                    "fixed_in": "5.1.12"
                },
                {
                    "from_version": "5.0",
                    "to_version": "5.0.14",
                    "fixed_in": "5.0.15"
                },
                {
                    "from_version": "4.9",
                    "to_version": "4.9.18",
                    "fixed_in": "4.9.19"
                },
                {
                    "from_version": "4.8",
                    "to_version": "4.8.17",
                    "fixed_in": "4.8.18"
                },
                {
                    "from_version": "4.7",
                    "to_version": "4.7.21",
                    "fixed_in": "4.7.22"
                },
                {
                    "from_version": "4.6",
                    "to_version": "4.6.21",
                    "fixed_in": "4.6.22"
                },
                {
                    "from_version": "4.5",
                    "to_version": "4.5.24",
                    "fixed_in": "4.5.25"
                },
                {
                    "from_version": "4.4",
                    "to_version": "4.4.25",
                    "fixed_in": "4.4.26"
                },
                {
                    "from_version": "4.3",
                    "to_version": "4.3.26",
                    "fixed_in": "4.3.27"
                },
                {
                    "from_version": "4.2",
                    "to_version": "4.2.30",
                    "fixed_in": "4.2.31"
                },
                {
                    "from_version": "4.1",
                    "to_version": "4.1.33",
                    "fixed_in": "4.1.34"
                },
                {
                    "from_version": "4.0",
                    "to_version": "4.0.33",
                    "fixed_in": "4.0.34"
                },
                {
                    "from_version": "3.9",
                    "to_version": "3.9.34",
                    "fixed_in": "3.9.35"
                },
                {
                    "from_version": "3.8",
                    "to_version": "3.8.36",
                    "fixed_in": "3.8.37"
                },
                {
                    "from_version": "3.7",
                    "to_version": "3.7.36",
                    "fixed_in": "3.7.37"
                }
            ],
            "direct_url": "https://patchstack.com/database/vulnerability/wordpress/wordpress-5-8-2-authenticated-object-injection-in-multisites"
        }
    ]
}
  • id → integer

    • Holds the unique numeric identifier of the vulnerability
  • product_id → integer

    • Holds the unique numeric identifier of the product
  • title → string

    • The title of the vulnerability, including the product name, version, and vulnerability type
  • description → string

    • A short description about the vulnerability
  • disclosure_date → datetime → YYYY-MM-DD HH:MM:SS

    • Date of when the vulnerability was publicly disclosed
  • disclosed_at → datetime → ISO 8601 format

    • Date of when the vulnerability was publicly disclosed
  • created_at → datetime → ISO 8601 format

    • Date of when the vulnerability was created and added to the database
  • url → string

    • The slug of the vulnerability which is used for the URL
  • product_slug → string

    • The slug of the product
    • The slug will be in lowercase, so make sure to convert your own slugs to lowercase before doing any comparison to this property
  • product_name → string

    • The title / name of the product
  • product_name_premium → string → nullable

    • The title / name of the product
    • This is used in rare scenarios where a developer of a plugin has 2 versions of their plugin but with the same slug but different product names.
  • product_type → string

    • The type of the product. Can be Plugin, Theme or WordPress
  • vuln_type → string

    • The vulnerability type, some examples are SQL Injection and Cross Site Scripting
  • cvss_score → decimal → nullable

    • The CVSS score of the vulnerability, between 1 and 10. Can be null, the older vulnerabilities in the database have not been classified yet.
  • cve → array of strings → can be an empty array

    • Contains an array of CVE ID’s bound to the vulnerability. One vulnerability could have multiple CVE ID’s. There are also vulnerabilities without CVE ID’s.
  • affected_in → string

    • The versions which are affected by this vulnerability.

    • Formats:

      • <= x.x.x (affecting versions up to and including)
      • < x.x.x (affecting versions up to)
      • x.x.x-x.x.x (affecting a specific range of versions, inclusive)
      • x.x.x,x.x.x (affecting specific versions)
      • x.x.x (affecting one version)
    • WordPress does not force plugin developers to stick to a certain versioning format. There are versions out there in an unusual format which is out of our control. Some plugins use a version in the form of a date such as 20220202, some use letters such as 2.0.2a, some just keep adding a number to the version e.g. 4.0000002. However, for the most part it’s in the usual format of x.x.x or x.x or x.x.xx

  • fixed_in → string → can be empty

    • The oldest version which has the vulnerability fixed
    • This can be empty, which implies that we have not recorded a fixed version for this vulnerability yet
  • patched_in_ranges → array of strings → can be an empty array

    • In case the WordPress core, plugin or theme have patched sub-versions, this will hold an array of versions in the format of:

      • from_version → string
        • Starting version, inclusive
      • to_version → string
        • Ending version, inclusive
      • fixed_in → string
        • The version which has the patch applied
    • You see this often in WordPress core vulnerabilities as they still support older versions such as 5.1, 5.2, 5.3, etc. Bigger plugins such as WooCommerce and Ninja Forms also do this.

  • direct_url → string

    • The direct URL of the vulnerability hosted at the Patchstack database frontend.
  • is_exploited → boolean

    • Whether or not the vulnerability is known to be exploited by Patchstack
  • patch_priority → integer → nullable

    • The patch priority value of the vulnerability which implies how soon the developer needs to patch the vulnerability and how soon the customers need to be protected.

      • NULL = unknown
      • 1 = Low → patch within 30 days
      • 2 = Medium → patch within 7 days
      • 3 or higher = High → patch immediately

Implementation

Section titled “Implementation”

Since some of these properties must be kept in mind while determining if a component is vulnerable or not, we have an example PHP script below which will explain the flow. In particular, the following properties must be used: product_slug, product_name_premium, affected_in, patched_in_ranges.

Note that it is an example implementation and should not be copied 1:1 for internal use, you’ll likely want to call the /all API endpoint using a different HTTP library and store the JSON response somewhere else (such as a memory based cache). The example will utilize Laravel’s collect function and Guzzle. The composer.json file which was used for this example is also included below.

{
    "require": {
        "illuminate/collections": "^8.83",
        "guzzlehttp/guzzle": "7.0"
    }
}
<?php


require './vendor/autoload.php';
use GuzzleHttp\Client;


/**
 * Determine if the component is vulnerable given the information of the component and present vulnerabilities.
 *
 * @param string $name
 * @param string $slug
 * @param string $currentVersion
 * @param string $type
 * @param \Illuminate\Support\Collection $vulnerabilities
 * @return bool
 */
function isVulnerable(string $name, string $slug, string $currentVersion, string $type, \Illuminate\Support\Collection $vulnerabilities): bool
{
    // Must have a valid current version.
    if (empty($currentVersion)) {
        return false;
    }


    // Determine if there is a vulnerability with this slug and type.
    $vulns = $vulnerabilities->where('product_slug', $slug)->where('product_type', getProductType($type));
    if ($vulns->count() === 0) {
        return false;
    }


    // Now we will loop through the vulnerabilities and return upon the first match.
    foreach ($vulns as $vuln) {
        // Get the current version, remove "v" as some components put this in place.
        $currentVersion = str_replace('v', '', strtolower($currentVersion));


        // The patched in ranges hold priority.
        if (count($vuln['patched_in_ranges']) > 0) {
            // Loop through all the present ranges.
            foreach ($vuln['patched_in_ranges'] as $range) {
                if (version_compare($currentVersion, $range['from_version'], '>=') && version_compare($currentVersion, $range['to_version'], '<=') && isMatchingName($name, $vuln['product_name_premium'])) {
                    return true;
                }
            }


            // If the patched in ranges exist and no match was made, we assume it's a fixed in the given version at this point.
            continue;
        }


        // Ignore empty affected in version, should never happen but best to catch it.
        $affectedIn = trim($vuln['affected_in']);
        if (empty($affectedIn)) {
            continue;
        }


        // Match against <= or <.
        if (strpos($affectedIn, '<= ') !== false || strpos($affectedIn, '< ') !== false) {
            $t = explode(' ', $affectedIn);
            $comparison = $t[0];
            $version = $t[1];


            if (version_compare($currentVersion, $version, $comparison) && isMatchingName($name, $vuln['product_name_premium'])) {
                return true;
            }


            continue;
        }


        // Match against versions separated by commas.
        if (strpos($affectedIn, ',') !== false) {
            $versions = explode(',', $affectedIn);
            foreach ($versions as $version) {
                $version = trim($version);
                if ($version == $currentVersion && isMatchingName($name, $vuln['product_name_premium'])) {
                    return true;
                }
            }


            continue;
        }


        // Match against a range of versions.
        if (strpos($affectedIn, '-') !== false) {
            $t = explode('-', $affectedIn);
            $start = $t[0];
            $end = $t[1];


            if (version_compare($currentVersion, $start, '>=') && version_compare($currentVersion, $end, '<=') && isMatchingName($name, $vuln['product_name_premium'])) {
                return true;
            }


            continue;
        }


        // Otherwise we are likely matching against one single version.
        if ($currentVersion == $affectedIn && isMatchingName($name, $vuln['product_name_premium'])) {
            return true;
        }
    }


    return false;
}


/**
 * If the premium field is filled in, match if it equals.
 * If it's empty, we will always return true.
 *
 * @param string $name
 * @param mixed $premiumName
 * @return bool
 */
function isMatchingName(string $name, mixed $premiumName): bool
{
    if (empty($premiumName)) {
        return true;
    }


    return $name === $premiumName;
}


/**
 * Convert the product type to how it's stored in the API.
 *
 * @param string $type
 * @return string
 */
function getProductType(string $type): string
{
    switch (strtolower($type)) {
        case 'plugin':
            return 'Plugin';
        case 'theme':
            return 'Theme';
        case 'wordpress':
            return 'WordPress';
        default:
            return 'Plugin';
    }
}


// Send the HTTP request, you'll likely want to cache this for an hour at a minimum.
if (!file_exists('db.cache')) {
    try {
        $client = new Client([
            'base_uri' => 'https://patchstack.com/database/api/v2/'
        ]);


        $response = $client->request('GET', 'all', [
            'headers' => [
                'PSKey' => '<your_pskey>'
            ]
        ]);


        file_put_contents('db.cache', (string) $response->getBody());
    } catch (\Throwable $e) {
        echo $e->getMessage();
        exit;
    }
}


// Get the vulnerabilities from the cache.
$vulnerabilities = json_decode(file_get_contents('db.cache'), true)['vulnerabilities'];


// Turn it into a collection.
$vulnerabilities = collect($vulnerabilities);


// The component we want to check, this is taken from your own dataset.
$component = [
    'name' => 'Ninja Forms',    // The name of the component
    'slug' => 'ninja-forms',    // The slug of the component
    'version' => '3.6.9',       // The current version of the component
    'type' => 'plugin'          // The component type
];


// Should return true.
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// Should return false.
$component['version'] = '3.6.10';
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// The component we want to check, this is taken from your own dataset.
$component = [
    'name' => 'WooRockets Nitro',   // The name of the component
    'slug' => 'wr-nitro',           // The slug of the component
    'version' => '1.7.5',           // The current version of the component
    'type' => 'theme'               // The component type
];


// Should return true.
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// Should return false.
$component['version'] = '1.7.10';
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// The component we want to check, this is taken from your own dataset.
$component = [
    'name' => 'WordPress',  // The name of the component
    'slug' => 'wordpress',  // The slug of the component
    'version' => '5.8.2',   // The current version of the component
    'type' => 'wordpress'   // The component type
];


// Should return true.
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// Should return false.
$component['version'] = '5.8.3';
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// Should return true.
$component['version'] = '5.9';
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));


// Should return false.
$component['version'] = '5.9.2'
var_dump(isVulnerable($component['name'], $component['slug'], $component['version'], $component['type'], $vulnerabilities));

Running this script with the proper PSKey injected on line 147 should result in the following response:

bool(true)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)

NPM features (Beta)

The Beta surface extends the Threat Intelligence API ahead of GA. It runs at a separate base URL, ships its own OpenAPI spec, and is available to selected partners working directly with Patchstack. The shared endpoints (/latest, /product/{type}/{name}/{version}, /product/{type}/{name}/{version}/exists, /batch) behave the same as the stable API but accept extra parameters and return a nested response shape. This page documents only what’s new — for the full spec including npm-flavour examples, see the auto-generated reference. Contact us if you’d like access.

Base URL

Section titled “Base URL”

https://vdp-api.patchstack.com/database/api/beta/

What’s new

Section titled “What’s new”

AdditionWhere it applies
GET /allNew endpoint — cursor-paginated full listing of every advisory, scoped by ?platform=.
?platform=npmList endpoints (/all, /latest, /product/...). Default is wordpress; case-insensitive.
?cursor=/all and /latest — cursor pagination alongside the existing ?page=&per_page=.
?include=detailsList endpoints — adds an advisory_details markdown field to each item (npm).
Nested response shapeAll list endpoints — product, cvss, cwe, version_info objects in place of the v2 flat shape.

The full Beta schema (every endpoint, parameter, response example) lives in the auto-generated reference.

Spec stability: the Beta spec may change without a version bump while the API is in beta. Pin a commit of the YAML in production integrations, or wait for the GA release when versioned URLs ship.

Pagination

Section titled “Pagination”

/all and /latest support two independent strategies. Use whichever fits your client:

  • Offset (?page=&per_page=) — returns a pagination block with totals, has_next_page, has_previous_page, etc. Easy to jump to a specific page; slower at depth and susceptible to row-shift when new advisories land while you’re paging.
  • Cursor (?cursor=) — returns a cursor block with next_cursor, has_more, per_page. Stable under concurrent inserts and faster at any depth. No total count (deliberately skipped to keep cursor mode fast).

cursor and page are mutually exclusive; passing both returns 422 Unprocessable Entity. To bootstrap cursor mode, send ?cursor= with an empty value.

When a cursor is malformed (invalid base64 or missing the v1: prefix), the endpoint returns 200 with an empty page:

{
  "vulnerabilities": [],
  "cursor": { "next_cursor": null, "has_more": false, "per_page": 100 }
}

Scoped npm packages

Section titled “Scoped npm packages”

npm package slugs that include a / (e.g. @scope/pkg) conflict with the route separator. URL-encode the / as %2F or contact us for guidance on the encoding helper.

Errors

Section titled “Errors”

In addition to the stable error codes, Beta returns:

StatusMeaning
422 Unprocessable EntityInvalid parameter combination (e.g. cursor + page), invalid platform, or per_page > 500.

Migration notes (stable v2 → beta)

Section titled “Migration notes (stable v2 → beta)”

  • Beta npm responses use nested objects (product, cvss, cwe, capec, version_info) whereas the v2 shape is flat. Update parsers accordingly.
  • ghsa_id was renamed to ghsa at the top level.
  • direct_url was renamed to url (the npm-flavoured shape exposes a single URL only).
  • The description field was dropped for npm (the title already includes it).
  • The response body always contains a vulnerabilities array, plus either pagination (offset mode) or cursor (cursor mode).

Testing

Section titled “Testing”

curl — one-liners

Section titled “curl — one-liners”

# Latest 24h, npm
curl 'https://vdp-api.patchstack.com/database/api/beta/latest?platform=npm&per_page=10' \
  -H 'PSKey: <your-api-key>'


# Cursor pagination walk (bootstrap + follow)
curl 'https://vdp-api.patchstack.com/database/api/beta/all?platform=npm&per_page=50&cursor=' \
  -H 'PSKey: <your-api-key>'


# Check a specific npm package/version with full advisory text
curl 'https://vdp-api.patchstack.com/database/api/beta/product/npm/axios/0.21.4?include=details' \
  -H 'PSKey: <your-api-key>'


# Boolean-only exists check
curl 'https://vdp-api.patchstack.com/database/api/beta/product/npm/axios/0.21.4/exists' \
  -H 'PSKey: <your-api-key>'

Cursor iteration (JavaScript / Node)

Section titled “Cursor iteration (JavaScript / Node)”

async function* allVulnerabilities(apiKey, platform = 'npm', perPage = 100) {
  const base = 'https://vdp-api.patchstack.com/database/api/beta/all';
  let cursor = '';            // empty = bootstrap cursor mode


  while (true) {
    const url = `${base}?platform=${platform}&per_page=${perPage}&cursor=${encodeURIComponent(cursor)}`;
    const res = await fetch(url, { headers: { PSKey: apiKey } }).then(r => r.json());


    for (const vuln of res.vulnerabilities) {
      yield vuln;
    }


    if (!res.cursor.has_more) return;
    cursor = res.cursor.next_cursor;
  }
}


// usage
for await (const vuln of allVulnerabilities(process.env.PATCHSTACK_KEY)) {
  console.log(vuln.id, vuln.title);
}

Cursor iteration (PHP)

Section titled “Cursor iteration (PHP)”

<?php


$apiKey = getenv('PATCHSTACK_KEY');
$cursor = '';


do {
    $url = 'https://vdp-api.patchstack.com/database/api/beta/all'
        .'?platform=npm&per_page=100&cursor='.urlencode($cursor);


    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['PSKey: '.$apiKey, 'Accept: application/json'],
    ]);
    $response = json_decode(curl_exec($ch), true);
    curl_close($ch);


    foreach ($response['vulnerabilities'] as $vuln) {
        // handle $vuln
    }


    $cursor = $response['cursor']['next_cursor'] ?? null;
} while ($response['cursor']['has_more'] ?? false);

More information

Section titled “More information”

You can find more information about the Patchstack Threat Intelligence API on https://patchstack.com/for-hosts/. If you have integration questions, email dave.jong@patchstack.com.

Threat Intelligence API

The Threat Intelligence API has custom pricing and is activated on requestcontact us.

Interactive reference: Every endpoint, parameter, request body and response shape is documented in the Threat Intelligence API reference.

Tooling (Postman, SDK, LLM): spec URLs and import instructions live on Overview → Using the API with your tools.

This page covers the concepts you need to use the API effectively — authentication, rate limiting, errors, and code samples. Use it alongside the interactive reference.

Endpoints at a glance

Section titled “Endpoints at a glance”

EndpointPurpose
GET /product/{type}/{name}/{version}Advisory list for a single product + version.
GET /product/{type}/{name}/{version}/existsBoolean-only exists check (faster).
GET /latestThe 20 most recent vulnerabilities.
POST /batchBulk lookup — up to 50 products per request.
GET /vulnerability/{id}Advisory detail (CVSS vector, OWASP, references, credit).

Each per-item payload includes description, vuln_type, cvss_score, cve, is_exploited, patch_priority, affected_in, and patched_in_ranges. See API properties for full field definitions.

Base URL

Section titled “Base URL”

https://patchstack.com/database/api/v2/

Authentication

Section titled “Authentication”

Every request must include your API key in the PSKey HTTP request header. Access is activated on request — contact us to request a key.

PSKey: <your-api-key>

Response format

Section titled “Response format”

All responses are JSON. Responses are cached until the Patchstack database updates, at which point the cache is cleared. GET /vulnerability/{id} returns a richer, differently-shaped payload documented in the reference.

Batch lookups

Section titled “Batch lookups”

POST /batch accepts an array of up to 50 {name, version, type, exists?} items. The response is keyed by product_slug, not by array index — duplicate slugs in the request collapse. Per-item exists: true returns a boolean for that slug; exists: false (or omitted) returns the full advisory list.

Rate limiting

Section titled “Rate limiting”

Custom, set per contract. Contact https://patchstack.com/for-hosts/ if you need a quota change.

Errors

Section titled “Errors”

StatusMeaning
401 UnauthorizedMissing or invalid PSKey header.
403 ForbiddenAPI key not authorised for the requested endpoint.
404 Not FoundUnknown product/version or vulnerability id.
422 Unprocessable EntityInvalid request payload (e.g. batch with more than 50 items).
429 Too Many RequestsRate limit exceeded.
500Server error — please include the request id in any bug report.

Testing — curl one-liners

Section titled “Testing — curl one-liners”

# Latest 20 vulnerabilities
curl 'https://patchstack.com/database/api/v2/latest' \
  -H 'PSKey: <your-api-key>'


# Full advisory list for a plugin version
curl 'https://patchstack.com/database/api/v2/product/plugin/tutor/1.5.2' \
  -H 'PSKey: <your-api-key>'


# Boolean-only exists check
curl 'https://patchstack.com/database/api/v2/product/plugin/tutor/1.5.2/exists' \
  -H 'PSKey: <your-api-key>'


# Batch — boolean-only across two products
curl -X POST 'https://patchstack.com/database/api/v2/batch' \
  -H 'PSKey: <your-api-key>' \
  -H 'Content-Type: application/json' \
  -d '[
    {"name":"easy-digital-downloads1","version":"1.0.0","type":"plugin","exists":true},
    {"name":"wordpress","version":"3.0.0","type":"wordpress","exists":true}
  ]'


# Advisory detail by id
curl 'https://patchstack.com/database/api/v2/vulnerability/4760' \
  -H 'PSKey: <your-api-key>'

Batch walk (PHP)

Section titled “Batch walk (PHP)”

<?php


$apiKey = getenv('PATCHSTACK_KEY');
$components = [
    ['name' => 'easy-digital-downloads1', 'version' => '1.0.0', 'type' => 'plugin',    'exists' => false],
    ['name' => 'wordpress',               'version' => '3.0.0', 'type' => 'wordpress', 'exists' => true],
];


$ch = curl_init('https://patchstack.com/database/api/v2/batch');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['PSKey: '.$apiKey, 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS     => json_encode($components),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);


foreach ($response['vulnerabilities'] as $slug => $result) {
    if (is_bool($result)) {
        echo "{$slug}: ".($result ? 'vulnerable' : 'clear').PHP_EOL;
    } else {
        echo "{$slug}: ".count($result)." advisor".(count($result) === 1 ? 'y' : 'ies').PHP_EOL;
    }
}

More information

Section titled “More information”

You can find more information about the Patchstack Threat Intelligence API on https://patchstack.com/for-hosts/. If you have integration questions, email dave.jong@patchstack.com.

Overview

The Patchstack Threat Intelligence API exposes our vulnerability database for WordPress plugins, themes and core. It supports single-product lookups, bulk lookups (POST /batch, up to 50 products per request), the /latest rolling feed, advisory-by-id detail, and a per-item payload that includes cvss_score, cve, is_exploited, patch_priority, and patched_in_ranges.

Custom pricing, activated on request — contact us.

NPM features (Beta)

Section titled “NPM features (Beta)”

For partners covering JavaScript components, a Beta surface adds npm-ecosystem coverage alongside WordPress, available to selected partners working directly with Patchstack. Beyond npm itself it ships the supporting additions that make npm coverage workable at scale: the new GET /all endpoint, cursor pagination, ?include=details for full advisory bodies, and a nested response shape the stable API will eventually adopt. Everything else (/latest, /product/{type}/{name}/{version}, /batch) is the same surface as the stable API.

See the NPM features (Beta) page for the full delta — base URL, parameters, pagination, errors, and migration notes — and the auto-generated reference for the full schema. Contact us for access.


The legacy Standard tier is no longer offered to new customers but remains documented for existing integrations — its endpoints are a strict subset of the current API.


Using the API with your tools

Section titled “Using the API with your tools”

The API ships with an OpenAPI spec and a generated Postman collection. Beta has its own spec — pick whichever you’re integrating against:

OpenAPI specPostman collection
Threat Intelligence APIthreat-intel-extended.yamlthreat-intel-extended.postman_collection.json
NPM features (Beta)threat-intel-beta.yamlthreat-intel-beta.postman_collection.json
Standard (legacy)threat-intel-standard.yamlthreat-intel-standard.postman_collection.json

Postman, Insomnia, Bruno or Hoppscotch

Section titled “Postman, Insomnia, Bruno or Hoppscotch”

Every endpoint, parameter, request body and example is preconfigured. Download the Postman collection and drag it into your tool, or import by URL from inside the tool:

ToolHow to import
PostmanFile → Import → Link and paste the collection URL.
InsomniaCreate → Import From → URL → paste the OpenAPI URL.
BrunoCollection → Import → OpenAPI V3 Spec → paste the OpenAPI URL.
HoppscotchCollections → Import/Export → OpenAPI → paste the OpenAPI URL.

Authentication: in Postman set the collection Authorization to API Key, key PSKey, value {{PSKEY}}, and add PSKEY as a collection variable with your real key as the Current value (leave Initial blank so it doesn’t sync to teammates). Other tools work the same way — set PSKey as a collection header once.

Claude Code and other LLM coding assistants

Section titled “Claude Code and other LLM coding assistants”

Point your assistant at the spec. LLMs parse OpenAPI cleanly and will generate clients that match the real field names instead of hallucinating.

  • Ad hoc: paste the spec URL into your prompt. Example: “Write a Python client for https://docs.patchstack.com/schemas/threat-intel-extended.yaml. I need a batch walker over a package.json-style list.”
  • In your repo: download the spec to docs/vendor/patchstack-threat-intel.yaml and reference it from your CLAUDE.md / AGENTS.md. Your assistant can then grep the YAML for specific fields without refetching.
  • Plain-text fallback: for tools that don’t parse YAML, our llms-full.txt contains the full reference as flat markdown.

SDK generation

Section titled “SDK generation”

Generate a client in any language from the spec:

# TypeScript
npx @openapitools/openapi-generator-cli generate \
  -i https://docs.patchstack.com/schemas/threat-intel-extended.yaml \
  -g typescript-fetch -o ./patchstack-client


# Python
npx @openapitools/openapi-generator-cli generate \
  -i https://docs.patchstack.com/schemas/threat-intel-extended.yaml \
  -g python -o ./patchstack-client-py

Speakeasy and Fern also consume the same spec and produce more idiomatic SDKs if you need a polished client library.

Spec stability: the Beta spec may change without a version bump while the API is in beta. Pin a commit of the YAML in production integrations, or wait for the GA release when we’ll publish versioned URLs. The stable spec tracks the v2 surface.

Standard tier API

The Standard Threat Intelligence API is no longer offered to new customers. New integrations should use the Threat Intelligence API, which is a strict superset — every endpoint and field documented here is available there. This page is preserved for existing Standard integrations.

Interactive reference: Every endpoint, parameter and response shape is documented in the Threat Intelligence API (Standard) reference.

Tooling (Postman, SDK, LLM): spec URLs and import instructions for all three tiers live on Overview → Using the APIs with your tools.

This page covers the concepts you need to use the API effectively — authentication, rate limiting, errors, and code samples. Use it alongside the interactive reference.

Base URL

Section titled “Base URL”

https://patchstack.com/database/api/v2/

Authentication

Section titled “Authentication”

Every request must include your API key in the PSKey HTTP request header. Standard keys remain valid for existing customers. New keys are not issued — contact us about Extended.

PSKey: <your-api-key>

Response format

Section titled “Response format”

All responses are JSON. Responses are cached until the Patchstack database updates, at which point the cache is cleared.

The Standard tier returns a flat per-item shape. For the richer shape with description, vuln_type, cvss_score, cve, and patched_in_ranges, use the Extended tier. Field definitions live in API properties.

Rate limiting

Section titled “Rate limiting”

Standard is limited to 5,000 requests per 24 hours. Contact https://patchstack.com/for-hosts/ to move to Extended for a higher quota and additional endpoints.

Errors

Section titled “Errors”

StatusMeaning
401 UnauthorizedMissing or invalid PSKey header.
403 ForbiddenAPI key not authorised for the requested endpoint.
404 Not FoundUnknown product/version combination.
429 Too Many RequestsRate limit exceeded.
500Server error — please include the request id in any bug report.

Testing — curl one-liners

Section titled “Testing — curl one-liners”

# Full advisory list for a plugin version
curl 'https://patchstack.com/database/api/v2/product/plugin/tutor/1.5.2' \
  -H 'PSKey: <your-api-key>'


# Boolean-only exists check (faster)
curl 'https://patchstack.com/database/api/v2/product/plugin/tutor/1.5.2/exists' \
  -H 'PSKey: <your-api-key>'


# WordPress core
curl 'https://patchstack.com/database/api/v2/product/wordpress/wordpress/5.8.2' \
  -H 'PSKey: <your-api-key>'

More information

Section titled “More information”

You can find more information about the Patchstack Threat Intelligence API on https://patchstack.com/for-hosts/. If you have integration questions, email dave.jong@patchstack.com.

Frequently Asked Questions

Account & Profile - Frequently Asked Questions

2FA recovery

If you have lost the ability to log in with 2FA (two factor authentication), we can remove it from your Patchstack account manually.

To request removing the 2FA from your account:

  1. Write an email stating that you wish to remove 2FA from your account. In the email, write down 3 domains that you protect with Patchstack, that are active on your account. In case you have less domains, write these all down.
  2. Email must be sent from the same email address that your Patchstack account is registered with.
  3. Send your email to sander.j@patchstack.com.
  4. For faster processing, you can also notify us about it via our support chat.

Alerts & Notifications - Frequently Asked Questions

How to send email notifications to all my team members?

Currently, Patchstack sends notifications only to the user, who is the owner or manager of the site. But in case you would like to get notified to other email addresses, you can set up the custom alerts!

Note that custom alerts is a feature for Developer or Enterprise plan accounts only.

Setting up an email alert

Section titled “Setting up an email alert”

  1. Navigate to the Alerts page from Patchstack App
  2. On the top right corner, click on + Create Trigger button
  3. Give your trigger a title (e.g. Notification to Joe)
  4. Choose the condition of when the alert is triggered. If you want to alert this email about found vulnerabilities, pick "Vulnerable" from the list
  5. Choose Email Notification and enter the email

If you have multiple people you wish to send notifications to, you will need to repeat the process and add another email address.

Click here to read more about creating custom alert triggers!

Billing & Refunds - Frequently Asked Questions

Do you offer refunds?

Patchstack offers refunds within 30 days of the first payment.

To request a refund, please open the new chat in our support channel. After your refund request, we will check your account and let you know about the process of refunding.

Errors - Frequently Asked Questions

Error: Blocked as suspected bot

This error might show up if visitors try to leave a comment on your site.

It is not caused by Patchstack but by a plugin called “MOJO Marketplace” or “Bluehost” as part of your Bluehost WordPress installation.

Either deactivate these plugins or reach out to your hosting company for more information as to why visitors are getting that error when they are trying to submit a comment.

Error: Cannot activate plugin because of: SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

When you see this error when you attempt to activate Patchstack:

  1. Check, if your server is using TLS 1.2 protocol activated. You can easily test it here: https://www.cdn77.com/tls-test In case it is disabled, you should activate it in your hosting environment.
  2. Ask your host to upgrade cURL/OpenSSL on your server to the latest version.

Error: "Cannot redeclare class Patchstack in…"

There are many reasons why this can happen, usually, it’s because of an .htaccess issue or because the Patchstack plugin does not work properly with one of your other plugins.

First, please copy and paste the PHP error that you are facing and send it to us through our chatbox at the bottom right corner of the page. This will help us to resolve your issue.

Secondly, if you can still access your WordPress admin panel, go to Plugins, find Patchstack Security and click on Deactivate. After that, if your site still shows the fatal error, go back to Plugins and click Delete under Patchstack Security.

If you cannot access your WordPress admin area at all, we recommend deleting the plugin manually. For that, follow the instructions here.

Error code 22

Error code 22 is the internal code we use for “temporary IP ban”. If you see this error even though you are a legitimate visitor, there can be multiple causes.

Please check the following steps:

  1. The real visitor's IP address is not properly forwarded to your application, either due to a proxy server or another plugin that overrides it. This causes the IP address of the server or proxy to be logged for all visitors which can block all traffic.
    We have a setting to override the IP header we use to retrieve the IP address. To find it, go to Patchstack App > yoursite.com > Protection > Additional settings > Scroll down to IP address header override setting. For example, if your host tells you it's in $_SERVER['IP-Header-X'] then you enter IP-Header-X in the text field.
  2. You have a plugin installed which sends a suspicious payload behind the scenes which ultimately triggers our temporary IP ban feature.
  3. The error page is cached by a caching plugin. We send error code 403 when this error is shown so this should never really happen unless the cache server is configured incorrectly.
  4. Make sure that you have whitelisted the proper user roles for your site. Check the user roles whitelist settings, by navigating to Patchstack App > yoursite.com > Protection > Additional settings.

The temporary IP ban usually lifts within 30 minutes. You can start a chat with us, make sure to provide the URL of your site so we can investigate the exact cause and fix it permanently.

Error code 24

Error code 24 means that there has been too many failed log in attempts. Therefore the IP got temporarily blocked by Patchstack.

You can adjust the threshold for failed log in attempts from Patchstack App, by navigating to Sites > yoursite.com > Hardening > Login protection

By scrolling down, you can see the Block IP addresses on login section, where you can tweak the settings.

  • Enable/disable automatic brute-force IP ban
  • Block IP for X minutes; after Y failed login attempts; over a period of Z minutes (where you can define X, Y and Z)

If you need any help, you can start a chat with us, make sure to provide the URL of your site so we can investigate the exact cause and fix it permanently.

Error code 5529

This error usually means that the visitor got blocked because of a malicious request received by your server.
If you are sure, it was a false positive blocking, you may whitelist the payload that got blocked.

We recommend you to check the firewall logs on your site. To open the firewall log:

  1. Go to Patchstack App > Sites > yourdomain.com
  2. Open the Protection tab
  3. Scroll to the bottom of this page, to find the Activity section
  4. Open the log entry which has the IP of the person who got blocked
  5. Copy the part of the payload that should be whitelisted

Example payload looks like this: [action] => edit_post

To whitelist a payload:

  1. Navigate to Patchstack App > Sites > yoursite.com > Protection > Additional Settings.
  2. Into the Whitelist textbox, type “PAYLOAD:[action] => edit_post”
  3. Click Save settings

If done correctly, the visitor should not get blocked with such request anymore.

If you have any questions regarding this error, feel free to chat with our live support here.

Error: "CSRF token missing or mismatch"

This error might show up on the Patchstack App when you perform certain actions.

Please follow these steps:

  1. Refresh the app/page by clicking the refresh button or by pressing F5.
  2. Logout from the Patchstack App.
  3. Login back into the Patchstack App.

This should resolve the issue. If it does not, please reach out to us so we can further investigate what is going wrong.

Error: "Sorry, this file type is not permitted for security reasons"

This can happen when you try to upload a file to your site.
The Patchstack plugin has no feature in place to prevent you from uploading files through the media / file manager, so this caused by a different plugin or by the default WordPress settings.

Take a look at this article to fix the issue.

Error: The site cannot be added since it is invalid or blocks Patchstack from accessing the site.

This error often appears when there is no public access to your website. There are 3 main reasons this is happening:

  1. Usually, it means that your server is protected using .htaccess and .htpasswd. To install the Patchstack plugin and connect Patchstack App to your website, it has to be publicly accessible for Patchstack as well, so you will have to remove the server authentication.

  2. In order for us to properly start monitoring your application, its response when you first add it must not be a 5xx HTTP status code.

  3. When your site is in maintenance mode, it will also result in a 5xx HTTP status code and thus will trigger this error.

Error: "The URL cannot be added since it returned a 5xx error code, this indicates an internal server error on your site. Please make sure it is accessible and not in maintenance mode."

In order for us to properly start monitoring your application, its response when you first add it must not be a 5xx HTTP status code.

When you put your site in maintenance mode, it will also result in a 5xx HTTP status code and thus will trigger this error.

Error: "The URL cannot be added since it timed-out or resulted in a server error. Is it currently online?"

This error can be shown because of multiple reasons:

  • The site is currently offline.
  • The site takes too long to load and times out.

Patchstack is blocked from accessing your site because of an IP block from your host, or because you have strict protection in place from a service such as Cloudflare, Incapsula, or Sucuri.

Error: "Warning: Cannot modify header information - headers already sent by"

There are 2 possible reasons this can happen:

  1. Check the very first error that shows up on the screen. If this error occurs in a file that is unrelated to Patchstack then the initial cause of this error is not caused by the Patchstack plugin. Try turning off your plugins one-by-one until the error disappears.
  2. If the very first error that shows up on the screen is in a file of the Patchstack plugin, then please copy the error, start a new chat, and paste the error with your site URL. That way we can figure out the cause and fix the error in a future plugin version.

Error: You have entered an incorrect reCAPTCHA value on Login Page

Solution 1: The easiest solution is to clear the cache, try to login from a different browser or incognito/private browser mode.

Solution 2: If the first solution doesn’t work, it is necessary to deactivate the plugin manually. Please complete the following steps:

  1. Go to the /wp-content/plugins folder via FTP (see different FTP clients here);
  2. Find and rename “patchstack” folder into something else, like “deactivate_patchstack”;
  3. After you log into your dashboard, you can rename the folder back from "deactivate_patchstack" to "patchstack";
  4. Disable reCAPTCHA from the settings in Patchstack App, by navigating to
    Patchstack App > Sites > yoursite.com > Hardening > Captcha

Firewall - Frequently Asked Questions

App is showing the firewall of my site as delayed

This firewall error might show up on the Patchstack App.

On the Patchstack App, you might see that the firewall is indicating as being “delayed”.
This can happen due to a few reasons:

  1. Scheduled tasks are not running properly on your web application. We attempt to ping our API from your site every three hours. However, since WordPress scheduled tasks run when you have visitors on your site, this might not happen if you have no visitors on your site. It is also possible that scheduled tasks are not running at all on your site even when you have visitors due to an error. You can use a plugin such as WP Crontrol to keep track of your scheduled tasks.
  2. You do not have the right API key configured on the license settings page. The API credentials which you can find on the Patchstack App under Sites > yourdomain.com > Settings. API Keys should match the API credentials on your WordPress site at /wp-admin > Settings > Security.

One potential solution to reason 1 is to use a server-based scheduled task that triggers your scheduled tasks even when you have no visitors.

  1. Disable the default WordPress cronjob by adding the following to your wp-config.php file in the root folder of your site:

    define('DISABLE_WP_CRON', true);
    
  2. Set up a cronjob in your hosting account management panel. In cPanel, this can be found under Advanced > Cron Jobs.

  3. Set the interval to something between 5 and 15 minutes.

  4. Set the cron command to the following (change the URL to your own):

    wget -q -O - https://yoursite.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
    
  5. Now click on the Create new cron job button.

How do I block an IP address from accessing my site?

With Patchstack, it is easy to block certain IP addresses from accessing your site.

In order to block an IP address, do the following:

  1. Log in to the Patchstack App

  2. Go to Sites > yourdomain.com > Protection > Additional settings

  3. Scroll down to IP Block List

  4. Enter each IP addresses to a new line

  5. The following formats are accepted:

    • 127.0.0.1
    • 127.0.0.*
    • 127.0.0.0/24
    • 127.0.0.0-127.0.0.255

I activated the plugin, but still get the message: "Install the plugin to activate the firewall"

There is an easy way to fix this issue.

Usually, this is because for some reason we were unable to contact our server to process the activation.

In order to solve this, deactivating and then activating the plugin from the plugin list in the WordPress admin area should fix the issue.

If this still does not work, your host is probably blocking outgoing connections to our server, https://api.patchstack.com

Feel free to contact our support chat with this issue.

Legitimate visitors or pages are being blocked by the firewall. How do I add these to the whitelist?

Our whitelist feature makes it easy to whitelist specific requests.

To manage the whitelist:

  1. Log in to the Patchstack App
  2. Go to Sites > yourdomain.com > Protection > Additional settings
  3. Scroll down to Whitelist text field

This text field supports a specific syntax that you can use to whitelist specific requests. Each definition must be placed on its own line.

We accept the following parameters in this text field:

Parameters
IP:IPADDRESS
PAYLOAD:someval
URL:/someurl

Definitions
IP = firewall will not run against the IP
PAYLOAD = if the entire payload contains the keyword, the firewall will not proceed
URL = if the URL contains the given URL, the firewall will not proceed

Example
IP:192.168.1.1
PAYLOAD:contact_form
URL:water
URL:/some-form

In this scenario, the firewall will not run if the IP address is 192.168.1.1 or if the payload contains contact_form or if the URL contains water, or if the URL contains /some-form.

What is the difference between a WAF and vPatching?

WAF stands for Web Application Firewall, which is a firewall that inspects web traffic and blocks malicious requests. WAFs typically run on the web server software itself and have limited knowledge of the web applications they are protecting. WAFs tend to include and run all firewall rules against all requests, even if it does not apply to the underlying software.

vPatching is similar to WAF: blocking known malicious requests but running within the application itself. Patchstack’s vPatching goes a step further and can take into context information that only the application (such as WordPress) itself is aware of, like user authorization, software versions, etc. Patchstack has built the vPatch system, a specific method that provides auto-mitigation to open-source software security vulnerabilities through crowdsourced security research and AI/ML based source code analysis.

This means that vPatches tend to be more efficient and cause less resource usage in the application compared to a WAF because the only rules that are enabled are the ones applicable to each website.

Read more about vPatching here

Other - Frequently Asked Questions

Can I have other security plugins activated and run

… [truncated — open the raw llms.txt above for the full file]

Related

llmtxt.app – AI SEO & Search Engine Optimization Directory

/llms.txt
635 tokens
/llms-full.txt
2,429 tokens
Websites

A proposal to standardise on using an /llms.txt file to provide information to help LLMs use a website at inference time.

/llms.txt
318 tokens
Websites

/llms.txt
33,874 tokens
/llms-full.txt
3,770,473 tokens
Websites

/llms.txt
1,164 tokens
/llms-full.txt
1,167 tokens
Websites

About Matt Rickard.

/llms.txt
515,931 tokens
/llms-full.txt
515,931 tokens
Websites

/llms.txt
628 tokens
Websites

Evan Boehs — personal website.

/llms.txt
265 tokens
Websites

This very website you're looking at right now!

/llms.txt
48 tokens
Websites