Skip to content
Doctor SEO

AI for SEO: Using the Models as Tools

Connecting Claude to Your Own Search Data: GSC and GA4 via MCP

Passionfruit’s March 2026 inventory lists Search Console as community-maintained, and no first-party connector is documented. How to connect Claude to Search Console and GA4 read-only, and how to audit the scopes before you grant them.

Updated 2 October 2026 24 min read

Two decisions stand between you and Claude answering questions out of your own Google Search Console and Google Analytics 4 data: which server you run, and how much of your Google account it may touch. Only the second is hard. This page settles the connection at read-only, then spends most of its length on the argument for keeping it there.

Begin with the asymmetry the setup guides pass over. As of 28 March 2026, no first-party connector for your own search data is documented. Dewang Mishra’s inventory of MCP connectors for marketing stacks, published at Passionfruit on that date, files Google Search Console under “Community / self-hosted”. Anthropic ships a verified Ahrefs connector, metadata “Added January 2026”, its 61-tool count checked on 20 September 2026, and Semrush documents an official MCP endpoint on its developer portal, last updated 5 August 2026. Two paid third-party suites have a maintained path into Claude. The data that is already yours does not.

One boundary first. Here Claude is the operator reading your reports, not the engine deciding whom to cite. How AI answer engines choose sources is Level 4 of this course, and nothing here restates it.

What you’ll learn

  • Why read-only is the right default for a measurement source, argued from what the sources do and do not document.
  • Why a figure Claude reads through the API can differ from the one on your screen, and what practitioners are arguing about.
  • A scope auditor in Python you run against the consent screen in front of you, one exit code per failure cause, no network access.
  • What an MCP server for Google Search Console is, who publishes the ones you can install, and whose problem that makes maintenance.
  • Why Google Analytics 4 takes the same OAuth grant and the same caution rather than a page of its own.

What connecting Claude to Search Console actually involves

Connecting Claude to Google Search Console means running a program — an MCP server — that exposes your Search Console data to the model as callable tools, declaring it in the Claude client’s configuration file, and authorising it against your Google account with OAuth. MCP, the Model Context Protocol, is the interface Claude uses to call an external function and get structured data back. After that, Claude asks for performance figures instead of waiting on a CSV export.

No vendor-built server for this is documented in the sources cited here. Two community projects are, and they are the ones below. AminForou/mcp-gsc had reached version 0.3.3 by July 2026 and exposes 20 tools, among them batch_url_inspection and compare_search_periods; its GitHub counters read roughly 1,300 stars and 183 forks on 20 September 2026. delaren47/gsc-ga4-mcp answers for Google Search Console and Google Analytics 4 from one server.

That locates responsibility rather than complaining about anybody’s code. A community-maintained server puts no company behind the endpoint and owes you no release schedule, so reading the source, pinning a version and choosing when to upgrade land on whoever installs it. Bring the reflex from Claude Code for technical SEO: the limit is one you set before the run, not one the tool enforces afterwards.

Who maintains each connector, and when it was checked

This table is dated in its last column, and it is the part of the lesson most likely to be wrong in six months: tool counts move, connectors appear, and community projects stop being maintained without saying so.

Connector Who publishes it First-party for this data? What it reads What it can write Source and date
Google Search Console Community: AminForou/mcp-gsc No 20 tools, including batch_url_inspection and compare_search_periods Per the scope you grant GitHub, version 0.3.3, July 2026; checked 20 September 2026
Search Console and GA4 together Community: delaren47/gsc-ga4-mcp No Both sources through one server Per the scope you grant GitHub; checked 20 September 2026
Ahrefs Ahrefs, with Anthropic’s verified badge Yes, for Ahrefs’ own data Ahrefs research data: keywords, backlinks, rank tracking, brand visibility Not documented as writing to your properties Connector page, metadata “Added January 2026”; checked 20 September 2026
Semrush Semrush, official Yes, for Semrush’s own data Semrush research data through its documented MCP endpoint Not documented as writing to your properties Semrush developer portal, last updated 5 August 2026
Rank Math Rank Math, official Yes, for your WordPress install Audits, metadata, schema, Search Console keyword data Metadata and schema in your WordPress; some features PRO-only Bhanu Ahluwalia, Rank Math blog, 27 July 2026

The last row is the one to pause on. Rank Math’s MCP connector (27 July 2026) pulls Search Console keyword data and writes metadata and schema into your site through a single OAuth consent. It does what its page says; the consequence for your permission model is that one approval puts the agent on both sides of the read/write boundary. Stacking connectors also costs context: every tool definition loads before you type a word, a cost no source cited here measures for SEO work.

Read-only, and the honest reason for it

Grant read-only on Google Search Console and Google Analytics 4, limited to the properties your question needs. The argument is not that write access to analytics is exotic. It is that both of those are the instrument you check the agent with, and an agent able to write to the instrument has quietly deleted your check. Every delegate-then-verify habit in this level depends on the verification staying out of the delegate’s reach.

Now the part most write-ups leave out. No case of an agent damaging Google Search Console or Google Analytics 4 data appears in the sources cited here. No study, no incident report, no sample size — there is no incident to attach one to. So the posture rests on what a granted permission makes possible rather than on something anyone has measured happening, and a rule whose evidentiary basis you can state out loud is a rule you can defend.

One thing this page will not hand you is the scope string itself. No source cited here documents the literal Google OAuth scope for read-only Search Console or Analytics access, and a permission written from memory is the worst kind of guess, because being wrong costs real access to real data rather than a typo. What you get instead is the test: the grant you want answers questions about performance and alters nothing, and its exact wording sits in Google’s documentation and on the screen you are about to approve.

A scope auditor you can run before you press Allow

The read-only decision can be mechanised offline. Paste the scope list from the consent screen into a text file, one per line; scope_audit.py classifies each line read-only or write-capable and exits non-zero if any is write-capable. It opens no socket and calls no API.

Two limits are printed by the script itself, and they are the point of it rather than caveats bolted on. The classification tests the shape of a string: a scope whose last path segment ends in .readonly is reported read-only, everything else write-capable. That is a naming convention, not an authority check, and a provider free to name a write scope …readonly would defeat it. More useful still: a scope list you paste is what the client asked for, not what the server granted. Nothing you run locally will tell you those two differed.

Every validation runs before any scope is judged, so a broken input can never surface as a finding about your configuration. One cause the validations cannot reach: a file cut exactly on a line boundary is indistinguishable from a complete one, because every line is whole and nothing in the bytes records how many lines there should have been. Exit 14 catches only the cut that lands mid-line; the docstring says so, and the remedy is counting the lines against the consent screen yourself. The codes:

Exit code Cause What it means
0 Every scope classified read-only Right shape; still count the lines
2 Usage error: no input path given Nothing examined
3 Finding: at least one write-capable scope Reduce the request
10 Input path missing or unreadable Nothing examined
11 Input empty: no scope lines Export failed; empty is not a pass
12 Input is an HTML document An interstitial saved under HTTP 200
13 Input is JSON, XML or CSV Right data, wrong format
14 Final line not newline-terminated Cut mid-line; redo the copy
15 A line is not a single scope token Prose or a partial paste got in
#!/usr/bin/env python3
"""scope_audit.py - classify an OAuth scope list before you press Allow.

Usage:  python3 scope_audit.py <scopes.txt>

Input is a plain text file, one scope string per line, copied from the consent
screen in front of you. The script is offline: it never calls Google, never
calls Anthropic, and never opens a network socket.

WHAT THE CLASSIFICATION IS. A scope whose last path segment ends in
'.readonly' is reported read-only. Every other scope is reported
write-capable. That is a test on the shape of a string, not a statement about
what an authorisation server granted. A provider free to name a write scope
'...readonly' would defeat it, and a list you paste is what the client asked
for, not what the server returned.

WHAT IT CANNOT DETECT. A download cut exactly on a line boundary is
indistinguishable from a complete file: every line is whole, the last one ends
in a newline, and nothing in the bytes records how many lines there should
have been. Exit 14 catches only the cut that lands mid-line. Count the lines
against the consent screen yourself.

EXIT CODES - one cause each, no cause shares a code with another:
  0   every scope on the list classified read-only
  2   usage error: no input path given
  3   FINDING: at least one write-capable scope on the list
  10  input path missing or unreadable
  11  input empty: no scope lines at all
  12  input is an HTML document (an interstitial or login page saved under 200)
  13  input is a structured document (JSON, XML or CSV), not one scope per line
  14  input looks truncated: the final line has no terminating newline
  15  input contains a line that is not a single scope token

Every validation runs, in that order, before any scope is judged, so a bad
input can never be reported as a finding about your configuration. Code 10 is
raised from two places because one cause has two detection points: the path is
absent, or the path exists and the read fails. No two causes share a code.
"""
import os
import sys


