=== Content Performance Analyzer – GA4 & Search Console Content Audit ===
Contributors: wajahatmubashir
Tags: content audit, content decay, search console, google analytics, seo audit
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.9.3
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Audit content decay with GA4 and Search Console data, and get a specific AI fix plan for every page losing traffic.

== Description ==

**Your AI content strategist for WordPress.** Content Performance Analyzer finds the pages that are quietly losing traffic, rankings, or conversions — and tells you exactly how to fix each one. It connects your Google Analytics 4 (GA4) and Google Search Console accounts, joins that data with the actual content of each page, and lets AI turn it into specific, page-level recommendations — so you spend your time refreshing content that matters instead of digging through spreadsheets.

Everything needed to find problems and get clear recommendations is free, with no URL limits. Bring an OpenAI, Groq, or Gemini key for free page-level AI recommendations. The optional Pro plan adds hosted AI with no provider key, ready-to-use draft assets, and full WordPress draft writing.

Every scan checks each page for three common problems:

* **Content decay** — pages that used to rank and are now losing traffic. Refreshing an old post is far cheaper than writing a new one, and these are the fastest wins.
* **SEO opportunities** — pages with strong impressions but weak click-through or ranking, where a title or heading change has outsized impact.
* **Conversion gaps** — pages getting real traffic but few conversions, often just missing a clear call to action.

For each flagged page, the built-in AI analysis writes a specific recommendation you can act on — not generic advice. Add an optional Serper API key and it also compares your page against the pages currently ranking for your keyword, so you can see what top results cover that you don't. It also maps the likely information branches behind each page's main search — the query fan-out that AI Mode and AI Overviews run — and marks each branch Covered, Likely covered, Mentioned in text, or No heading found.

This plugin is a strong fit for blogs, resource libraries, and content-heavy sites with dozens to a few hundred posts, and for freelancers or agencies managing content for clients.

= What You Get =

* **AEO readiness score** — every page rated 0-100 on a structural readiness checklist for AI search (question headings, direct answers, schema, dates), with each failed check named
* **Question gaps** — real Search Console queries you rank for that no heading on the page answers, plus one-click AI answer blocks with FAQ schema
* **Coverage map** — the likely information branches behind a page's main search intent, built from real Search Console queries, People Also Ask questions, and the headings of the pages that rank, each marked Covered, Likely covered, Mentioned in text, or No heading found on your page, with the one action to take. Google does not publish the exact sub-queries, so every branch is labeled an estimate
* **AI referral tracking** — see which pages get visitors from ChatGPT, Perplexity, Copilot, and Gemini
* **llms.txt** — optional machine-readable site guide for AI crawlers, generated from your analyzed pages
* **One-click Google connection** — connect Analytics and Search Console with a single sign-in, then pick your property and site from dropdowns. No Google Cloud setup required
* **One-click page import** — load every published page and post from your site with checkboxes, instead of pasting URLs
* **Works before any setup** — content-only mode checks pages for missing alt text, thin content, and heading problems with zero credentials
* **Performance flagging** — every page automatically labeled for decay, SEO opportunity, or conversion gap
* **AI content recommendations** — specific, page-level refresh guidance powered by OpenAI, Groq, or Google Gemini (free tier)
* **AI alt text fixes** — lists the exact images missing alt text and writes and applies alt text for you, using image recognition
* **Competitor gap analysis** — optional Serper integration compares your content against top-ranking pages
* **GA4 conversion tracking** — uses your GA4 key events by default, with support for custom conversion events like demo or trial signups
* **Keyword insights** — shows the primary keyword each page ranks for, pulled from Search Console
* **Recovery tracking** — mark a page as fixed and the plugin watches it for 28-60 days, then tells you whether the fix worked: recovered, improving, or no lift
* **One-click refresh drafts** — create a draft copy of any flagged page with its fix plan embedded at the top, so a refresh starts in the editor, not in a spreadsheet
* **Dashboard widget** — your top priority pages and fix results, right on the WordPress dashboard
* **Freshness at a glance** — published date, last WordPress edit, the last meaningful content change the plugin detected (with or without WordPress revisions), and a refresh-due status on every page
* **AI crawler rules** — allow or block each AI crawler in robots.txt from a checklist, with a recommended preset that welcomes AI search engines and turns away training-only crawlers
* **IndexNow** — tells Bing, Yandex, Seznam, Naver, and Yep the moment you publish, update, or unpublish a page, instead of waiting for the next crawl
* **Smart interlinking** — safe, AI-suggested internal links inserted only on exact anchor matches
* **PageSpeed Insights without a key** — Core Web Vitals and performance scores through the free hosted service, or your own API key if you prefer
* **Background processing** — analyzes hundreds of URLs in batches without timeouts, with live progress
* **CSV export** — download the full insights table for reporting or further analysis
* **Read-only access for your team** — Editors, Authors, and Contributors can open Content Insights to read the findings and export them; scans, AI generation, and every change to the site stay with administrators
* **Weekly automatic scan** — optional full rescan of every page on the day and hour you choose, so the table stays current without anyone starting it

