Blog · Claude × Google Analytics 4

Connect Google Analytics 4 to Claude (Code): three ways, and what each one is for

Published July 20, 2026 · Updated · 12 min read

There are three ways to connect Google Analytics 4 to Claude or Claude Code, and none of them starts with a GA4 export. Google's official Google Analytics MCP server runs on your own machine and answers from the GA4 Data and Admin APIs with a read-only scope. Hosted connectors from data vendors such as Supermetrics and Windsor.ai add GA4 to Claude from its connector directory and fetch the numbers when you ask. A synced data backend such as TableBI copies GA4 into tables that your agent queries with read-only SQL, next to Search Console and your ad spend. The first two ask GA4 live, which suits one-off questions; the third stores the numbers under fixed definitions, which suits questions that come back every week. Below: a side-by-side comparison, the setup for each route, and four GA4 queries that work in Claude Code once the data is synced.

Three ways to connect GA4 to Claude, compared

"Connect Google Analytics to Claude" now names three different products. Google publishes an official MCP server that can only read. Data-connector vendors sell hosted Claude connectors that read GA4 and, for ad platforms, can also make changes. A synced backend — the route the second half of this page sets up — keeps a copy of GA4 that your agent queries in SQL alongside your other channels.

RouteWhat the agent readsCan it change anything?What you set upPrice
Google's official Google Analytics MCP serverLive GA4 Data API and Admin API results: account and property details, core, funnel and realtime reports, custom dimensions and metricsNo — it requests only the analytics.readonly scopepipx, a Google Cloud project with the Analytics Admin and Data APIs enabled, and Application Default Credentials; then one claude mcp add. It runs on your machineFree, open source (Apache 2.0); the repository labels it experimental
Supermetrics' Claude connectorGA4 reporting data, fetched each time you ask, through a connector listed in Claude's directoryNot GA4 — its campaign management covers ad platforms such as Google Ads and Facebook AdsClick Connect on the Supermetrics listing in Claude's directory, then authorize GA414-day free trial, then a paid plan
Windsor.ai's Claude connectorGA4 metrics and dimensions through Windsor.ai's hosted MCP connector, pulled when you askNot GA4 — its write actions cover ad platformsLink the property at onboard.windsor.ai, then add the Windsor connector in ClaudeFree plan; paid plans from $19/month billed yearly, as of September 2026
TableBI (this page)A synced copy of GA4 — date, property, channel and country with sessions, users, page views, conversions and revenue — in read-only SQL, beside Search Console, Google Ads and Meta Ads (beta)No — read-only by design, and nothing is installed on your websitetablebi install once, then one OAuth: tablebi connect ga4. Works in Claude CodeFree for one source and one live dashboard; see pricing