def fail(code, message):
    sys.stderr.write("scope_audit: %s\n" % message)
    sys.exit(code)


def main(argv):
    if len(argv) != 2:
        sys.stderr.write("usage: scope_audit.py <scopes.txt>\n")
        sys.exit(2)                                              # code 2
    path = argv[1]

    if not os.path.isfile(path):
        fail(10, "cannot read %s" % path)                        # code 10
    try:
        with open(path, "r", encoding="utf-8", errors="replace") as fh:
            raw = fh.read()
    except OSError as exc:
        fail(10, "cannot read %s: %s" % (path, exc))             # code 10

    head = raw.lstrip()[:512].lower()
    if "<html" in head or head.startswith("<!doctype html"):
        fail(12, "input is an HTML document, not a scope list")  # code 12

    lines = [ln.strip() for ln in raw.splitlines()]
    scopes = [ln for ln in lines if ln and not ln.startswith("#")]
    if not scopes:
        fail(11, "input holds no scope lines")                   # code 11
    if (head[:1] in ("{", "[") or head.startswith("<?xml")
            or any("," in ln for ln in scopes)):
        fail(13, "input is a structured document, not one scope per line")  # code 13
    if not raw.endswith("\n"):
        fail(14, "input looks truncated: final line is not newline-terminated")  # code 14
    for ln in scopes:
        if " " in ln or "\t" in ln or "/" not in ln:
            fail(15, "not a single scope token: %r" % ln)        # code 15

    width = max(len(s) for s in scopes)
    writable = []
    print("scope_audit - classification by string shape, not by an API call")
    print("source: %s   scopes read: %d" % (path, len(scopes)))
    print("")
    for s in scopes:
        tail = s.rstrip("/").rsplit("/", 1)[-1]
        if tail.endswith(".readonly"):
            verdict = "read-only"
        else:
            verdict = "WRITE-CAPABLE"
            writable.append(s)
        print("%s  %s" % (s.ljust(width), verdict))
    print("")
    if writable:
        print("%d of %d scopes are write-capable by shape." % (len(writable), len(scopes)))
        print("A write-capable scope on an analytics property lets an agent change")
        print("the instrument you would use to check the agent. Go back and reduce it.")
        sys.exit(3)                                              # code 3
    print("All %d scopes are read-only by shape." % len(scopes))
    print("Shape is not authority: confirm the count against the consent screen.")
    sys.exit(0)                                                  # code 0


if __name__ == "__main__":
    main(sys.argv)

The fixtures are generated, not hand-written, so every run below is reproducible from this page alone. Every line the generator writes is a synthetic fixture, and deliberately not a URL: a line such as <provider>/auth/searchdata.readonly has the right shape and no real permission behind it, so nothing here reaches a consent request by accident.

#!/usr/bin/env python3
"""make_fixtures.py - writes every file the scope_audit.py transcripts use.

synthetic fixture: every scope line written by this generator is a placeholder,
not a scope string. Each line opens with the literal angle-bracket segment
<provider>, which is deliberately not a host, and the names after it are
invented, so nothing here can be copied into a consent request by accident.
Replace each line with a scope string from the consent screen in front of you.

Run:  python3 make_fixtures.py
"""
import os

HERE = os.path.dirname(os.path.abspath(__file__))

FILES = {
    # synthetic fixture: three read-only scopes
    "scopes-readonly.txt":
        "<provider>/auth/searchdata.readonly\n"
        "<provider>/auth/siteindex.readonly\n"
        "<provider>/auth/analyticsdata.readonly\n",

    # synthetic fixture: two read-only scopes and one write-capable
    "scopes-mixed.txt":
        "<provider>/auth/searchdata.readonly\n"
        "<provider>/auth/siteindex\n"
        "<provider>/auth/analyticsdata.readonly\n",

    # empty export
    "scopes-empty.txt": "",

    # an HTML interstitial saved under HTTP 200 instead of the scope list
    "interstitial.html":
        "<!doctype html>\n<html><head><title>One moment, please...</title></head>\n"
        "<body><p>Checking your browser before you continue.</p></body></html>\n",

    # the wrong export format: a structured document, not one scope per line
    "scopes.json":
        '{"granted_scopes": ["<provider>/auth/searchdata.readonly"]}\n',

    # a download cut mid-line: the last line has no terminating newline
    "scopes-truncated.txt":
        "<provider>/auth/searchdata.readonly\n"
        "<provider>/auth/siteind",

    # a line that is not a single scope token
    "scopes-malformed.txt":
        "<provider>/auth/searchdata.readonly\n"
        "siteindex readonly please\n",
}

