Systems Library / Marketing Automation / How to Create an AI-Powered Content Refresh System
Marketing Automation content marketing

How to Create an AI-Powered Content Refresh System

Identify and update outdated content automatically to maintain rankings.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

Traffic from old content decays. Google rewards freshness, especially for topics where the landscape changes. This ai content refresh update outdated articles system identifies which of your articles are losing rankings, pulls fresh information on the topic, and generates a specific update plan for each one. You decide what to act on. The system just removes the guesswork about where to start.

Refreshing existing content is almost always higher ROI than creating new content. You already have the domain authority and the backlinks. You just need the article to reflect what is true today.

What You Need Before Starting

Step 1: Identify Decaying Content

Decaying content shows a consistent traffic drop month over month. Pull this from GA4:

import os
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import RunReportRequest, DateRange, Dimension, Metric
from google.oauth2 import service_account
from dotenv import load_dotenv

load_dotenv()

def get_ga4_client():
    credentials = service_account.Credentials.from_service_account_file(
        os.getenv("GOOGLE_CREDENTIALS_PATH"),
        scopes=["https://www.googleapis.com/auth/analytics.readonly"]
    )
    return BetaAnalyticsDataClient(credentials=credentials)

def get_page_traffic_comparison(ga_client, property_id: str) -> list:
    """Compare last 30 days vs prior 30 days for content pages."""
    
    def fetch_period(start, end):
        request = RunReportRequest(
            property=f"properties/{property_id}",
            date_ranges=[DateRange(start_date=start, end_date=end)],
            dimensions=[Dimension(name="pagePath"), Dimension(name="pageTitle")],
            metrics=[Metric(name="screenPageViews"), Metric(name="newUsers")],
            limit=100
        )
        response = ga_client.run_report(request)
        
        pages = {}
        for row in response.rows:
            path = row.dimension_values[0].value
            if not any(p in path for p in ["/blog/", "/articles/", "/systems/", "/guides/"]):
                continue
            pages[path] = {
                "title": row.dimension_values[1].value,
                "views": int(row.metric_values[0].value),
                "new_users": int(row.metric_values[1].value)
            }
        return pages
    
    recent = fetch_period("30daysAgo", "today")
    prior = fetch_period("60daysAgo", "31daysAgo")
    
    results = []
    for path, data in recent.items():
        prior_views = prior.get(path, {}).get("views", 0)
        current_views = data["views"]
        
        if prior_views == 0:
            continue
        
        change_pct = ((current_views - prior_views) / prior_views) * 100
        
        results.append({
            "path": path,
            "title": data["title"],
            "current_views": current_views,
            "prior_views": prior_views,
            "change_pct": round(change_pct, 1),
            "status": "decaying" if change_pct < -15 else "stable" if abs(change_pct) <= 15 else "growing"
        })
    
    return sorted(results, key=lambda x: x["change_pct"])

Step 2: Check Current SERP Position

You want to know if the traffic drop is paired with a ranking drop:

import requests

def check_serp_position(keyword: str) -> dict:
    url = "https://serpapi.com/search"
    params = {
        "q": keyword,
        "api_key": os.getenv("SERPAPI_KEY"),
        "num": 20,
        "hl": "en",
        "gl": "us"
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    your_domain = os.getenv("YOUR_DOMAIN", "yoursite.com")
    
    for i, result in enumerate(data.get("organic_results", []), 1):
        if your_domain in result.get("link", ""):
            return {
                "keyword": keyword,
                "position": i,
                "url": result["link"],
                "title": result.get("title", "")
            }
    
    return {"keyword": keyword, "position": None, "url": "", "title": ""}

Step 3: Generate the Refresh Plan

import anthropic
import json

client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))

def generate_refresh_plan(
    article_title: str,
    article_url: str,
    article_content: str,
    current_serp_position: int,
    traffic_change_pct: float
) -> dict:
    
    prompt = f"""You are an SEO content strategist. Generate a specific refresh plan for this article.

ARTICLE: {article_title}
URL: {article_url}
CURRENT RANKING POSITION: {current_serp_position or "Not ranking in top 20"}
TRAFFIC CHANGE (30-day): {traffic_change_pct:+.1f}%

CURRENT ARTICLE CONTENT (first 2000 words):
---
{article_content[:2000]}
---

Analyze why this article may be losing traffic and create a specific refresh plan.

Return as JSON:
{{
  "diagnosis": "One paragraph on what is likely causing the traffic drop",
  "urgency": "high/medium/low",
  "refresh_type": "minor_update/major_rewrite/structural_overhaul",
  "estimated_effort_hours": 0,
  "specific_updates": [
    {{
      "type": "add/update/remove/restructure",
      "section": "which section or element",
      "instruction": "specific instruction for the writer",
      "priority": "must/should/nice-to-have"
    }}
  ],
  "new_sections_to_add": [],
  "outdated_sections_to_remove": [],
  "keyword_opportunities": [],
  "predicted_outcome": "What you expect to happen after the refresh"
}}"""

    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=2000,
        messages=[{"role": "user", "content": prompt}]
    )
    
    raw = message.content[0].text.strip()
    if raw.startswith("```"):
        raw = raw.split("```")[1]
        if raw.startswith("json"):
            raw = raw[4:]
    
    return json.loads(raw)

Step 4: Generate the Refreshed Content

Once you have a plan, execute it:

def refresh_article(original_content: str, refresh_plan: dict) -> str:
    updates_str = json.dumps(refresh_plan["specific_updates"], indent=2)
    new_sections_str = "\n".join(f"- {s}" for s in refresh_plan.get("new_sections_to_add", []))
    remove_str = "\n".join(f"- {s}" for s in refresh_plan.get("outdated_sections_to_remove", []))
    
    prompt = f"""Update this article according to the refresh plan below.

REFRESH TYPE: {refresh_plan['refresh_type']}
DIAGNOSIS: {refresh_plan['diagnosis']}

SPECIFIC UPDATES TO MAKE:
{updates_str}

NEW SECTIONS TO ADD:
{new_sections_str or "None"}

SECTIONS TO REMOVE OR TRIM:
{remove_str or "None"}

ORIGINAL ARTICLE:
---
{original_content}
---

Produce the complete refreshed article. Keep what is working. Fix what is not.
Maintain the original author's voice. Do not add fluff to increase word count.
If you add new information, make sure it is accurate and specific."""

    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=4000,
        messages=[{"role": "user", "content": prompt}]
    )
    
    return message.content[0].text

if __name__ == "__main__":
    ga_client = get_ga4_client()
    property_id = os.getenv("GA4_PROPERTY_ID")
    
    traffic_data = get_page_traffic_comparison(ga_client, property_id)
    decaying = [p for p in traffic_data if p["status"] == "decaying"][:10]
    
    print(f"Found {len(decaying)} decaying articles\n")
    
    for page in decaying[:3]:
        print(f"Planning refresh for: {page['title']} ({page['change_pct']:+.1f}%)")
        
        with open(f"content/{page['path'].split('/')[-1]}.md", "r") as f:
            content = f.read()
        
        plan = generate_refresh_plan(
            article_title=page["title"],
            article_url=page["path"],
            article_content=content,
            current_serp_position=None,
            traffic_change_pct=page["change_pct"]
        )
        
        with open(f"refresh-plans/{page['path'].split('/')[-1]}-plan.json", "w") as f:
            json.dump(plan, f, indent=2)
        
        print(f"  Urgency: {plan['urgency']}, Type: {plan['refresh_type']}")

What to Build Next

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems