Find and refresh the Ghost posts quietly losing traffic
See every published post ranked by how much search traffic it has lost, then let an agent draft a refreshed version you approve before publishing.
Build me a content decay desk for my Ghost blog. Most of my traffic comes from posts I wrote a year ago and I have no idea which of them are quietly dying, so I want one screen: a ranked table of my published posts, worst decay first, that tells me what each post used to earn from search versus what it earns now.
Discover the account identifiers instead of 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 rather than 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 post inventory from Ghost with Browse Posts (Admin API), filtered to published posts, capturing slug, title, url, published_at, updated_at, tags and author for each one. Ghost 6.0 removed support for limit=all and caps results at 100 items per page, so request 100 per page and keep paging by reading meta.pagination.next and passing it as the next page until it comes back null. Do not assume one request covers the whole blog.
Get the search data from Google Search Console with Search Analytics Query, called twice with the page dimension: a current window of the last 90 days and a prior window covering the 90 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, compare the two windows to 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 post has slipped down the results.
Get pageviews and engagement per page path from Google Analytics with Run Report (GA4), using pagePath as the dimension and screenPageViews plus engagement metrics such as userEngagementDuration and engagementRate, over the same current window. This is the supporting signal that separates a post that lost its ranking from a post that people still find but no longer read.
Join the three sources by matching the Ghost slug 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 me a small count of rows that failed to match so I can see the coverage rather than silently losing posts.
Bake in three business rules. Exclude any post published within the last 90 days, since a new post has not stabilised and would clutter the top of the list with normal early movement. Exclude posts below a minimum impression floor in the prior window, defaulting to around 100 impressions and adjustable in the UI, so statistical noise does not outrank real problems. Rank by absolute clicks lost, descending, not by percentage, so a post that fell from 4,000 clicks to 2,000 sits above a post that fell from 20 clicks to 2.
The main table shows one row per post with title linking to the live post, clicks now versus prior with the delta, impressions now versus prior with the delta, average position and its drift, pageviews and engagement, days since the post was last updated based on updated_at, publish date, and tags. Make the decay severity visually obvious at a glance and let me sort by any column and filter by tag, author and date range.
Each row expands to show the query-level detail for that post. Call Search Analytics Query again scoped to that single page with the query dimension, for both the current and prior windows, then split the results into terms the post is holding, meaning it appears in both windows, and terms it has lost, meaning it had meaningful impressions in the prior window and has fallen away or dropped out entirely. Show clicks, impressions and average position for each term in both windows. Note in the UI that Search Console only returns rows with at least one impression, so a query disappearing means it fell below that floor rather than proving the post was deindexed.
Every row gets a Refresh this post button that starts a background agent for that post. The agent reads the full current post with Ghost Read Post (Admin API), pulls the queries that post is losing with Search Analytics Query, then writes an updated version of the article that genuinely addresses those lost queries: updating stale facts, dates and examples, strengthening or adding the sections that map to the lost terms, and tightening the introduction, while keeping my voice and the existing structure where it still works. It saves the result with Ghost Create Post (Admin API) as a draft, using ?source=html when sending HTML, carrying over the original tags, and titling it so I can tell it apart from the original.
The agent must always create a new draft and must never overwrite, edit or publish the live post. Nothing goes live from this app. Alongside the draft it writes a plain English changelog explaining what it changed and why, tied back to the specific queries the post was losing, in language I could forward to a colleague with no API or SEO jargon.
That output lands back in the app next to the original so I can compare before publishing anything: the original post and the new draft side by side, the changelog above them, and a link out to the draft in Ghost admin. 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 result so I can close the app, come back later, and still see the drafts and changelogs from earlier runs.
What does this prompt do?
- Ranks every published post on your Ghost blog by how much search traffic it has actually lost, worst first, so the quietly dying pages stop hiding behind your overall traffic number
- Shows clicks, impressions and average search position for the last 90 days beside the 90 days before that, plus how long it has been since you last touched the post
- Expands any row to reveal the exact search terms the post still ranks for and the ones it has slipped out of, so you know what to write toward
- Gives every post a Refresh button that sends an assistant to read the post, study the terms it is losing, and write a refreshed version as a new draft with a plain English list of what it changed and why
What do I need to use this?
- A Ghost blog you can log into as an admin
- A Google Search Console account where your blog is already verified
- A Google Analytics account collecting traffic for the same blog
- A blog with at least a few months of history, since the decay comparison needs a before and after period to measure
How can I customize it?
- Change the comparison window from 90 days to 28 or 180 days depending on how fast your niche moves
- Raise or lower the minimum impressions a post needs before it can appear, to keep low-traffic noise off the list
- Filter the desk down to a single tag or author so each writer only sees the posts they own
- Adjust how much editorial freedom the refresh assistant has, from light factual touch-ups to a full rewrite of the weakest sections
FAQs
What is content decay?
Will this overwrite my live blog posts?
Why are my newest posts missing from the list?
Do I need both Google Analytics and Google Search Console?
How up to date is the search data?
Why is a post ranked above another that lost a bigger percentage?
Related templates
Replace the monthly decay spreadsheet with a live board of pages losing search traffic, moving each one from spotted to refreshed and monitored.
See every client's month, numbers and report status on one board, then generate their branded Google Slides deck in a single click.
Every client site on one grid with the biggest drops first, plus a one-click monthly recap written up and posted to that client's channel.
Paste a batch of URLs or pull them from your sitemaps, then see coverage, canonicals and crawl dates for every page in one sortable table.
See every draft, scheduled and published Ghost post on a month calendar, drag posts to new dates, and park ideas until you are ready to write.
See every client's traffic, ad spend and search numbers on one screen, then let an agent write and design this month's report for you to approve.
Stop losing traffic to posts you forgot you wrote.
Put every declining post in one ranked list and refresh the ones that matter, starting with the biggest losses.