for name, body in FILES.items():
    path = os.path.join(HERE, name)
    with open(path, "w", encoding="utf-8") as fh:
        fh.write(body)
    print("wrote %-24s %4d bytes" % (name, len(body.encode("utf-8"))))

A short runner drives the set:

#!/bin/sh
# run_all.sh - runs scope_audit.py against every fixture and prints each exit code.
python3 make_fixtures.py
for f in scopes-readonly.txt scopes-mixed.txt scopes-empty.txt interstitial.html \
         scopes.json scopes-truncated.txt scopes-malformed.txt missing.txt; do
  echo ""
  echo "\$ python3 scope_audit.py $f"
  python3 scope_audit.py "$f" 2>&1
  echo "exit $?"
done
echo ""
echo "\$ python3 scope_audit.py"
python3 scope_audit.py 2>&1
echo "exit $?"

This is the verbatim output of sh run_all.sh, every described run included:

wrote scopes-readonly.txt       110 bytes
wrote scopes-mixed.txt          101 bytes
wrote scopes-empty.txt            0 bytes
wrote interstitial.html         142 bytes
wrote scopes.json                60 bytes
wrote scopes-truncated.txt       59 bytes
wrote scopes-malformed.txt       62 bytes

$ python3 scope_audit.py scopes-readonly.txt
scope_audit - classification by string shape, not by an API call
source: scopes-readonly.txt   scopes read: 3

<provider>/auth/searchdata.readonly     read-only
<provider>/auth/siteindex.readonly      read-only
<provider>/auth/analyticsdata.readonly  read-only

All 3 scopes are read-only by shape.
Shape is not authority: confirm the count against the consent screen.
exit 0

$ python3 scope_audit.py scopes-mixed.txt
scope_audit - classification by string shape, not by an API call
source: scopes-mixed.txt   scopes read: 3

<provider>/auth/searchdata.readonly     read-only
<provider>/auth/siteindex               WRITE-CAPABLE
<provider>/auth/analyticsdata.readonly  read-only

1 of 3 scopes are write-capable by shape.
A write-capable scope on an analytics property lets an agent change
the instrument you would use to check the agent. Go back and reduce it.
exit 3

$ python3 scope_audit.py scopes-empty.txt
scope_audit: input holds no scope lines
exit 11

$ python3 scope_audit.py interstitial.html
scope_audit: input is an HTML document, not a scope list
exit 12

$ python3 scope_audit.py scopes.json
scope_audit: input is a structured document, not one scope per line
exit 13

$ python3 scope_audit.py scopes-truncated.txt
scope_audit: input looks truncated: final line is not newline-terminated
exit 14

$ python3 scope_audit.py scopes-malformed.txt
scope_audit: not a single scope token: 'siteindex readonly please'
exit 15

$ python3 scope_audit.py missing.txt
scope_audit: cannot read missing.txt
exit 10

$ python3 scope_audit.py
usage: scope_audit.py <scopes.txt>
exit 2

The value is not in the Python: the mixed list exits 3 while broken inputs exit 10 through 15, so a failed export can never be mistaken for a clean audit — the failure that makes a checklist worse than none.

GA4 runs on the same grant

Nothing about Google Analytics 4 changes the procedure. The decision that matters comes first — how much of the account the agent may see — and only then do you issue the OAuth grant and point the client configuration at a server. delaren47/gsc-ga4-mcp can bundle Google Analytics 4 and Google Search Console in one server because no step in that sequence differs, which is why GA4 is a section here and not a page: two pages would differ by a noun.

The gap between the two sources is where an agent improvises. Which query earned an impression and a click in Google is a Google Search Console fact; what the visitor then did is a Google Analytics 4 fact; neither product holds the other half. Hand an agent both in read-only and it can join them, provided you make it say the join is approximate. Hand it one and half its answers come from your data, the rest from inference in the same confident voice. Reading those series is Level 5 on SEO analytics and measurement.

