How to Detect Yoast SEO Premium on Any Website (API)

September 9, 2026 · 10 min read

Detecting Yoast SEO tells you a site cares about search. Detecting Yoast SEO Premium tells you something considerably more useful: the site has a recurring line item for SEO tooling and someone on staff signs off on it. That is a different qualification signal, and it is available from a single unauthenticated HTTP request, because Premium announces itself in the page source with a comment that is distinct from the free plugin’s.

This guide covers the exact comment variants, why a Premium site comes back with two Yoast entries and sometimes two different version numbers, how to check one site with curl, how to check hundreds through the DetectZeStack API, and the one failure mode that will quietly poison a prospect list if you do not handle it. Every response below was captured from a live scan while writing this post.

Why Detecting Yoast SEO Premium Is Different From Detecting Yoast SEO

Yoast SEO is the free plugin from the WordPress.org repository, installed on an enormous number of sites, many of them by a theme bundle or a host’s one-click stack rather than by a deliberate decision. As a buying signal it is weak, precisely because it is so common and so free.

Yoast SEO Premium is a paid annual subscription per site. Someone had to buy it, expense it, and renew it. It carries the internal-link suggestion engine, redirect management, multiple focus keyphrases, and 24/7 support — the features a team reaches for when SEO is an ongoing workstream rather than a checkbox. For anyone selling SEO services, content, WordPress maintenance, or a competing SEO plugin, the Premium list is the segment worth the outreach budget and the free list mostly is not.

The two are detected separately, and that separation is the whole point of this article. Yoast SEO Premium is its own technology with its own fingerprint and its own version field. It is not a flag on the Yoast SEO entry, and you cannot infer it from the free entry.

The HTML Comment Fingerprint That Separates Premium From Free

Yoast wraps its output in an HTML comment on every page it optimizes, and the comment text differs between the two editions. That difference is the fingerprint.

The Free Plugin Comment

A site on the free plugin emits a single version number and nothing about a paid edition. This is the real comment from torquemag.io:

<!-- This site is optimized with the Yoast SEO plugin v28.4 - https://yoast.com/product/yoast-seo-wordpress/ -->

The matching pattern is anchored on the literal phrase Yoast SEO plugin v followed by the version, and it optionally tolerates the older Yoast WordPress SEO plugin wording that legacy installs still emit. Note that Yoast SEO Premium plugin does not satisfy this pattern — the word Premium sits between SEO and plugin, so the free pattern cannot match a Premium page. That is deliberate, and it is why Premium needs the second pattern below.

The Premium Plugin Comment (and the Yoast SEO Version It Carries)

A Premium site emits a longer comment that names the paid edition, gives the Premium build number, and then gives the core Yoast SEO version in parentheses. Real comments from three live sites:

$ curl -s https://kinsta.com | grep -io "<!-- This site is optimized with the Yoast[^>]*>"
<!-- This site is optimized with the Yoast SEO Premium plugin v24.6 (Yoast SEO v24.6) - https://yoast.com/wordpress/plugins/seo/ -->

$ curl -s https://yoast.com | grep -io "<!-- This site is optimized with the Yoast[^>]*>"
<!-- This site is optimized with the Yoast SEO Premium plugin v28.5-RC1 (Yoast SEO v28.5-RC2) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->

$ curl -s https://www.searchenginejournal.com | grep -io "<!-- This site is optimized with the Yoast[^>]*>"
<!-- This site is optimized with the Yoast SEO Premium plugin v28.3 (Yoast SEO v28.3) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->

Two things are worth noticing before moving on. The trailing URL is not stable — kinsta.com points at /wordpress/plugins/seo/ while the other two point at /product/yoast-seo-premium-wordpress/, because the plugin changed the link it emits at some point between those builds. Do not anchor your own regex on the URL. And the two version numbers can disagree, which the next section covers.

EditionComment phrase to matchVersion captured
Yoast SEO (free)Yoast SEO plugin v…The plugin version
Yoast SEO PremiumYoast SEO Premium plugin v…The Premium build version
Yoast SEO (on a Premium site)… (Yoast SEO v…)The core version in parentheses

Why a Premium Site Returns Both Yoast SEO and Yoast SEO Premium

Premium is an add-on that runs on top of the free plugin, not a replacement for it, so a Premium install genuinely has both codebases present. The single Premium comment satisfies both fingerprints at once: Yoast SEO Premium matches on the build number at the front of the comment, and Yoast SEO matches on the core version inside the parentheses. Both entries come back in the same response, both at confidence 100, both with source: "http".

The two entries are not classified identically, and this trips people up when they filter by category. Yoast SEO is filed under both SEO and WordPress plugins. Yoast SEO Premium is filed under SEO only. So in the categories object of a real Premium scan, categories["WordPress plugins"] lists only Yoast SEO, while categories.SEO lists both. If you filter on the WordPress plugins category, you will miss every Premium detection.

Where WordPress comes from: the Yoast SEO fingerprint implies WordPress, so a Yoast hit pulls a separate WordPress entry into the response under the CMS category even if nothing else on the page identified the CMS. The Yoast SEO Premium fingerprint carries no implication of its own — it does not need to, because it can never fire without the Yoast SEO entry firing on the same comment.

Manual Detection With curl and grep

For one site, a single request answers the question. Match on the word Premium specifically, because a plain grep -i yoast returns hits on both editions and on the schema block, which tells you nothing about which one is installed:

$ curl -s https://kinsta.com | grep -io "yoast seo premium plugin v[^ ]*"
yoast seo premium plugin v24.6

Any output is a positive. To pull both version numbers out of the comment in one pass:

$ curl -s https://yoast.com \
  | grep -oiE "Yoast SEO Premium plugin v[^ ]+ \(Yoast SEO v[^)]+\)"
Yoast SEO Premium plugin v28.5-RC1 (Yoast SEO v28.5-RC2)

And to classify a site into premium, free, or neither in one shot:

