Content decay review board for your WordPress pages

By General Input

Replace the monthly decay spreadsheet with a live board of pages losing search traffic, moving each one from spotted to refreshed and monitored.

Integrations

  • Google Search Console
  • WordPress
  • Google Analytics

Type

App

Categories

  • Marketing

Build me a content decay review board for my WordPress site. Every month my content team rebuilds the same spreadsheet of pages that are losing ground in Google search, argues over which ones to rewrite, and then loses track of what happened after the rewrite. I want an app that replaces that ritual: a board of published pages grouped into lanes by refresh status, where each page carries its own numbers, an owner, notes, and a history.

Discover the account identifiers rather than asking me to paste them. Use Google Search Console List Sites and use the returned siteUrl string verbatim, because an inexact property string silently returns empty data instead of an error. Use Google Analytics List Account Summaries to find the numeric GA4 property id. If either credential can reach more than one property, let me pick from a dropdown and remember the choice.

Get the search data from Google Search Console with Search Analytics Query using the page dimension, called twice: a current window of the last 28 days and a prior window covering the 28 days immediately before it. End the current window three days before today rather than at today, because Search Console data lags two to three days and the freshest days come back artificially low. For each page produce clicks now versus clicks before, impressions now versus impressions before, the absolute and percentage change in each, and the drift in average position, where a rising position number means the page slipped down the results. Search Analytics only returns rows with at least one impression, so a page that appears in the prior window and is absent from the current one has fallen out of the data entirely rather than sitting at a clean zero. Treat that as a total loss, mark it on the row as dropped out of the data, and do not let it silently disappear from the board.

Get the CMS side from WordPress with List Posts filtered to published posts, capturing id, title, link, slug, publish date, last modified date, author and categories for each one. Page through the whole archive rather than assuming one request covers it: request the maximum per page and keep going while the page number is under the total page count returned in the response headers. Remember that WordPress returns title, content and excerpt as objects with a rendered property, not as plain strings. Use Retrieve a Post when you need the full body of a single post for the drawer or for the brief agent.

Get engagement from Google Analytics with Run Report (GA4) using the landing page dimension over the same current window, pulling sessions and the conversion metric the property exposes, which is key events on newer properties and conversions on older ones. This is the supporting signal that separates a page that lost its ranking from one people still land on but never act on.

Join the three sources by matching the WordPress post URL to the page URL that Search Console and Google Analytics report. Normalise both sides before comparing: strip the protocol and domain, drop query strings and fragments, and treat trailing slashes as equivalent. Show a small count of rows that failed to match so I can see the coverage instead of silently losing pages.

Bake in the decay rule that actually matters. Flag a page for refresh when clicks fell meaningfully, default around 20 percent or more, while impressions held flat or rose, default meaning impressions are down no more than 5 percent. That combination says demand for the topic is still there and the ranking is what slipped, which is the case a rewrite can fix. Pages where clicks and impressions both fell still belong on the board, but label them as a demand decline rather than a ranking decline so nobody wastes a rewrite on them. Exclude any page published in the last 90 days, since it is still ramping and has not settled into a stable ranking. Exclude pages under a minimum impressions floor in the prior window, defaulting to around 100 impressions and adjustable in the UI, so noise does not outrank real problems. Make all three thresholds editable from the board.

The main surface is the board itself, with four lanes: Watching, Needs refresh, In progress, and Refreshed and monitoring. Every qualifying page starts in Watching, and anything matching the decay rule gets a clear flag proposing a move to Needs refresh, which a person confirms by dragging the row across. Each row shows the page URL and title, clicks for the last 28 days versus the prior 28 with the delta, the impressions trend, the average position drift, the matched WordPress post with its last modified date and how long ago that was, the assigned owner, and a short note. Refresh status, owner, and notes are persisted in the app and must survive a data refresh, so moving a row and coming back tomorrow shows the same lane, the same owner, and the same note against freshly pulled numbers. Let me filter the board to a single owner, sort each lane by clicks lost, and see a count per lane.

Clicking a row opens a drawer for that page. Call Search Analytics Query again scoped to that single page using the query dimension with a page filter, for both the current and prior windows, then split the results into queries the page is holding, meaning it appears in both windows, and queries it has slipped on, meaning it had meaningful impressions before and has fallen away or dropped out. Show clicks, impressions and average position for each query in both windows, and note in the drawer that a query disappearing means it fell below the one impression floor rather than proving the page was deindexed. Alongside the query detail, show the page's Google Analytics sessions and conversions, the matched WordPress post with a link to edit it, the owner and notes fields, the current refresh brief if one exists, and the history of lane changes with dates.

Every row gets a Draft refresh brief button that starts a background agent for that page. The agent re-reads that page's query mix from Search Console with Search Analytics Query across both windows, retrieves the current post body from WordPress with Retrieve a Post, and looks over the rest of the published archive with List Posts to find related pages worth linking to. It works out which topics the page has lost ground on by comparing the query sets, then writes a refresh brief: what the page used to win on and no longer does, the sections to add or expand and what each should cover, headings and questions to answer, stale facts and dates to update, internal links to add with suggested anchor text and the target post, and what to leave alone because it still works. The brief has to be readable by a writer with no SEO background, so plain language and no jargon.

The brief lands back on the app row so the board is where the work lives. Show the agent run state on the row as queued, running, done or failed with the error reason, and persist both the run state and the finished brief so I can close the app, come back later, and still read briefs from earlier runs. Once a brief exists, offer an optional follow-up button that saves it into WordPress as a draft post using Create a Post with draft status, titled so it is obviously the refresh brief for that page and carrying the original post's categories. The agent must never edit, overwrite or publish the live page. Nothing goes live from this app.

The Refreshed and monitoring lane is what makes the loop close, so treat it properly. When a row moves into that lane, stamp the refresh date and snapshot the clicks, impressions and average position at that moment as the baseline. From then on the row shows check-ins at 30, 60 and 90 days after the refresh, each comparing the current 28 day window against that stored baseline, badged as recovered, flat, or still falling. A page still falling at the 90 day check-in should prompt a move back into Needs refresh rather than quietly sitting there. Persist the baseline with the row so the comparison stays honest even as the underlying data window rolls forward.

Be upfront in the UI about the limits of the data. Show the date the search data is current to and state that Google Search Console lags two to three days, that its history only goes back about 16 months so older comparisons are unavailable, and that it only reports rows with at least one impression so a missing page or query means it fell below that floor rather than proving anything about indexing. Put these where the numbers are, not buried in a settings page, because the team will be making rewrite decisions off them in a monthly review meeting.

Related prompts

Explore more prompts
Shoppable link console for your WordPress recipe archiveAgency reporting board that builds each client's Slides deckOne console for every SEO client's search performanceCheck in bulk which of your pages Google has actually indexedBuild every client's monthly marketing report in one clickFind and refresh the Ghost posts quietly losing trafficGravity Forms conversion dashboard with dead form alertsMerchandising board for products people view but never buyMulti-site GA4 dashboard with one-click client reportsWhich marketing channels actually produce pipeline, not traffic