The interface gained a split the API has not

The Search Console interface and the Search Analytics API do not move in lockstep, and there is a dated case. Google Search Central announced on 24 September 2026 that the Search Console web search type is being split into web: text-based and web: multimodal, rolling out globally from that day. The API reference for searchanalytics.query, last updated 11 August 2026 and checked on 26 September 2026, lists no multimodal value.

The consequence is concrete. The agent queries the API, so it works with the aggregate and cannot break out a dimension the API does not expose. Ask it to explain a step only visible with the new split and it will reach for another explanation, because a model missing the explanatory variable rarely answers “I cannot tell from this”.

Practitioners disagree about what those multimodal rows are, and no verdict is offered here. Either they carry impressions Google was not counting at all, or they carry impressions already inside the old total, now separated out. Which is true decides how to read the step in your chart: on the first reading the total climbs while your site stands still; on the second the text-based figure gives up exactly what the multimodal figure gains and the total never moves.

Google’s own answer is the strongest evidence for the first reading. John Mueller, on Bluesky on 24 September 2026, began with “I’ll double-check, I thought these were not reported at all before”, then came back with “And … checking with the team, the data wasn’t previously in the counts, the report has new data”.

Against that sits an export someone actually looked at. Dave Smart, quoted by Barry Schwartz at Search Engine Roundtable on 24 September 2026, described “corresponding drops in impressions and clicks starting the 10th on a few properties, which lines up with the new data”. Property count, domains and impression volume all went undisclosed, so this is one practitioner reading an unstated number of sites.

What would settle it does not exist in public: no impression series across both dates with its properties named, no Google note stating when collection began. Neither side can show its working, which makes the direction of the step in your total a measurement you take rather than a position you adopt. Pull the figures either side of 24 September 2026 and read them; an agent with your data connected cannot honestly tell you more.

What a connected Search Console is actually worth

What a read-only Search Console connection buys is time, not insight. Query groupings, batch URL inspections, period-over-period comparisons, impression drops traced section by section — none of it is an export and a reconciliation any more. Judgement is what it does not buy, and there is published evidence that real data in front of the model does not buy it either.

Keyword.com puts a sample size on its numbers, which is rare enough to say. State of AI in SEO 2026 (1 January 2026) rests on n=97 usable responses, self-selected, with no margin of error published. Within it, 38% named technical SEO audits among the tasks they use AI for, and exactly 1% — one respondent of the 97 — called their work fully automated. Asked why they had not automated more, 57% picked “quality not good enough” from the hold-back options — the hold-back figure, not the report’s separate 70% for biggest limitations, and not the separate 57% it gives for ChatGPT adoption.

On judgement specifically, the closest documented trial is David’s at ContextBolt, who spent a week running his own real SEO with Claude (published 23 June 2026). He found the model “treated third-party estimates as factual data” and pushed an unwinnable keyword while its difficulty score was on screen. That is a single author on a single site across seven days, with nothing held constant for comparison and no sample size, because the author publishes none. Connecting your own data fixes where the numbers come from. It does not change what the model does with a number once it has one.

There is one boundary no connector moves, and Lawrence Hitches states it directly in his guide to Claude for technical SEO (2 April 2026): Claude analyses crawl data, it does not crawl sites. So a connected Search Console hands the model Google’s account of your pages, filtered by whatever Google decided to report. That is a second-hand description, not a look at the site. For what the unaided model does reach, see What Claude can and cannot do for SEO in AI for SEO: using the models as tools.

Common mistakes

  • Accepting a bundled consent because the bundle is convenient. A connector that reads your search data and writes your metadata behind one approval does what it advertises; the problem is that your permission model now has no boundary. The fix: separate the reading grant from the writing grant.
  • Treating a read-only grant as though it expires. It does not. The consent given to answer one question is still live long after the answer, and read-only is still permanent standing access to everything in scope. The fix: record the grant date beside the configuration and revoke it when the analysis closes.
  • Explaining a step in a chart that crosses 24 September 2026. Whether those rows are new data or a reclassification is unsettled, and the API the agent queries does not expose the new dimension. The fix: annotate the date, export both sides, and answer on your own property rather than adopting anyone’s explanation — a model’s included.
  • Pasting an OAuth scope string copied from a blog post. The string grants whatever it grants, not whatever the article said. The fix: find it in Google’s documentation, run it through scope_audit.py, and check the count against the consent screen.