= Optional Pro Workflow =

Pro does not lock the audit or diagnosis. It removes setup and writing work:

* **Hosted AI with no provider key** — recommendations work immediately after license activation
* **200 included AI generations per month** — the latest remaining allowance is visible in the page-plan modal
* **Competitor gap analysis with no Serper key** — 600 included searches a month compare your pages against what currently ranks
* **Ready-to-use draft assets** — finished titles, meta descriptions, question headings, FAQ answers, quotable passages, and evidence-based internal-link suggestions
* **Smart relevance and verification notes** — only justified assets are generated, and missing or unverified facts are called out for review
* **Full draft writing** — creates a separate 600–900 word WordPress draft for editorial review; the live page is not changed
* **One-click apply** — writes the suggested title and meta description straight into Rank Math, Yoast SEO, or SEOPress, with undo
* **Evidence audit and section drafts** — the claims on a page that need backing, each with what would support it; 40–90 word draft sections for the branches your page misses; and a copyable writer brief that says what to keep, add, restructure, link, or split out
* **Bring your own assistant (MCP)** — connect Claude Code, Cursor or Claude Desktop to your site's audit over the Model Context Protocol; your assistant reads the diagnosis, writes the fix plan or draft assets, and saves them back — on your own subscription, with no API key and no hosted-AI usage

Nothing here requires a Google Cloud project. The Google connection and PageSpeed both run through a free hosted service, so setup is a single sign-in. If you would rather run everything yourself, every hosted convenience has a self-hosted alternative: add your own PageSpeed or AI API key in Settings, and enable the `cpanalyzer_show_byo_oauth_fields` filter to use your own Google OAuth client.

== Installation ==

1. Install and activate the plugin through the 'Plugins' screen in WordPress
2. Go to Content Insights → URL Management and click "Load Pages From Your Site" to import your pages, then start a scan — the content check works with no setup at all
3. To add traffic data, go to Content Insights → Settings and click "Connect with Google," then pick your GA4 property and Search Console site from the dropdowns
4. Optional: add an OpenAI, Groq, or Google Gemini API key for AI recommendations, and toggle on PageSpeed analysis (no key needed)

== Screenshots ==

1. The content audit table — every page flagged for content decay, SEO opportunity, or conversion gap, with the decay-type diagnosis, page age, AI fix summary, and recovery actions in one view
2. A page-level AI fix plan grounded in the page's own GA4 and Search Console data: decay diagnosis, keyword cannibalization, SEO and answer-engine recommendations, and one-click alt text fixes
3. AEO readiness — every page scored 0-100 on how quotable it is for AI search, alongside the classic SEO metrics
4. Connect Google Analytics 4 and Search Console with one click — no Google Cloud project, no API keys, read-only access
5. Import every published page and post with checkboxes instead of pasting URLs, then run the first scan
6. Choose your AI provider — Groq and Google Gemini free tiers or OpenAI — with direct links to create a key
7. Once connected, pick your GA4 property and Search Console site from dropdowns; conversion events are optional

== Upgrade Notice ==

= 1.9.3 =
Editors, Authors and Contributors can now open Content Insights to read the findings. Scans, AI generation and every change to the site stay with administrators.

= 1.9.2 =
Pro can now run every AI action in your own assistant over MCP instead of the hosted AI. In that mode the site sends no page content to the hosted AI and spends none of the included generations.

= 1.9.1 =
Coverage verdicts now say only what their evidence proves, and an AI key lets the model judge coverage in the same call that builds the branches. Cached competitor searches are cleared on update, and coverage maps re-check in the background.

= 1.9.0 =
Sibling pages now come from shared Search Console queries, and covered verdicts use exact phrases and one heading per branch. Coverage maps re-check in the background. Title variations keep only the year, brand-position and clause-trim forms.

= 1.8.9 =
Headings from related-article cards and calls to action no longer count as page sections. Stored pages are cleaned and coverage maps re-check in the background; rescan pages to store up to 12,000 characters of the article's own text.

= 1.8.8 =
GA4 now reads the same 30 complete days as Search Console, so the first scan after this update compares a shifted window and week-over-week numbers move once. AI payloads now say how much of the stored evidence they carry.

= 1.8.7 =
Coverage maps now require stronger heading evidence, and Search Console keyword selection checks page relevance. Existing coverage maps refresh in the background. Stored title and heading entities are decoded during upgrade. Rescan pages to collect rendered internal links and broader query lists.

= 1.8.6 =
Sites that live in a subdirectory (a subdirectory multisite subsite, or WordPress installed under a folder such as /blog) now match each analyzed URL to its post. Before this fix the folder was added twice, so freshness details, refresh drafts, the refresh confirmation, and page history all reported no post on those sites.