Sources, read September 26, 2026 (Google's README re-read October 1, 2026, unchanged on tools, scope and setup): googleanalytics/google-analytics-mcp (tools, read-only scope, setup, license), Supermetrics' GA4 to Claude page, Windsor.ai's GA4 to Claude guide and Windsor.ai pricing.

Pick by the job. If you already run a Google Cloud project and want ad-hoc GA4 answers in Claude Code for free, Google's MCP server is the direct route: set up credentials once, and every question becomes a live Data API call. If you want GA4 inside the Claude app with no terminal — or in the same connector as ad platforms you may also want Claude to operate — a vendor connector is the shortest path. If the question comes back every week and crosses sources — sessions against Search Console clicks, GA4 conversions against ad spend — a synced backend is built for it, because the numbers are stored once under shared definitions instead of being re-fetched and re-reconciled in every conversation. GA4 alone rarely answers "was that campaign worth it"; the join is the work.

Route 1: Google's GA4 MCP server in Claude Code

Google's server is a Python package, analytics-mcp, that Claude Code starts as a local process. Setup, per its README, has four parts: install pipx; enable the Google Analytics Admin API and the Google Analytics Data API in a Google Cloud project; configure Application Default Credentials for a user who can see your GA4 properties, including the read-only scope; then register the server with Claude Code:

terminal
# Application Default Credentials with the read-only Analytics scope
gcloud auth application-default login \
  --scopes https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform \
  --client-id-file=YOUR_CLIENT_JSON_FILE

# register the server with Claude Code
claude mcp add analytics-mcp --scope user \
  -e "GOOGLE_APPLICATION_CREDENTIALS=PATH_TO_CREDENTIALS_JSON" \
  -e "GOOGLE_PROJECT_ID=YOUR_PROJECT_ID" \
  -- pipx run analytics-mcp

From then on Claude can call tools such as run_report, run_funnel_report and run_realtime_report. Two things to know before relying on it. Each answer is a fresh Data API request in GA4's own dimension and metric names, so nothing is stored between sessions. And it sees GA4 only — putting sessions next to ad spend means a second connector and a join that the model does in its head.

Route 2: a vendor connector in the Claude app

Supermetrics and Windsor.ai both list a connector in Claude's directory. With Supermetrics you click Connect on its listing and authorize GA4. With Windsor.ai you link the GA4 property at onboard.windsor.ai first, then add the Windsor connector in Claude. Both fetch GA4 data when you ask a question, and both carry many other sources on the same connector. Supermetrics starts with a 14-day trial and Windsor.ai has a free plan; beyond that, both are paid. Their write features exist for ad platforms; neither changes your GA4 configuration.

Route 3: sync GA4 into a backend Claude Code queries

TableBI is a data backend built to be driven from the terminal by an agent like Claude Code. GA4 is a live connector: one OAuth round-trip in the browser, and syncs run on a schedule from then on. Three moves — Pipe the source in, Ask in read-only SQL, Pin the answer as a live dashboard.

terminal
# install the CLI, then teach your agent to drive it
npm i -g @tablebi/cli
tablebi login
tablebi install

# browser opens for OAuth → first sync (one property is picked for you;
# with several, it lists them — re-run with --account <property id>)
tablebi connect ga4

# what's connected, and how fresh is each source?
tablebi context

tablebi install plants a skill in ~/.claude/skills; after that, "how did organic do last week?" inside Claude Code is enough — the agent knows which commands to run. The connector requests Google's read-only Analytics scope and reads GA4 through the API, so there is no tag, script or plugin to add to your site, no Data API client to maintain and no service account key in your repo.

No second model in the loop: TableBI hosts no LLM. Your Claude Code session is the brain; the backend returns deterministic SQL results plus a trust block with each source's freshness and caveats.

Two altitudes: ga4_raw and facts

GA4 data lands at two levels, and knowing which to query is most of the skill:

  • ga4_sources_raw and ga4_ai_landing_raw — two smaller GA4 tables next to the main one: sessions, users, conversions and revenue by source and medium, and the landing pages of visits that came from AI assistants such as ChatGPT, Perplexity, Claude and Gemini. The ai_assistant(source) macro recognizes those sources, so "how much traffic does ChatGPT send us, and to which pages?" is one query.
  • ga4_raw — what GA4 reported: date, property, channel, country, sessions, users, page_views, conversions, revenue. Channel and country breakdowns live here, and so does forensics when a number looks wrong.
  • facts — the cross-channel table where GA4 sits beside Search Console, Google Ads and Meta Ads (beta) under shared dimensions (date, platform, account, campaign) and shared measures (impressions, clicks, cost, conversions, conversion_value). For GA4 rows the account is the property; channel and country are empty here. This is where "was it worth it" gets answered.

Start any session by rehydrating — definitions, connected sources, freshness and the command cheatsheet in one JSON blob the agent holds in context:

terminal
tablebi context --json

Four GA4 queries worth stealing

1. Sessions and conversions by channel. The default "where is traffic actually coming from" question, over the last 28 days:

claude code → tablebi
tablebi ask "SELECT channel, SUM(sessions) AS sessions,
             SUM(conversions) AS conv, SUM(revenue) AS revenue
             FROM ga4_raw
             WHERE date >= (SELECT MAX(date) FROM ga4_raw) - 28
             GROUP BY channel ORDER BY sessions DESC"

2. Week over week, by channel. Trends beat totals — this is the shape that tells you whether a dip is real or a weekday artifact:

claude code → tablebi
tablebi ask "SELECT channel,
             SUM(CASE WHEN date > (SELECT MAX(date) FROM ga4_raw) - 7
                      THEN sessions ELSE 0 END) AS this_week,
             SUM(CASE WHEN date <= (SELECT MAX(date) FROM ga4_raw) - 7
                      THEN sessions ELSE 0 END) AS prev_week
             FROM ga4_raw
             WHERE date >= (SELECT MAX(date) FROM ga4_raw) - 14
             GROUP BY channel ORDER BY this_week DESC"

3. Conversion rate per channel, without doing the arithmetic in the model. Definition macros (ctr, cpa, cvr, roas, aov…) live in the query engine, so the agent never re-derives a ratio slightly wrong:

claude code → tablebi
tablebi ask "SELECT channel, cvr(SUM(conversions), SUM(sessions)) AS cvr,
             SUM(conversions) AS conv
             FROM ga4_raw
             WHERE date >= (SELECT MAX(date) FROM ga4_raw) - 28
             GROUP BY channel ORDER BY conv DESC"

4. The one GA4 can't answer alone — spend against outcome. Same table, both sides of the equation:

claude code → tablebi
tablebi ask "SELECT platform, SUM(cost) AS spend,
             SUM(conversions) AS conv, cpa(SUM(cost), SUM(conversions)) AS cpa
             FROM facts
             WHERE date >= (SELECT MAX(date) FROM facts) - 28
             GROUP BY platform ORDER BY spend DESC"

Notice what every window has in common: (SELECT MAX(date) FROM …) - N, never current_date, which the query layer doesn't offer — so a window can't silently include days that haven't synced. The newest day is still provisional: Google's own documentation says GA4 data processing can take 24–48 hours, and reports can change during that time. That is why the trust block lists, for each source, the newest synced day and the newest day every active property has reached — the same discipline we walk through in Claude Code analytics.

Using Codex instead of Claude Code

Both routes that run on your machine work in OpenAI's Codex as well. Codex reads MCP servers from ~/.codex/config.toml (or a project's .codex/config.toml), and OpenAI's MCP page says the Codex CLI, the IDE extension and the ChatGPT desktop app share that configuration. Google's GA4 server goes in as a [mcp_servers] table with the same command and environment variables its README gives for Claude Code:

~/.codex/config.toml
[mcp_servers.analytics-mcp]
command = "pipx"
args = ["run", "analytics-mcp"]
env = { GOOGLE_APPLICATION_CREDENTIALS = "PATH_TO_CREDENTIALS_JSON", GOOGLE_PROJECT_ID = "YOUR_PROJECT_ID" }

For TableBI, tablebi install --codex plants the same skill in ~/.agents/skills, so Codex drives the CLI the way Claude Code does. If you would rather give Codex tools as well, tablebi install --codex --mcp also appends a [mcp_servers.tablebi] table to ~/.codex/config.toml for TableBI's built-in local MCP server (tablebi mcp: context, query, sql, define and dashboards). There is no hosted TableBI MCP endpoint; both routes run the CLI on your machine with your own sign-in.

Errors you will run into

WhereWhat you seeWhat to do
Google's MCP serverPermission or "API not enabled" errors on the first callEnable both the Analytics Admin API and the Analytics Data API in the Cloud project, and make sure the Application Default Credentials include the analytics.readonly scope, as the README requires
Google's MCP serverYour property is missing from get_account_summariesThe credentials belong to a Google user who can't see that property; sign in with one who can
Google's MCP serverClaude or Codex doesn't list the serverCheck the path in GOOGLE_APPLICATION_CREDENTIALS and that pipx run analytics-mcp starts in a terminal on its own
TableBIGoogle shows "This app isn't verified" before the consent pageTableBI's Google verification is in review: choose Advanced, then continue to console.tablebi.com. The scope is read-only
TableBIconnect ga4 returns a list instead of syncingYour account sees several properties: rerun with --account <property id>
TableBIYesterday's sessions look lowGA4 keeps processing the last 24–48 hours; the answer's trust block says how far each source runs
Any routeGA4 organic sessions don't match Search Console clicksThey count different things; see why Search Console clicks don't match GA4 sessions

The trap: GA4 sessions are not Search Console clicks

The most common bad analysis in this category is an agent trying to reconcile GA4 sessions with Search Console clicks, finding a gap, and confidently inventing a story about broken tracking. They are different populations counted different ways: a Search Console click is a click on a search result; a GA4 session is on-site behavior with its own deduplication and its own consent-dependent losses. They were never supposed to be equal.