The short version

  • Read-only is the default because Search Console and GA4 are the instrument you check the agent with. That rests on what a permission makes possible, not on a measured incident: no case of an agent damaging either data set appears in the sources cited here.
  • No OAuth scope string is printed here, because no source cited here documents one. scope_audit.py classifies a pasted list by the .readonly convention and says in its output that shape is not authority and that a pasted list is a client request, not a server grant.
  • Keyword.com (1 January 2026, n=97) found 1% describing their work as fully automated. Connected data speeds up the reading, not the deciding.
  • Passionfruit’s connector inventory of 28 March 2026 files Google Search Console as “Community / self-hosted”, and no first-party Anthropic or Google connector for Search Console or GA4 is documented in the sources cited here, so installation, maintenance and permissions are the installer’s.
  • Two paid third-party suites do have maintained paths — Ahrefs through an Anthropic-verified connector (61 tools, checked 20 September 2026) and Semrush through an official MCP endpoint (last updated 5 August 2026). The asymmetry is the landscape, not a scandal.
  • The interface split web search into text-based and multimodal on 24 September 2026; the searchanalytics.query API reference, last updated 11 August 2026 and checked 26 September 2026, lists no multimodal value, so Claude does not see the split.
  • Whether those rows are new data or a reclassification is unsettled and nobody has published a paired property-level series, so answer it from your own export.

Frequently asked questions

Why are Claude’s impression figures different from my Search Console dashboard?

Check the date range before calling it a bug. From 24 September 2026 the Search Console interface reports web search as text-based and multimodal separately, while the Search Analytics API reference for searchanalytics.query — last updated 11 August 2026, checked 26 September 2026 — carries no multimodal value. An MCP server reads the API, so a period spanning that date is summed one way for the agent and displayed another way for you.

Do I have to self-host something to get Search Console into Claude?

No connector built by Google or Anthropic is documented in the sources cited here, so the installable options for a direct Search Console connection are community servers you run yourself. The community servers documented here are AminForou/mcp-gsc (version 0.3.3, July 2026, 20 tools) and delaren47/gsc-ga4-mcp, which covers GA4 too. Dewang Mishra’s Passionfruit inventory of 28 March 2026 is what classes it “Community / self-hosted”. Rank Math’s official connector reaches Search Console keyword data through WordPress, a different route, shown in the table above.

Which permission should I tick on the Google consent screen?

The one that answers questions about performance and changes nothing — confirming its exact wording on Google’s documentation and on the screen itself, because no source cited here documents that literal string. One thing to know before trusting your own audit of it: a scope list you paste is what the client asked for, not what the server granted, and nothing you run locally closes that gap.

Are the scopes in the example files real?

No. Every line written by make_fixtures.py is a synthetic fixture: <provider>/auth/searchdata.readonly is a placeholder, not a URL and not a permission name, so it cannot reach a consent request by accident. They exist so the auditor’s nine exit paths can be shown without printing a scope string no source cited here documents. Replace them with your own consent screen’s list first.

Sources

Cited without a link because the page could not be opened. Named and dated instead, with no mirror or repost substituted.

  • Search Analytics API reference, searchanalytics.query — last updated 11 August 2026, checked 26 September 2026. Lists no multimodal value.
  • Google Search Central, announcement of the Search Console web search type split into web: text-based and web: multimodal — 24 September 2026.
  • Dave Smart, quoted by Barry Schwartz at Search Engine Roundtable — 24 September 2026. No property count, impression volume or domains disclosed.
  • John Mueller, posts on Bluesky — 24 September 2026, 14:16 UTC.
  • GitHub AminForou/mcp-gsc — version 0.3.3, July 2026; roughly 1,300 stars, 183 forks, 20 tools; checked 20 September 2026.
  • GitHub delaren47/gsc-ga4-mcp — bundles Search Console and Google Analytics 4; checked 20 September 2026.

Continue the course

Previous lesson: Claude Code for Technical SEO, where the agent workflow over this data is built. Related here: What Claude Can and Cannot Do for SEO, and Getting Cited by Claude for the retrieval side of the same product. Level hub: AI for SEO: Using the Models as Tools. Course index: the free SEO course.

For reading the series: SEO Analytics and Measurement. For how AI search picks whom to cite: GEO and AIO: SEO for AI Search.