= 1.8.5 =
Freshness no longer depends on WordPress revisions. The plugin now keeps its own record of meaningful content changes, so a site with revisions switched off or cleaned still gets an honest refresh date, and each page says whether that date was detected, confirmed by you, or estimated from the last edit. Mark fixed now starts recovery tracking only; use the new "I refreshed the content" control to restart the refresh clock. Records build in the background after you update.

= 1.8.4 =
A page's real headings now reach a connected assistant instead of the word "Array", and a fix plan saved that way keeps the measurement, effort and confidence fields the site's own generator stores. Coverage maps built before this release refresh themselves in the background over the minutes after you update, so their impressions, link suggestions and actions catch up with the current rules.

= 1.8.3 =
A fix plan saved by a connected assistant now gets a plain instruction as its title and a priority that counts the clicks the page is leaving on the table, not just its decay. A query with almost nothing behind it can no longer become a page's keyword, and the "link to this page instead" suggestion now needs real overlap with the page it points at.

= 1.8.2 =
A fix plan saved by a connected assistant now keeps its own action title, priority and primary keyword instead of showing the previous plan's, and an ampersand or a long query survives the save. Coverage branches saved the same way now show the search impressions behind their queries instead of zero.

= 1.8.1 =
Pro MCP fix: reading coverage inputs and saving a coverage map no longer spend a hosted competitor search on a cache miss; pass fetch: true when you want fresh SERP data. The External Services section now describes the included AI service accurately: every request is answered by Kimi K3 on the plugin author's Modal deployment, with no third-party AI gateway.

= 1.8.0 =
Pro can now connect your own assistant to this site's audit over the Model Context Protocol: Claude Code, Cursor or Claude Desktop reads the diagnosis, writes the fix plan or the draft assets, and saves them back on your subscription — no API key, no hosted generations. Nothing changes until you turn it on: the endpoint is off by default, write tools are off by default, and the free audit and diagnosis are untouched.

== Changelog ==

= 1.9.3 =
* Content Insights is now visible to every role that can edit posts (Administrators, Editors, Authors and Contributors). Subscribers still can't open it
* Non-admin roles see the table, the details modals and the CSV export. Scans, retries, AI generation, Mark fixed, refresh drafts and every other change to the site stay with administrators, and those controls are hidden for other roles
* The Edit link on each row only shows when the viewer can edit that post
* URL Management, Internal Links and Settings stay admin-only
* Developers can change the viewing capability with the `cpanalyzer_insights_capability` filter

= 1.9.2 =
* Pro: a new AI Engine choice in Settings — "Included hosted AI" (the default) or "My own assistant only (MCP)". With the assistant engine, Claude Code, Claude Desktop, Cursor, Codex or another MCP client writes fix plans, coverage maps, draft assets and refresh briefs on your own Claude or GPT subscription, and the site makes no AI request at all: scans, retries, background fill-in and every generate button stop before contacting the hosted AI
* Alt text, evidence audits, answer blocks and AI link suggestions have no MCP tool yet, so they stay off while the assistant engine is selected
* Settings warns when the assistant engine is selected but the MCP endpoint is off
* Notices, the scan progress bar and the sync status point at the assistant's fix_plan prompt instead of asking for a key or a license
* A background AI fill-in scheduled before the AI was switched off now stops instead of retrying

= 1.9.1 =
* Coverage labels match their evidence: Covered (an exact phrase, or a heading an AI verdict named), Likely covered (every meaningful word matched a heading), Mentioned in text, and No heading found. The summary reads "3 covered (1 exact), 9 mentioned, 2 not found", and the dashboard badge counts only exact matches
* A sibling page appears, and turns the advice into a link, only when it also ranks for one of the branch's queries, the query is not only brand words, and the page earns clicks on it. It is shown as "Also ranks for", never as already covering the branch
* A "What is ..." H1 can answer a definition branch
* With an AI key, the branch call also returns a verdict per branch; a verdict whose heading the page does not have is rejected and the rules verdict stands. Each branch shows whether AI or the rules judged it, and flags a covered-vs-missing disagreement as disputed. A connected assistant can send verdicts through save_coverage_map under the same check
* Competitor search never counts the site's own pages, even when called with a bare path, and cached results are filtered on read; caches from before 1.8.9 are cleared on update
* The plan-date line names the site's timezone

= 1.9.0 =
* Sibling pages come from Search Console: a page that ranks for one of a branch's own queries is its sibling, the most clicks winning. The title fallback now needs every specific label word, never matches on one word, and never picks a product page for an article branch. Each sibling says why (shared query or title) in the coverage table, the writer brief and the connected assistant
* Covered verdicts take an exact phrase first, compare words on light stems (detect/detecting, policy/policies), use each heading for one branch only, and need a real definition heading for "X definition" and "what is X" branches. Each covered branch says whether a phrase or its words matched
* Call-to-action headings inside articles ("Future-proof your workforce ...", "Get started today") no longer count as sections, on new scans and on stored pages (filter: cpanalyzer_cta_patterns)
* Title variations keep only the year, brand-position and trimmed forms; a trimmed title cuts at a colon, comma, dash or bar or not at all, and "vs.", "vs" and "v." are written "vs."
* Search Console query text is cleaned once (trailing punctuation and parentheticals dropped, the raw text kept). Primary keywords go to the most-clicked relevant query, and a brand-only query stays with the page that ranks best for it
* A page whose impressions halved at a steady position reads demand decay, not zero-click
* Cannibalization also reads each page's stored query rows, so two pages sharing a query at 50 or more impressions each, or three smaller ones, list each other

= 1.8.9 =
* The scraper reads section headings and page text from the article itself: notice bars, chat modals, tables of contents, related-article cards and calls to action are left out, and everything from a "What to read next" style heading on is dropped. Stored page text grows from 5,000 to 12,000 characters (filters: cpanalyzer_chrome_markers, cpanalyzer_tail_markers)
* Coverage maps ignore headings that are another page's title, read up to 120 headings, and are re-checked in the background after the update. Stored pages are cleaned without a rescan
* A sibling page needs the branch's words in its title or H1, not one shared word
* A question branch is covered only by a question or definition heading, and a one-word branch only by a short heading
* Keyword selection skips version-number, overlong and language-tail queries, brand-only queries on pages that do not own the brand, and queries past position 30 when closer ones exist; it prefers the whole phrase over a truncated one and needs a word from the title or H1. Coverage maps use the 90-day queries when the 30-day rows are empty
* Title variations keep acronyms and the page's own spelling (ADA, TimeClock), write "vs.", skip question forms that read wrong, and never exceed 70 characters
* Search Console keyword recovery continues across runs from where it stopped (50 pages per run by default) until every page missing queries has been tried
* A page whose impressions fell by half from 1,000 or more is no longer called low volume because its clicks were few
* The fix-plan tool schema states each text field's length limit; competitor search skips the site's own brand domains, login and error pages, and pages under 200 words; the details panel shows when the plan was written and which timezone it uses

= 1.8.8 =
* The refresh brief sends the page's top 10 Search Console queries with every column, its stored coverage map, cached competitor headings for its primary keyword, and a structured page excerpt (title, headings, intro, question answers, close) in place of the first 1,500 characters. A competitor gap claimed with no competitor data is removed
* Fix plans send 10 Search Console queries per page instead of 5
* GA4 reads the same 30 complete days as Search Console, ending at the same lag date, for views, engagement, conversions and the previous period
* Search Console keyword recovery is capped at 25 pages per scan by default (filter: cpanalyzer_gsc_keyword_recovery_limit, 0 lifts the cap)
* Every AI payload and the connected assistant's page output carry an evidence block: queries sent against queries stored, page text sent against stored, competitor pages analyzed against attempted, where search data came from, and how the freshness date is known
* Deterministic title variations (year, brand last, trimmed; the keyword-first and question forms were retired in 1.9.0) appear in the details panel, the connected assistant's page output, and Pro draft assets
* Pro draft assets now send each Search Console query's text; it was sent empty

= 1.8.7 =
* Coverage maps use word boundaries, ignore site-wide heading words for sibling suggestions, and require one heading to answer a branch before marking it covered. Existing maps refresh in the background.
* Primary keywords skip operator queries and unrelated Search Console terms. Coverage inputs label title fallbacks and do not search competitors with them.
* Coverage inputs merge up to 100 longer-window queries with sync queries; inbound links include rendered content links.
* MCP output explains why page impressions exceed listed queries and shows local dates alongside UTC dates.
* Scraped and stored titles, descriptions and headings decode HTML entities. Existing stored content is updated without a rescan.

= 1.8.6 =
* Sites in a subdirectory (a subdirectory multisite subsite, or WordPress installed under a folder) now resolve analyzed URLs to their posts: the site folder was being added twice, so every per-page feature reported no post there. Root-domain sites are unaffected

