Library · Marketing, sales and analytics with AI

The SEO machine: the full stack with Claude Code

Builder65 minUpdated: October 2026
68 of 105 in the library

Time: ~25 min theory + 40 min practice


The gist

SEO in 2026 isn't just about "getting into Google." Some searches are now answered by AI: a user asks ChatGPT about a real estate agency in Cuenca and leaves with an answer without visiting a single website. The new game is showing up both in Google and in AI answers. Claude Code lets you automate almost the entire stack, from keyword research to a weekly audit, for a modest token bill.

🎨 Picture this: an SEO machine is like an orchard that bears fruit for years. Plant an article (a few tokens and about 30 minutes of your time), and it can bring in visitors for years, as long as you keep it updated. The more you plant and the better you tend it, the bigger the harvest.


Key concepts

  • Google AI Overviews (formerly called SGE): AI blocks in Google's results; on some informational searches they take clicks away from the regular results
  • AEO (Answer Engine Optimization): optimizing for AI answers (ChatGPT, Claude, Gemini, Perplexity)
  • Keyword Gap: keywords a competitor ranks for but you don't
  • Schema Markup: machine-readable markup that helps AI understand what a page is about
  • Core Web Vitals: page speed and stability, a Google ranking factor
  • searchfit-seo:*: a plugin with SEO skills for Claude Code (installed through /plugin; it includes a set of SEO skills and commands)

Theory

SEO in 2026: what AI has changed

Before 2024 there was one goal: get onto the first page of Google. In 2026 the picture is more complicated.

🎨 Picture this: SEO used to be about getting into the shop window. A shopper walked down the street, came in, looked around. Now shoppers have a personal consultant (AI) who visits every shop window on their own, makes a shortlist and brings the shopper a ready-made answer. Your job is to get that consultant to recommend you.

What changed:

Before (until 2024) Now (2026)
Goal: positions 1-3 in Google Goal: a position in Google + mentions in AI answers
Traffic = clicks from the search engine Traffic = clicks from the search engine + brand searches after AI
Keywords → articles Questions and answers → source authority
Backlinks = ranking Backlinks + mentions in authoritative sources
Technical SEO Technical SEO + structured data

Google AI Overviews (formerly SGE) appear above the regular results on some informational searches; how often depends on the topic, region and language. If you're not among the sources in an AI Overview, you get less traffic from those searches.

ChatGPT, Claude and Gemini tend to rely on sources that:

  • Are often cited in authoritative material
  • Have a clear question-and-answer structure
  • Use Schema Markup (Article and other types)
  • Have good technical SEO

None of the AI services officially disclose what role markup plays for them. Treat it as good hygiene, not a lever with a guaranteed effect.

AEO vs. SEO: they don't contradict each other, they complement each other. A good SEO article = a good AEO article, if the structure is right.


The key tools in the stack

The searchfit-seo plugin adds ready-made SEO skills to Claude Code. It's installed through /plugin from the marketplace; the list below is as of October 2026:

bash
# searchfit-seo plugin skills (installed through /plugin from the marketplace):
# searchfit-seo:seo-audit          — a full site audit
# searchfit-seo:keyword-clustering  — grouping keywords
# searchfit-seo:content-brief       — a brief for an article
# searchfit-seo:on-page-seo         — optimizing a page
# searchfit-seo:schema-markup       — markup for AI and Google
# searchfit-seo:ai-visibility       — checking visibility in AI answers
# searchfit-seo:content-strategy   — content strategy
# searchfit-seo:internal-linking    — internal linking strategy
# searchfit-seo:technical-seo       — a technical audit

# External tools (through APIs):
# Google Search Console API — positions, clicks, impressions (free)
# Ahrefs API                — competitor keywords (paid, prices on their site)
# Google PageSpeed API      — Core Web Vitals (free)

To get started, Google Search Console (free) + the searchfit-seo skills are enough. Add Ahrefs when you scale up.


Automated keyword research

We find where the opportunity is: keywords with traffic and commercial intent.

python
import anthropic
import os
import json

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

def find_keyword_opportunities(topic: str, our_domain: str) -> dict:
    """
    Analyzes keyword opportunities for a given topic.
    Without the Ahrefs API, it uses only Claude to generate hypotheses.
    With the Ahrefs API, you add real search volumes (see the comment below).
    """
    
    response = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=3000,
        messages=[{
            "role": "user",
            "content": f"""Do keyword research for the topic: "{topic}"
            Domain: {our_domain}
            
            Task: find 30 keywords with commercial potential.
            
            Break them into categories:
            
            1. TRANSACTIONAL (people want to buy/order):
               Examples: "buy apartment Cuenca", "real estate agency Ecuador"
               
            2. INFORMATIONAL (people are looking for information):
               Examples: "how can a foreigner buy property in Ecuador", "living in Cuenca reviews"
               
            3. NAVIGATIONAL (people are looking for a specific brand/site):
               Examples: "Acme Realty real estate", "Cuenca agency for Americans"
               
            4. LONG-TAIL (lower competition):
               Examples: "how a US retiree can buy an apartment in Cuenca", "Cuenca neighborhoods where Americans live"
            
            For each keyword, estimate:
            - Estimated search volume (low/medium/high)
            - Competition level (low/medium/high)
            - Commercial potential (1-5)
            - Type of content to create (article/FAQ/guide/comparison)
            
            Response format: a JSON list of objects:
            {{
              "keyword": "...",
              "category": "transactional/informational/navigational/long-tail",
              "estimated_volume": "low/medium/high",
              "competition": "low/medium/high",
              "commercial_potential": 1-5,
              "content_type": "...",
              "priority": "high/medium/low"
            }}
            
            Return only a valid JSON array."""
        }]
    ).content if b.type == "text")
    
    keywords = json.loads(response)
    
    # Filter the priority opportunities
    high_priority = [k for k in keywords if k["priority"] == "high"]
    
    print(f"Total keywords: {len(keywords)}")
    print(f"High priority: {len(high_priority)}")
    print("\nTop 10 by priority:")
    for i, kw in enumerate(high_priority[:10], 1):
        print(f"  {i}. {kw['keyword']} | potential: {kw['commercial_potential']}/5 | competition: {kw['competition']}")
    
    return {"all": keywords, "high_priority": high_priority}


def find_keyword_gap(our_keywords: list, competitor_keywords: list) -> list:
    """
    Finds keywords the competitor has but we don't.
    our_keywords / competitor_keywords are lists of strings (keywords).
    """
    
    response = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=2000,
        messages=[{
            "role": "user",
            "content": f"""Do a keyword gap analysis.
            
            Our keywords: {json.dumps(our_keywords[:50], ensure_ascii=False)}
            Competitor keywords: {json.dumps(competitor_keywords[:50], ensure_ascii=False)}
            
            Find:
            1. The top 20 competitor keywords we don't have, prioritized by commercial intent
            2. Quick wins: our keywords in positions 11-30 (could move into the top 10 in 2-4 weeks)
            3. Shared keywords: where we compete head to head; compare potential positions
            
            Format: JSON with three arrays:
            {{
              "gap_keywords": [{{"keyword": "...", "why_valuable": "...", "urgency": "high/medium"}}],
              "quick_wins": [{{"keyword": "...", "current_position_estimate": "11-30", "action": "..."}}],
              "competitive": [{{"keyword": "...", "our_advantage": "...", "their_advantage": "..."}}]
            }}"""
        }]
    ).content if b.type == "text")
    
    return json.loads(response)


# Example usage
if __name__ == "__main__":
    # Basic keyword research without an API
    opportunities = find_keyword_opportunities(
        topic="real estate in Ecuador for Americans",
        our_domain="acme-realty.example"
    )
    
    # Save it for later
    with open("keyword-research.json", "w", encoding="utf-8") as f:
        json.dump(opportunities, f, ensure_ascii=False, indent=2)
    
    print("\nResults saved to keyword-research.json")

A content brief in 5 minutes

Instead of writing briefs by hand, Claude does it automatically.

Use the searchfit-seo:content-brief skill directly from Claude Code:

Type this into the chat
Claude, use the searchfit-seo:content-brief skill.

Topic: "buy an apartment in Cuenca Ecuador"
Our audience: American investors and expats aged 35-55
Competitors to analyze: [URL of competitor article 1, URL of competitor article 2]

Give me a full content brief:
- The main keyword and LSI keywords
- An H1 headline (clickable, with the keyword)
- Article structure (H2s, H3s with a description of what each section covers)
- What competitors have and what's missing (our competitive advantage)
- Recommended length (in words)
- Schema markup type (Article / FAQ / HowTo / LocalBusiness)
- A CTA at the end of the article

Or through Python, to automate it as part of a pipeline:

python
def generate_content_brief(keyword: str, audience: str, competitor_urls: list) -> str:
    """Generates a content brief for a specific keyword"""
    
    response = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=2500,
        messages=[{
            "role": "user",
            "content": f"""Create a content brief for an SEO article.
            
            Target keyword: "{keyword}"
            Audience: {audience}
            Competitors: {', '.join(competitor_urls) if competitor_urls else 'not specified'}
            
            The brief should include:
            
            ## Keywords
            - Main: {keyword}
            - LSI (semantically related): [a list of 10-15 terms]
            - Long-tail variations: [a list of 5-7 phrases]
            
            ## H1 headline
            [A clickable headline with the main keyword, 50-60 characters]
            
            ## Meta Description
            [155 characters, includes the keyword and a call to action]
            
            ## Article structure
            [H2 section 1 — title]
            - What to include: ...
            - Approximate length: ... words
            
            [H2 section 2 — title]
            ...
            
            ## FAQ section
            [5-7 questions people actually ask about this topic]
            
            ## Recommendations
            - Length: ... words
            - Schema markup: ...
            - Content type: article/guide/comparison/FAQ
            - Unique angle (what sets us apart from competitors): ...
            
            Write in English."""
        }]
    ).content if b.type == "text")
    
    return response


# Generate briefs for the top 5 priority keywords
priority_keywords = [
    "buy an apartment in Cuenca",
    "living in Ecuador as an American",
    "Ecuador real estate investment",
    "moving to Cuenca from the US",
    "cost of living in Cuenca 2026",
]

for kw in priority_keywords:
    brief = generate_content_brief(
        keyword=kw,
        audience="Americans aged 35-55 considering a move or an investment",
        competitor_urls=[]
    )
    
    # Save each brief to its own file
    filename = kw.replace(" ", "-").replace("/", "-") + ".md"
    with open(f"briefs/{filename}", "w", encoding="utf-8") as f:
        f.write(f"# Content brief: {kw}\n\n")
        f.write(brief)
    
    print(f"Brief created: briefs/{filename}")

Automatic schema markup

Schema.org markup helps search engines understand the structure of your content. It doesn't guarantee you'll show up in rich results or in AI Overviews.

python
def add_schema_markup(page_content: str, page_type: str, business_info: dict) -> str:
    """
    Generates JSON-LD schema markup for a page.
    
    page_type: "article" / "faq" / "local_business" / "howto" / "product"
    business_info: a dictionary with business details (name, address, phone, etc.)
    """
    
    schema_prompt = f"""
    Create JSON-LD schema.org markup for a page.
    
    Page type: {page_type}
    Business details: {json.dumps(business_info, ensure_ascii=False)}
    
    Page content (first 1500 characters):
    {page_content[:1500]}
    
    Requirements:
    1. Use the most suitable schema.org type
    2. Fill in all required properties
    3. Add recommended properties where possible
    4. For FAQ pages: add FAQPage schema with real questions from the content
    5. For articles: add Article schema with the author and publication date
    6. For a local business: add LocalBusiness + GeoCoordinates
    7. For instructions: add HowTo schema with step-by-step steps
    
    Return only a valid JSON-LD object (no markdown code blocks).
    """
    
    schema_json = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=2000,
        messages=[{"role": "user", "content": schema_prompt}]
    ).content if b.type == "text").strip()
    
    # Wrap it in a tag for inserting into HTML
    html_tag = f'<script type="application/ld+json">\n{schema_json}\n</script>'
    
    return html_tag


# Example: schema for a real estate agency
business_info = {
    "name": "Acme Realty",
    "url": "https://acme-realty.example",
    "telephone": "+593-XX-XXX-XXXX",
    "address": {
        "city": "Cuenca",
        "country": "Ecuador"
    },
    "description": "A real estate agency in Cuenca for English-speaking buyers",
    "languages": ["en", "es"],
    "priceRange": "$$"
}

faq_page_content = """
How much does an apartment in Cuenca cost? [an answer with real figures from the agency]...
How can a foreigner buy property in Ecuador? [an answer reviewed by a lawyer]...
Do you need a visa to buy property? [an answer reviewed by a lawyer]...
"""

schema_html = add_schema_markup(
    page_content=faq_page_content,
    page_type="faq",
    business_info=business_info
)

print("Schema markup created:")
print(schema_html[:500] + "...")

What changed with rich results. Google removed HowTo rich results in 2023, and FAQ rich results stopped appearing as of May 7, 2026 (according to the Google Search Central documentation). The FAQPage and HowTo markup itself is still valid: it describes the page structure for machines, but don't expect "expandable questions" in the search results because of it. Check your markup for validity, and don't build traffic expectations on it.


AI visibility: showing up in AI answers

AEO is a young discipline. The goal: when a user asks ChatGPT or Claude about your topic, you're in the answer.

Use the searchfit-seo:ai-visibility skill:

Type this into the chat
Claude, use the searchfit-seo:ai-visibility skill.

Our site: acme-realty.example
Topic: real estate in Ecuador for Americans

Check our visibility in AI answers for these queries:
- "How can a US citizen buy property in Ecuador"
- "English-speaking real estate agency in Cuenca"
- "Living in Ecuador as an American: where to live"
- "Is it safe to buy property in Ecuador"

Give me:
1. The current level of AI visibility (do we show up in answers?)
2. What's keeping AI from recommending us
3. Specific changes to improve AI visibility:
   - Which pages to add or rework
   - What markup to add
   - Which content format AI engines prefer
4. Priority of actions (what to do first)

Or use code for regular monitoring. This is a simplified check: Claude answers from memory, without searching the web, so the script doesn't reproduce the real answers of ChatGPT, Gemini, Perplexity or Google AI Mode. For a real measurement, open those services yourself or use an AI-visibility monitoring tool (for example, Peec AI or OtterlyAI; check their sites for prices and terms).

python
def check_ai_visibility(brand_name: str, topics: list, test_queries: list) -> dict:
    """
    Checks how much a brand is mentioned in AI answers.
    Simulates AI answers to the given queries and looks for mentions of the brand.
    """
    
    results = {}
    
    for query in test_queries:
        # Simulate an AI answer to the query (no real request to ChatGPT)
        response = "".join(b.text for b in client.messages.create(
            model="claude-sonnet-5-5",
            max_tokens=1000,
            system=f"""You are a search AI assistant that answers users' questions.
                       Answer based on information that actually exists on the internet.
                       If you recommend specific resources or companies, explain why.""",
            messages=[{
                "role": "user",
                "content": query
            }]
        ).content if b.type == "text")
        
        # Check whether our brand is mentioned
        brand_mentioned = brand_name.lower() in response.lower()
        
        # Analyze what's being recommended
        analysis = "".join(b.text for b in client.messages.create(
            model="claude-haiku-4-5",  # a light model for simple tagging of the answer; current model names: the What's current page
            max_tokens=300,
            messages=[{
                "role": "user",
                "content": f"""User query: "{query}"
                
                AI answer: {response[:800]}
                
                Our brand: {brand_name}
                
                Answer:
                1. Is our brand mentioned? (yes/no)
                2. Who/what is recommended? (list them)
                3. Why are these sources mentioned? (one sentence)
                4. What do we need to do to get into an answer like this? (one sentence)
                
                Format: JSON {{
                    "mentioned": true/false,
                    "recommended": ["...", "..."],
                    "why_they_rank": "...",
                    "our_action": "..."
                }}"""
            }]
        ).content if b.type == "text")
        
        results[query] = {
            "brand_mentioned": brand_mentioned,
            "analysis": json.loads(analysis)
        }
    
    # Overall summary
    mention_rate = sum(1 for r in results.values() if r["brand_mentioned"]) / len(results)
    
    print(f"\nAI Visibility Report for {brand_name}:")
    print(f"Mention rate in AI answers: {mention_rate:.0%} ({sum(1 for r in results.values() if r['brand_mentioned'])}/{len(results)} queries)")
    print("\nPer query:")
    for query, data in results.items():
        status = "MENTIONED" if data["brand_mentioned"] else "not mentioned"
        print(f"  [{status}] {query[:60]}...")
    
    return results


# Example usage
visibility = check_ai_visibility(
    brand_name="Acme Realty",
    topics=["Ecuador real estate", "Cuenca housing"],
    test_queries=[
        "How can a foreigner buy an apartment in Cuenca?",
        "Real estate agency in Cuenca for Americans",
        "Is it safe to buy property in Ecuador in 2026",
    ]
)

An automatic SEO audit every week

Instead of a manual check once a month, an automatic audit every Monday.

python
#!/usr/bin/env python3
# weekly-seo-audit.py — run with cron every Monday

import anthropic
import requests
import json
import os
from datetime import datetime, timedelta

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

# Google Search Console API (requires OAuth 2.0 setup)
GSC_PROPERTY = os.environ.get("GSC_PROPERTY", "https://acme-realty.example/")


def get_gsc_data(days_back: int = 28) -> dict:
    """
    Pulls data from Google Search Console through the API.
    Requires: google-auth-oauthlib, google-api-python-client
    pip install google-auth-oauthlib google-api-python-client
    
    If GSC isn't set up, returns placeholder data for the demo.
    """
    try:
        from google.oauth2.credentials import Credentials
        from googleapiclient.discovery import build
        
        creds = Credentials.from_authorized_user_file(
            os.environ.get("GSC_CREDENTIALS_FILE", "gsc-credentials.json")
        )
        service = build("searchconsole", "v1", credentials=creds)
        
        end_date = datetime.now().strftime("%Y-%m-%d")
        start_date = (datetime.now() - timedelta(days=days_back)).strftime("%Y-%m-%d")
        
        request = {
            "startDate": start_date,
            "endDate": end_date,
            "dimensions": ["query", "page"],
            "rowLimit": 100,
            # the API returns rows sorted by clicks, descending, so no separate sort is needed
        }
        
        response = service.searchanalytics().query(
            siteUrl=GSC_PROPERTY, body=request
        ).execute()
        
        return response.get("rows", [])
    
    except Exception as e:
        # Demo data if GSC isn't set up
        print(f"GSC isn't set up ({e}). Using demo data.")
        return [
            {"keys": ["buy apartment cuenca", "acme-realty.example/"], "clicks": 45, "impressions": 890, "ctr": 0.051, "position": 8.2},
            {"keys": ["ecuador real estate americans", "acme-realty.example/blog/"], "clicks": 23, "impressions": 560, "ctr": 0.041, "position": 12.5},
            {"keys": ["living in cuenca reviews", "acme-realty.example/blog/life/"], "clicks": 18, "impressions": 340, "ctr": 0.053, "position": 6.1},
        ]


def check_page_speed(url: str) -> dict:
    """Checks Core Web Vitals through the Google PageSpeed Insights API (free)"""
    api_key = os.environ.get("PAGESPEED_API_KEY", "")
    
    params = {
        "url": url,
        "strategy": "mobile",  # mobile matters more to Google
    }
    if api_key:
        params["key"] = api_key
    
    try:
        response = requests.get(
            "https://www.googleapis.com/pagespeedonline/v5/runPagespeed",
            params=params,
            timeout=30
        )
        data = response.json()
        
        # Pull out the main metrics
        metrics = data.get("lighthouseResult", {}).get("audits", {})
        return {
            "lcp": metrics.get("largest-contentful-paint", {}).get("displayValue", "N/A"),
            "cls": metrics.get("cumulative-layout-shift", {}).get("displayValue", "N/A"),
            "tbt": metrics.get("total-blocking-time", {}).get("displayValue", "N/A"),  # the lab stand-in for INP
            "score": data.get("lighthouseResult", {}).get("categories", {}).get("performance", {}).get("score", 0) * 100,
        }
    except Exception as e:
        return {"error": str(e)}


def run_weekly_audit(site_url: str, report_dir: str = "reports/seo") -> str:
    """Runs the full weekly SEO audit"""
    
    today = datetime.now().strftime("%Y-%m-%d")
    os.makedirs(report_dir, exist_ok=True)
    
    print(f"Running the SEO audit for {site_url} ({today})")
    
    # 1. Data from GSC
    print("  Pulling Google Search Console data...")
    gsc_data = get_gsc_data(days_back=28)
    
    # 2. Core Web Vitals
    print("  Checking Core Web Vitals...")
    speed_data = check_page_speed(site_url)
    
    # 3. Claude analyzes everything and makes recommendations
    print("  Analyzing with Claude...")
    
    analysis = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=3000,
        messages=[{
            "role": "user",
            "content": f"""Analyze the SEO data and give recommendations.

Site: {site_url}
Date: {today}

Google Search Console (top queries over 28 days):
{json.dumps(gsc_data[:20], ensure_ascii=False, indent=2)}

Core Web Vitals:
{json.dumps(speed_data, ensure_ascii=False, indent=2)}

Do the analysis:

## 1. Quick wins (doable in 1-2 weeks)
Queries in positions 11-30 with a chance of moving into the top 10.
For each: what exactly to do (rewrite the title? add a section? improve internal links?)

## 2. Ranking drops
If you see a high CTR at a low position, the page is relevant but isn't ranking.
What should we do?

## 3. Technical
Core Web Vitals: are there any critical speed problems?
What should be fixed first?

## 4. Content opportunities
Which topics are worth covering, judging by the GSC data?
Which queries get lots of impressions but few clicks (a title or meta problem)?

## 5. Priority plan for the week
The top 5 specific tasks with a time estimate (hours).

Write in English. Be specific, no fluff."""
        }]
    ).content if b.type == "text")
    
    # 4. Save the report
    report_path = f"{report_dir}/seo-{today}.md"
    with open(report_path, "w", encoding="utf-8") as f:
        f.write(f"# SEO audit {today}\n\n")
        f.write(f"**Site:** {site_url}\n\n")
        f.write(f"**Core Web Vitals (mobile):** LCP {speed_data.get('lcp', 'N/A')} | Score {speed_data.get('score', 'N/A')}/100\n\n")
        f.write("---\n\n")
        f.write(analysis)
    
    print(f"\nReport saved: {report_path}")
    return analysis


# Run it
if __name__ == "__main__":
    run_weekly_audit(
        site_url="https://acme-realty.example",
        report_dir="reports/seo"
    )

Add it to cron so it runs automatically:

bash
# Crontab: run every Monday at 09:00
# crontab -e  →  add the line:
0 9 * * 1 cd /path/to/project && python weekly-seo-audit.py >> logs/seo-audit.log 2>&1

# Or use Routines in Claude Code: a scheduled task in the cloud (the /schedule command).
# As of October 2026 this is a research preview on paid plans; see the Claude Code documentation for the terms.

An SEO calculator: how to estimate whether it's worth investing

The numbers in the table are made up; this is a teaching example. It's not a forecast or a promise of income: real numbers depend on the niche, the competition, the region and the quality of your content. Plug in your own data.

Calculation step Assumption for the example
Searches per month for the chosen phrase 500
Click-through rate (CTR) at the chosen position 28% (in real life it depends heavily on the query and on AI blocks in the results)
Visits per month 500 × 28% = 140
Site conversion to an inquiry 3%
Inquiries per month 140 × 3% ≈ 4
Share of inquiries that become a deal 25%
Agency revenue per deal $3,000
Monthly revenue from one phrase under these assumptions 4 × 25% × $3,000 = $3,000

On the other side, count your costs: Claude tokens, your own time, hosting and any paid tools you use. Nobody guarantees rankings, so also run a bad-case scenario, for example half the visits and half the conversion rate. If the math still looks reasonable, the article is worth the effort.

🎨 Picture this: AEO = being the expert AI turns to. When ChatGPT recommends you, you're at the top without a single paid ad click. Every well-written article with the right markup is one more expert working for you 24/7.


Practice

Assignment: launch an SEO machine for a real site or a test domain.

Step 1: Set up Google Search Console (15 min)

  • Go to search.google.com/search-console
  • Add your site and verify it through DNS or an HTML file
  • Wait 2-3 days for data to accumulate (or use the script's demo data)

Step 2: Keyword research with Claude (10 min)

bash
# Run the keyword research script
python keyword-research.py

# Open keyword-research.json
# Pick the top 3 priority keywords for your first articles

Step 3: A content brief with the searchfit-seo skill (5 min)

Type this into the chat
# In Claude Code, use the skill:
Claude, use the searchfit-seo:content-brief skill.
Topic: [your keyword from step 2]
Audience: [a description of your audience]

Step 4: Schema markup for the home page (10 min)

bash
# Run the schema markup script
python add-schema.py --page-type local_business --url https://your-site.com
# Copy the resulting JSON-LD and paste it into your site's <head>

Step 5: Set up the weekly audit (10 min)

bash
# Set up cron:
crontab -e
# Add the line:
0 9 * * 1 cd /path/to/project && python weekly-seo-audit.py

# Or run it by hand for the first report:
python weekly-seo-audit.py

Step 6: AI visibility check (5 min)

bash
# Check visibility in AI answers:
python check-ai-visibility.py

# Compare with the results 4 weeks after making changes

Result: by the end of the practice you have a keyword list, your first content briefs, schema markup and an automatic weekly audit.


Tools and resources


Key takeaways

SEO in 2026 is a two-front game: Google + AI engines. The techniques differ, but the content is shared: structured answers to your audience's real questions.

AEO (Answer Engine Optimization) isn't a replacement for SEO, it's a layer on top. A clear question-and-answer structure and tidy schema markup improve your chances of showing up both in Google and in ChatGPT, Claude and Gemini answers, but they don't guarantee it.

Automating the SEO machine: keyword research (Claude) → content brief (5 min) → article → schema markup → weekly audit (automatic). Once it's set up, the routine runs on its own, but you still need to read and check the reports and the texts.

SEO pays off slowly: usually months, not days, and it depends on the niche, the competition and the quality of your content. There are no guaranteed rankings. But a good article that you keep updated stays a working asset.


Next lesson

→ AI Advertising: generating creatives, A/B tests, smart bidding

The mark stays in this browser only and is never sent anywhere. My progress