So every answer ships with a trust block — per-source freshness, caveats and lineage. The Search Console-versus-GA4 mismatch is written down as an explicit caveat, and the agent reads it before you do, so it explains the gap instead of raising a false alarm. If Search Console is your main lane, Claude Code for SEO covers that side in depth, and why Search Console clicks don't match GA4 sessions lists every documented cause.

Pin it: GA4 on a live URL

When a GA4 view is worth keeping, don't screenshot it — pin it. A title, a chart kind after two colons (line, bar, hbar, pie, area, scorecard; no suffix renders a table), and the query:

claude code → tablebi
tablebi pin --title "GA4 traffic overview" \
  --widget "Daily sessions (28d)::line=SELECT date, SUM(sessions) AS sessions
    FROM ga4_raw WHERE date >= (SELECT MAX(date) FROM ga4_raw) - 28
    GROUP BY date ORDER BY date" \
  --widget "Sessions by channel::hbar=SELECT channel, SUM(sessions) AS sessions
    FROM ga4_raw WHERE date >= (SELECT MAX(date) FROM ga4_raw) - 28
    GROUP BY channel ORDER BY sessions DESC"
✓ published → https://dk.tablebi.com/d/dsh_…  (public, read-only, self-refreshing)

The output is a URL you hand to a client or a co-founder — no React app, no BI seat. Here is a live dashboard on real data you can open now: Search Console totals across the TableBI founder's own sites. It is a different dashboard from the one described above, published the same way, and it re-runs its queries as the sources sync (what the demo shows, and what it doesn't):

Why not write the Data API client yourself?

Rolling your own is a legitimate choice — Claude Code will happily write the client. It's the second week that decides it:

  • Auth and refresh are ongoing, not one-time. Refresh tokens get revoked and scopes change, and somebody has to notice when the job quietly stops.
  • Incremental sync is the actual work. Backfill, re-pulls for rows GA4 is still processing, retries — that's most of the code in any pipeline, and none of it is your product.
  • One source is never the question. The moment you want GA4 conversions next to ad spend, a hand-rolled client becomes a hand-rolled warehouse with a hand-rolled definition layer.

The same three-way choice exists for ad platforms: see connecting Google Ads to Claude Code and connecting Meta Ads to Claude. For the dashboard side of this workflow, see Claude Code dashboards; for channels with no live connector, CSV analysis with Claude Code covers the file path into the same definitions.

FAQ

How do I connect Claude Code to Google Analytics?

Two routes work in the terminal. With Google's official server, enable the Analytics Admin and Data APIs in a Google Cloud project, set up Application Default Credentials with the analytics.readonly scope, and register it with claude mcp add analytics-mcp … -- pipx run analytics-mcp. With TableBI, run tablebi install once, then tablebi connect ga4, authorize in the browser (with several properties, re-run it with --account and the property ID); Claude Code then queries GA4 with read-only SQL.

Can Claude set up Google Analytics for me?

Not through these connections — they read data. Google's MCP server requests only the analytics.readonly scope, and TableBI's GA4 connector is read-only and installs nothing on your website. Creating the property and adding the Google tag still happen in GA4's admin and in your site's code; Claude can draft the tag and the steps, but a person applies them.

Is there an official Google Analytics MCP server?

Yes. Google publishes googleanalytics/google-analytics-mcp, an open-source (Apache 2.0) server labeled experimental. It runs locally, uses the GA4 Admin and Data APIs, and is read-only: it requests the analytics.readonly scope. Its README documents setup for Gemini CLI, Gemini Code Assist and Claude Code.

What GA4 fields can I query in TableBI?

ga4_raw holds date, property, channel, country, sessions, users, page_views, conversions and revenue as GA4 reported them. facts holds the cross-channel view — date, platform, account and campaign — so GA4 conversions sit beside ad spend under one set of definitions.

Why don't my GA4 sessions match Search Console clicks?

Different populations, counted differently — a search-result click versus on-site behavior with its own deduplication. That mismatch ships as an explicit caveat in every trust block, so your agent explains the gap instead of "fixing" it.

Try it

Put GA4 one command away from your Claude Code session.

terminal
npm i -g @tablebi/cli && tablebi install