$ html=$(curl -sL -A "Mozilla/5.0" https://torquemag.io)
$ if echo "$html" | grep -qi "yoast seo premium plugin"; then echo premium
  elif echo "$html" | grep -qi "yoast seo plugin"; then echo free
  else echo none; fi
free

This is fine for a handful of domains and it stops being fine somewhere around the second page of a prospect list. You end up hand-rolling redirect handling, timeouts, user-agent negotiation for sites that reject a bare client, and version parsing — and at the end you still only have one bit of information about a site whose hosting, page builder, ecommerce platform, and caching layer are the fields that actually decide whether the lead is worth a call.

Detecting Yoast SEO Premium With the DetectZeStack API

Free Check With the /demo Endpoint

The public /demo endpoint takes a url query parameter, requires no API key, is limited to 20 requests per hour per IP, and returns exactly the same JSON as the authenticated endpoint. Here is the live response for kinsta.com, trimmed to the entries this post is about:

$ curl -s "https://detectzestack.com/demo?url=kinsta.com" | jq '.'
{
  "url": "https://kinsta.com",
  "domain": "kinsta.com",
  "technologies": [
    {
      "name": "Yoast SEO",
      "version": "24.6",
      "categories": ["SEO", "WordPress plugins"],
      "confidence": 100,
      "description": "Yoast SEO is a search engine optimisation plugin for WordPress and other platforms.",
      "website": "https://yoast.com/wordpress/plugins/seo/",
      "icon": "Yoast SEO.png",
      "source": "http"
    },
    {
      "name": "Yoast SEO Premium",
      "version": "24.6",
      "categories": ["SEO"],
      "confidence": 100,
      "description": "Yoast SEO Premium is a search engine optimisation plugin for WordPress and other platforms.",
      "website": "https://yoast.com/wordpress/plugins/seo/",
      "icon": "Yoast SEO.png",
      "source": "http"
    },
    {
      "name": "WordPress",
      "categories": ["CMS", "Blogs"],
      "confidence": 100,
      "website": "https://wordpress.org",
      "icon": "WordPress.svg",
      "cpe": "cpe:2.3:a:wordpress:wordpress:*:*:*:*:*:*:*:*",
      "source": "http"
    },
    {
      "name": "Kinsta",
      "categories": ["PaaS", "Hosting"],
      "confidence": 100,
      "website": "https://kinsta.com",
      "icon": "kinsta.svg",
      "source": "http"
    }
  ],
  "categories": {
    "SEO": ["Yoast SEO", "Yoast SEO Premium"],
    "WordPress plugins": ["Yoast SEO"],
    "CMS": ["WordPress"],
    "Blogs": ["WordPress"],
    "PaaS": ["Kinsta"],
    "Hosting": ["Kinsta"],
    "Page builders": ["WordPress Block Editor"],
    "Programming languages": ["PHP"],
    "Databases": ["MySQL"],
    "CDN": ["Cloudflare"],
    "Security": ["Cloudflare Bot Management", "HSTS"],
    "JavaScript libraries": ["jQuery", "jQuery Migrate"],
    "Cloud hosting": ["Google Cloud"]
  },
  "meta": {
    "status_code": 200,
    "tech_count": 13,
    "scan_depth": "full"
  },
  "cached": false,
  "response_ms": 3502
}

That single call returned the Premium detection and the context that qualifies the lead: managed WordPress hosting on Kinsta, the block editor rather than a page builder, Cloudflare in front. The description, website, and icon fields are trimmed on two entries above to keep the listing readable; the API returns them whenever the fingerprint database has them, and omits them entirely when it does not.

The contrast case matters just as much. A scan of torquemag.io, on the free plugin, returned "SEO": ["Yoast SEO"] with version: "28.4" and no Premium entry at all, alongside WP Rocket and WP Engine — a real WordPress stack with real spend on hosting and caching, but not on Yoast.

Authenticated /analyze for the Full Technology Array

For production use, call /analyze with your key. The response shape is identical to /demo, so every jq filter you write against one works against the other:

$ curl -s "https://detectzestack.p.rapidapi.com/analyze?url=kinsta.com" \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" \
  | jq '{domain,
         premium: ((.categories.SEO // []) | index("Yoast SEO Premium")) != null,
         premium_version: (.technologies[] | select(.name == "Yoast SEO Premium") | .version),
         core_version:    (.technologies[] | select(.name == "Yoast SEO")         | .version),
         cms: .categories.CMS, hosting: .categories.Hosting,
         depth: .meta.scan_depth, tech_count: .meta.tech_count}'
{
  "domain": "kinsta.com",
  "premium": true,
  "premium_version": "24.6",
  "core_version": "24.6",
  "cms": ["WordPress"],
  "hosting": ["Kinsta"],
  "depth": "full",
  "tech_count": 13
}

Test membership in categories.SEO rather than comparing it for equality — it is an array, and on a Premium site it has at least two elements.

Narrowing to a Yes or No With /check

When a boolean is all you need, /check returns a much smaller object built from the same scan. The technology name is matched case-insensitively and has to be URL-encoded because it contains spaces:

$ curl -s "https://detectzestack.p.rapidapi.com/check?url=kinsta.com&tech=Yoast%20SEO%20Premium" \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: detectzestack.p.rapidapi.com"
{
  "domain": "kinsta.com",
  "technology": "Yoast SEO Premium",
  "detected": true,
  "confidence": 100,
  "version": "24.6",
  "categories": ["SEO"],
  "response_ms": 0,
  "cached": true
}

The technology field echoes back the canonical name on a match, so tech=yoast%20seo%20premium in lowercase still returns "Yoast SEO Premium". A miss returns "detected": false with confidence 0, an empty version, and an empty categories array. The response_ms: 0 and cached: true above are a cache hit from the /analyze call a moment earlier.

Scanning Up to 10 Domains at Once With POST /analyze/batch

POST /analyze/batch accepts up to 10 URLs per request and scans them concurrently. Each entry in results carries either a result object in the single-domain shape or an error string. The script below reads domains.txt, one domain per line, and writes a CSV with both version numbers so you can segment on edition and on build:

#!/usr/bin/env bash
# find-yoast-premium.sh - segment a domain list by Yoast edition
KEY="YOUR_KEY"
HOST="detectzestack.p.rapidapi.com"

echo "domain,edition,premium_version,core_version,hosting,tech_count" > yoast.csv
: > yoast_retry.txt

# Process domains.txt in batches of 10 (the /analyze/batch maximum)
xargs -n 10 < domains.txt | while read -r batch; do
  urls=$(printf '%s\n' $batch | jq -R . | jq -s '{urls: .}')
  resp=$(curl -s -X POST "https://$HOST/analyze/batch" \
    -H "X-RapidAPI-Key: $KEY" \
    -H "X-RapidAPI-Host: $HOST" \
    -H "Content-Type: application/json" \
    -d "$urls")

  # One row per fully scanned domain
  echo "$resp" | jq -r '.results[]
    | select(.result != null and .result.meta.scan_depth == "full")
    | .result as $r
    | ($r.categories.SEO // []) as $seo
    | [
        $r.domain,
        (if ($seo | index("Yoast SEO Premium")) != null then "premium"
         elif ($seo | index("Yoast SEO")) != null then "free"
         else "none" end),
        ([$r.technologies[] | select(.name == "Yoast SEO Premium") | .version] | first // ""),
        ([$r.technologies[] | select(.name == "Yoast SEO")         | .version] | first // ""),
        (($r.categories.Hosting // []) | join(";")),
        ($r.meta.tech_count | tostring)
      ] | @csv' >> yoast.csv

  # DNS-only scans never saw a page body, so Yoast status is unknown
  echo "$resp" | jq -r '.results[]
    | select(.result != null and .result.meta.scan_depth == "partial")
    | .result.domain' >> yoast_retry.txt
done

echo "scanned: $(($(wc -l < yoast.csv) - 1))"
echo "premium: $(awk -F, 'NR>1 && $2=="\"premium\""' yoast.csv | wc -l)"
echo "retry:   $(wc -l < yoast_retry.txt)"

A 1,000-domain list is 100 batch calls. Note the scan_depth filter: partial results are routed to a retry file instead of being written out as none, which is the difference between a clean segmentation and a list quietly padded with false negatives. Throughput, retry policy, and a production Python version of this loop are covered in how to batch scan 1,000 websites.

Reading the Version Field Correctly

Premium and the free plugin ship as separate downloads on separate release cadences, so the two versions in one comment can and do disagree. Yoast’s own site was running a release candidate of each when this post was written, and the two builds were not the same:

$ curl -s "https://detectzestack.com/demo?url=yoast.com" \
  | jq '[.technologies[] | select(.name | startswith("Yoast")) | {name, version}]'
[
  {
    "name": "Yoast SEO",
    "version": "28.5-rc2"
  },
  {
    "name": "Yoast SEO Premium",
    "version": "28.5-rc1"
  }
]

Three rules follow from that output. Read the Yoast SEO Premium version when the question is which paid build is installed — that is the number tied to the subscription. Read the Yoast SEO version when the question is compatibility or a known issue in the core plugin. And do not assume the strings are digits and dots: 28.5-rc1 is a legitimate value, so a naive numeric parse will throw on real data.

The versions also come back lowercased. The raw HTML on yoast.com reads v28.5-RC1, and the API returns 28.5-rc1, because the detector matches its patterns against a lowercased copy of the page body. Compare version strings case-insensitively, and expect rc, beta, and similar suffixes rather than a clean semantic version.

What Detection Cannot Tell You (Limitations and False Negatives)

This fingerprint fails in exactly one direction, and knowing which one keeps a prospect list honest. A detection is strong evidence that Premium is installed. A non-detection is not evidence that it is absent.

Practical Uses: Prospecting WordPress Sites That Already Pay for SEO Tools

The reason to separate the two editions is that they support opposite pitches from the same raw scan.

Selling SEO services or content. The Premium segment has an approved budget line and someone accountable for search performance. The free segment has intent without spend, which is a longer sales cycle at a lower price point. Sorting a list into premium, free, and none before anyone writes an email is the cheapest segmentation available.

Selling a competing SEO plugin. The Premium list is your displacement target and it is the only one with a provable switching cost, because you know what the incumbent costs. The free list is a volume play instead.

WordPress agencies and maintenance shops. A Premium detection paired with the rest of the response tells you what the maintenance contract has to cover. In the two scans above, kinsta.com is Premium on Kinsta hosting with the block editor, and torquemag.io is free Yoast on WP Engine with WP Rocket for caching — two different quotes, and the API returned the whole picture in one call each. See how to detect WP Rocket for the caching layer and find companies using Elementor for the page-builder dimension.

Tracking adoption over time. Because Premium carries its own version, re-scanning a list quarterly shows both churn between editions and upgrade lag. A site sitting several major versions behind is a maintenance conversation.

Try DetectZeStack Free

100 requests per month, no credit card required. Header, cookie, DNS, and TLS detection included on every plan.

Get Your Free API Key

Conclusion and Next Steps

Yoast SEO Premium is detected from one HTML comment: <!-- This site is optimized with the Yoast SEO Premium plugin v… (Yoast SEO v…) -->. The free plugin emits a shorter comment with no Premium in it, and the two are matched by separate patterns, so a Premium site returns both Yoast SEO Premium and Yoast SEO at confidence 100 with two independently versioned entries. Premium is filed under SEO only, so filter on categories.SEO rather than categories["WordPress plugins"]. For one domain, curl | grep -i "yoast seo premium plugin" settles it. For a list, /analyze/batch returns the edition, both versions, and the rest of the stack at 10 domains per call. The one rule to write into the pipeline: only record a negative when meta.scan_depth is "full", because a stripped comment and a missing plugin look identical from the outside.

Related Reading

Get API updates and tech detection tips

Join the mailing list. No spam, unsubscribe anytime.