= 1.8.5 =
* Durable freshness: the plugin keeps its own per-page record of meaningful content changes, compared on every save of a tracked page, so revisions are optional evidence instead of a requirement. Cleaning revisions or deleting the history cache can no longer erase a detected refresh
* A meaningful change is measured, not guessed: formatting, block markup, punctuation, link targets, tracking parameters, a title edit or one changed year never count; several rewritten paragraphs, a new section, a material expansion, replaced statistics, or small edits that add up do. A lone revision with nothing to compare against is no longer treated as proof of a change
* Pages with no change on record get a baseline estimated from the last WordPress edit (or the publish date), frozen when the record is created, so later minor saves no longer keep pushing the refresh date back. Every place that shows freshness - the table, the details panel, the dashboard widget, the CSV export, AI fix plans and the connected assistant - says whether the date was detected, confirmed by you, or estimated
* The details panel now shows the publish date, the last WordPress edit, the last meaningful refresh (or "Unknown"), and the freshness baseline as separate facts, with revision and change-detection coverage
* New "I refreshed the content" control in the details panel records a manually confirmed refresh, shown as such and reversible. Mark fixed keeps its recovery tracking but no longer resets the refresh clock; existing Mark fixed entries keep their recovery verdicts and are not treated as content refreshes
* Freshness safeguards: paragraph splitting and source-line reflow do not count as rewrites; partial content cannot verify a whole-page refresh; the first small edit remains in cumulative comparisons; two saves of the same page landing at once can no longer leave the record describing the older version
* AI plans generated before the freshness upgrade are marked expired without deleting their content or starting automatic regeneration
* Status and the next due date now come from the same calendar: a page baselined on March 1 with a six-month cycle is due on September 1, not already due on August 30
* What to do now follows the diagnosis: a recent drop says monitor and recheck, an unconfirmed drop says find the cause first, a zero-click page is about winning back the click, demand decay says check demand before deciding, and a page that changed recently is checked before another rewrite. The same rules reach AI fix plans and the fallback text
* A plain save inside a traffic drop is no longer described as an edit; only a detected content change is. A technical diagnosis still mentions the last save, labeled as a save
* CSV export: eight columns appended (last WordPress edit, last meaningful refresh, refresh evidence, manual confirmation, freshness baseline and its source, next due date, change-detection coverage). Existing columns are unchanged
* For page builders that keep text outside the editor, the panel reports limited coverage instead of claiming a verified refresh; the cpanalyzer_freshness_content and cpanalyzer_freshness_coverage filters let a site supply that text

= 1.8.4 =
* Every heading on a page now reaches a connected assistant as the heading itself. They arrived as the word "Array", so a plan written outside the site was working from a page that appeared to have no headings at all
* A fix plan saved by a connected assistant can now carry the rest of what the site's own generator stores - the kind of work, the expected click-through and conversion change, how to measure it, a confidence and an estimate in hours - instead of dropping them on every save. The opportunity score stays the plugin's own arithmetic, whatever a plan sends
* Coverage maps stored before this release are recomputed in the background after updating, ten at a time: branch impressions, the "link to this page instead" suggestions, the covered, buried and missing verdicts, and the action each branch earns all catch up with the current rules, from data already on the site - no AI call and no competitor search

= 1.8.3 =
* A fix plan saved without an action title is now filed under an instruction written from the page's own diagnosis - "Refresh the content to recover rankings for hourly time tracking" - instead of the first sentence of the summary, which was usually the diagnosis restated and read as a quote
* A derived content priority now counts the clicks a page is leaving on the table, not only its decay: a page holding position 5 on 20,000 impressions at a 1% click-through rate comes back high priority even with nothing wrong with it, and the same number is available to a connected assistant as opportunity_clicks
* A Search Console query needs one click, or ten impressions, before it can be picked as a page's keyword automatically. On a page where every query has zero clicks, a two-impression misspelling used to win the column; now it stays blank until there is something real to put in it
* The "link to this page instead" suggestion on the coverage map now needs genuine overlap: two of the branch's words in the other page's headings and at least 60 per cent of them, a heading carrying the search word for word beats a loose match, and words every page shares - software, tool, platform, guide - no longer count as overlap at all

= 1.8.2 =
* A fix plan saved by a connected assistant can send its own action title, content priority and primary keyword, and each is stored as sent. Omit one and the site derives it from the page's own data - the title from the first sentence of the summary, the priority from the stored diagnosis, the keyword from the page's Search Console queries - instead of leaving the previous plan's label in place
* Coverage branches saved by a connected assistant now carry the Search Console impressions behind their own queries instead of zero, so the branch table and the action each branch earns read from real demand
* One text cleaner for every stored plan, whoever wrote it: "Oracle Time & Labor" keeps its ampersand instead of showing as "&amp;" on screen, and a stray backslash in front of a quote is dropped
* Suggested keywords and technical issues declare their limits and a plan that goes over is refused with a clear message, instead of being cut at 120 characters with no warning. A keyword can now be up to 240 characters, so a long conversational query is kept whole on both paths
* Search-operator searches, percent-encoded strings and bare URLs in Search Console data are no longer treated as keywords, so none of them can become a page's primary keyword or fill the Keyword column

= 1.8.1 =
* Pro MCP: collect_coverage_inputs reads People Also Ask, related searches and competitor headings from the cache only, and reports serp_source (cache, live, not_cached or none); pass fetch: true to spend one hosted search on a cache miss. save_coverage_map never searches. Previously a cache miss on either call ran a live search and spent one of the site's hosted searches, contrary to the read-tools promise
* Coverage map: when no SERP data was read, per-branch competitor counts are unknown (null) instead of reading as "0 of 0 competitors cover this"; a failed live search is reported the same way
* External Services: the included Pro AI service description now matches the relay — every request is answered by Kimi K3 on the plugin author's Modal deployment; the AI gateway path described in 1.8.0 was removed because it rejected server-to-server requests

= 1.8.0 =
* New Pro feature — Bring your own assistant (MCP): a Model Context Protocol endpoint at /wp-json/cpanalyzer/v1/mcp that lets Claude Code, Cursor or Claude Desktop work directly against this site's audit on your own AI subscription. Streamable HTTP with JSON responses, stateless, HTTPS only, and off until you enable it in Settings
* Seventeen tools: nine that read (site summary, page list, everything known about one page, the grounding payload, the coverage map and its inputs, cached competitor context, data health, scan status) and eight that write (fix plan, coverage map, draft assets, refresh draft, apply and undo title/meta, mark fixed, start scan)
* Five prompts — fix_plan, coverage_branches, draft_assets, refresh_brief and weekly_review — each carrying the same rules the plugin's own generator follows, so a client model is held to the same standard; and one resource, the site summary
* Tokens with scopes: create a token per client, read-only or read and write, shown once and never stored. Application Passwords work too. Rate limited to 120 requests a minute per token, with every call logged by tool name, scope and duration
* Writes are opt-in twice: the site setting "Allow write tools" and the token's write scope. Read tools never touch your pages, competitor context and coverage inputs read the cache unless you explicitly ask for a live search (fetch: true), saving a coverage map never searches, and no MCP call spends a hosted AI generation
* New Settings card with the endpoint URL, the token table and copy-ready snippets for each client
* On WordPress 6.9 and later the same tools also register as core Abilities under the cpanalyzer/ namespace, so the official WordPress MCP adapter and WP-CLI see them
* Internal refactors with no change in behaviour: the fix-plan rules and the draft-asset rules each moved into a single shared getter, and mark-as-fixed, apply/undo title and meta, and scan start are now callable methods behind their existing AJAX handlers

Earlier releases (1.0.0 to 1.7.3) are listed in [changelog.txt](https://plugins.svn.wordpress.org/content-performance-analyzer/trunk/changelog.txt).

== External Services ==

This plugin connects to the external services below. The full privacy policy — covering the plugin and auth.cpanalyzer.com, data handling, security, and deletion requests — is at https://cpanalyzer.com/privacy.

= 1. Content Performance Analyzer Hosted Service (auth.cpanalyzer.com) =

**What it does:** The plugin author's own service. It holds the Google OAuth client so you do not need a Google Cloud project, runs PageSpeed lookups without your own key, and provides the optional hosted AI and competitor search included with a Pro license.

**Data sent:**

* **Registration** — your site's home URL, once, to obtain an installation token
* **Google sign-in** — the WordPress admin URL to return you to, and the one-time handoff code Google issues
* **Token refresh** — your Google refresh token, forwarded to Google for a new access token
* **PageSpeed** — the URL being analyzed, mobile/desktop strategy, and your installation token
* **Hosted AI (Pro)** — your installation token and license key, plus the page URL, performance metrics, search queries, content structure, and page-text excerpts described in section 5. No site visitor personal data is sent. Nothing is sent when a Pro user selects "My own assistant only (MCP)" as the AI engine
* **Competitor search (Pro)** — your installation token and license key, plus the page's primary keyword, forwarded to Serper.dev (section 6) when no personal Serper key is saved

**What is stored:** Your Google tokens live in your own WordPress database, not on the service. A one-time sign-in handoff entry expires after five minutes. For hosted AI and competitor search, the service stores request counts only — no prompts, page content, metrics, queries, or generated output are retained.

**When:** On "Connect with Google," token refresh, keyless PageSpeed analysis, and Pro hosted AI or competitor search.

**How to avoid it entirely:** Enable the `cpanalyzer_show_byo_oauth_fields` filter and supply your own Google OAuth client, add your own PageSpeed API key, and use OpenAI, Groq, or Gemini with your own key. The plugin then talks to those providers directly and never uses this service.

**Service URL:** https://auth.cpanalyzer.com (endpoints: /auth/start, /api/v1/register, /token/exchange, /token/refresh, /api/v1/pagespeed, /api/v1/ai/chat/completions, /api/v1/serper)

**Terms:** https://cpanalyzer.com/terms — **Privacy:** https://cpanalyzer.com/privacy

= 2. Google Analytics 4 =

**What it does:** Retrieves traffic and engagement metrics for your pages, and lists your GA4 properties for the settings dropdown.

**Data sent:** Your GA4 property ID, date ranges, requested URL paths and metrics, and any custom conversion event names, authenticated with an OAuth 2.0 access token — obtained through the hosted service above by default, or directly from Google if you supply your own OAuth client.

**When:** Manual data collection, the scheduled daily refresh, the optional weekly scan, or URL uploads.

**Service URLs:** https://analyticsdata.googleapis.com/, https://analyticsadmin.googleapis.com/, https://accounts.google.com/o/oauth2/v2/auth, https://oauth2.googleapis.com/, https://www.googleapis.com/oauth2/v3/tokeninfo

**Terms:** https://marketingplatform.google.com/about/analytics/terms/ — **Privacy:** https://policies.google.com/privacy

= 3. Google Search Console =

**What it does:** Retrieves clicks, impressions, CTR, average position, and search queries for your verified site, and lists your sites for the settings dropdown.

**Data sent:** Your verified site URL, date ranges, and performance queries, with the same OAuth token as GA4.

**When:** Manual data collection, the scheduled daily refresh, the optional weekly scan, or URL uploads.

**Service URLs:** https://searchconsole.googleapis.com/, https://www.googleapis.com/webmasters/v3/sites

**Terms:** https://support.google.com/webmasters/answer/4559176 — **Privacy:** https://policies.google.com/privacy

= 4. Google PageSpeed Insights (Optional) =

**What it does:** Fetches performance scores and Core Web Vitals for specific URLs. Off by default.

**Data sent:** The URL being analyzed and the mobile/desktop strategy. By default the request rides the hosted relay (section 1), which supplies the API key; add your own key in Settings and the plugin calls Google directly instead.

**When:** Only when you enable PageSpeed analysis and trigger it.

**Service URLs:** https://www.googleapis.com/pagespeedonline/v5/runPagespeed (own key), https://auth.cpanalyzer.com/api/v1/pagespeed (default relay)

**Terms:** https://developers.google.com/terms — **Privacy:** https://policies.google.com/privacy

= 5. AI Providers — OpenAI, Groq, or Google Gemini (Optional) =

**What it does:** Generates content recommendations, answer blocks, alt text, and interlinking suggestions from your performance data and page content. You choose one provider and supply its key; without a key, no AI requests are made and the plugin runs in content-check-only mode.

**Data sent:** Page URLs, performance metrics (traffic, engagement, search queries and rankings), content structure, excerpts of your page text, and up to a few thousand characters of post text for Smart Interlinking. For AI alt text, the image itself is sent for recognition (OpenAI and Gemini only). Your API key is sent with each request. No site visitor data is ever sent.

**When:** AI analysis of flagged pages (manual, batch, or the background queue), Smart Interlinking, answer blocks, and alt text generation.

**Included AI service (Pro):** An active Pro license uses "Content Performance AI" automatically, with no key to supply. Choosing "My own assistant only (MCP)" as the AI engine in Settings stops these requests. Requests go to `https://auth.cpanalyzer.com/api/v1/ai/chat/completions`, a relay run by the plugin author, which authenticates the request, counts it against your monthly allowance, and forwards it to Kimi K3, an open-weight model the plugin author deploys and runs on Modal, a serverless compute platform, in the United States. Modal supplies servers only; no AI company receives the data. The relay never receives your Google credentials or license key, stores request counts only, and nothing sent is used by the plugin author to train any model. If a request cannot be answered, it fails and is not sent to any other provider. Modal terms: https://modal.com/legal/terms — privacy: https://modal.com/legal/privacy

**Service URLs:** https://api.openai.com/v1/chat/completions, https://api.groq.com/openai/v1/chat/completions, https://generativelanguage.googleapis.com/v1beta/openai/chat/completions, and for the included Pro service https://auth.cpanalyzer.com/api/v1/ai/chat/completions (relay) → the plugin author's Modal deployment

**Terms and privacy:** OpenAI https://openai.com/policies/terms-of-use and https://openai.com/policies/privacy-policy — Groq https://groq.com/terms/ and https://groq.com/privacy-policy/ — Gemini https://ai.google.dev/gemini-api/terms and https://policies.google.com/privacy

= 6. Serper.dev (Optional) =

**What it does:** Retrieves the top Google results for your page's primary keyword so the plugin can compare your content against ranking competitors, then fetches the public HTML of those competitor pages to extract headings and structure.

**Data sent:** The search keyword derived for your page, with your Serper API key — or through the hosted relay on Pro.

**When:** Only when a Serper key is saved, or an active Pro license uses the included service. Otherwise no Serper requests or competitor fetches are made.

**Included with Pro:** With no personal key saved, searches are relayed through `https://auth.cpanalyzer.com/api/v1/serper` using the author's Serper account — 600 searches per license per month. The relay forwards only the query, result count, country, and language, and stores request counts only. A personal key always takes precedence and bypasses the relay entirely.

**Service URL:** https://google.serper.dev/search — **Terms:** https://serper.dev/terms — **Privacy:** https://serper.dev/privacy

= 7. IndexNow (Optional) =

**What it does:** Notifies participating search engines that one of your pages was published, updated, or unpublished, so they can recrawl it without waiting. One submission to api.indexnow.org is relayed to Bing, Yandex, Seznam, Naver, and Yep. Google does not participate in IndexNow.

**Data sent:** The URL of the page that changed, your site's host name, and the IndexNow key the plugin generated for your site, together with the public address of that key's verification file. No page content, metrics, or visitor data is sent.

**When:** Only when the IndexNow setting is on, and only as a page is published, updated, or unpublished. Nothing is sent from staging or local sites, or while Settings, Reading discourages search engines. Repeat changes to the same URL are held for ten minutes so one edit session sends one notification.

**Service URL:** https://api.indexnow.org/indexnow — **About:** https://www.indexnow.org/ — **Terms:** https://www.bing.com/indexnow

= Important Notes =

* All communication uses HTTPS. No data is sent without your configuration and action, and no site visitor personal data is ever transmitted to any of these services
* Every hosted convenience has a self-hosted alternative: your own Google OAuth client, PageSpeed key, AI provider key, and Serper key
* Disable any optional service and the plugin still performs content checks

== Frequently Asked Questions ==

= Can I use my own Claude or ChatGPT subscription instead of an API key? =
Yes, with Pro. Turn on the MCP endpoint in Settings, create a token, and point your assistant at it: Claude Code, Cursor and Claude Desktop (through the mcp-remote bridge) all connect today. Your assistant reads the same audit the dashboard shows — the page list, the diagnosis and its evidence, the content analysis — and writes the fix plan, the coverage map or the draft assets on your own subscription, so it costs no hosted generations. Writing back is opt-in twice over: the site needs "Allow write tools" turned on and the token needs the write scope, or the connection stays read-only. The endpoint requires HTTPS and is off until you turn it on. claude.ai web connectors and ChatGPT need OAuth sign-in, which arrives in a later release.

= Does the coverage map show Google's real fan-out queries? =
No. Google does not publish them. The map estimates them from the real searches your page appears for in Search Console, People Also Ask questions, and the headings of the pages that currently rank, and labels every branch an estimate.

= Do I need a Google Cloud project or API keys to start? =
No. Click "Connect with Google," sign in, and pick your GA4 property and Search Console site from dropdowns. There is no Cloud Console setup, no OAuth client to create, and no PageSpeed key to obtain. You can also run the content check with no credentials at all.

= Is it really free? =
The complete audit and recommendation workflow is free, with no URL limits: Google data, decay diagnosis, SEO and conversion flags, AEO checks, question gaps, exports, recovery tracking, and page-level AI recommendations with your own OpenAI, Groq, or Gemini key. Pro is optional and adds hosted AI with no provider key, ready-to-use draft assets, and complete draft writing.

= Do I need to pay for AI? =
Not necessarily. Groq and Google Gemini offer free tiers that handle typical sites, and Groq is the recommended free provider based on output-quality testing. OpenAI is another BYOK option. Pro includes hosted AI if you do not want a provider account or key. Without any AI key or Pro license, the plugin still imports traffic data, flags decaying pages, diagnoses why they dropped, and runs every content check — you just do not get the AI-written output.

= What is content decay? =
Content decay is when a page that used to perform well gradually loses traffic and rankings, usually without anything visibly breaking. It is the most common reason older posts quietly stop earning traffic, and refreshing a decayed page is typically far cheaper than writing a new one. This plugin finds those pages and classifies why each one dropped — ranking slide, zero-click capture, falling demand, a self-inflicted edit, or a drop that was never about search at all.

= Does this work alongside Google Site Kit? =
Yes, and they do different jobs. Site Kit reports your GA4 and Search Console numbers. This plugin joins those numbers to the actual content of each page, works out which pages are losing ground and why, and writes a specific fix for each. Running both is fine — they do not conflict, and each maintains its own Google connection.

= What data leaves my site? =
Page URLs, performance metrics, content structure, and excerpts of page text go to the BYOK AI provider you configure, or to the hosted service when an active Pro user selects hosted AI. Your site URL and PageSpeed test URLs also go to the hosted service for the no-key Google connection and PageSpeed relay. No site visitor personal data is transmitted. The External Services section documents each endpoint and payload category.

= How long does analysis take for large sites? =
Most sites see AI suggestions within roughly 6 to 40 minutes for 200 URLs, depending on your AI provider's rate limits. Progress is shown live and refreshes automatically. If a run hits provider rate limits, the remaining pages fill in on their own in the background, roughly one page every couple of minutes.

= Does this plugin slow down my site? =
No. All heavy processing runs asynchronously in the background through WordPress cron, so visitors are unaffected.

= Will the analysis time out on shared hosting? =
It is built for it. Work is processed in small batches with dynamic timeout prediction and a safety buffer, and processing continues even if you close the browser. Very restrictive hosts can still interrupt a batch — if that happens, the next scheduled run picks up where it stopped.

= Can I use this on multisite? =
Yes, the plugin is multisite compatible.

= Can my editors and writers see the audit? =
Yes. Every role that can write posts (Editors, Authors, and Contributors) gets the Content Insights page in read-only mode: the table, each page's AI plan, freshness details, coverage, and the CSV export. Running scans, generating AI output, marking pages fixed, and changing settings stay with administrators. Subscribers don't see it. Developers can change the required capability with the `cpanalyzer_insights_capability` filter.

= How do I access the plugin settings after activation? =
After activation you will see "View Insights" and "Settings" links directly on the WordPress plugins page, or navigate to Content Insights in your admin menu.
