A step-by-step guide. Two files to copy, one command to run!
If you use Google Analytics to track your website, there is a way to stop opening dashboards and just ask Claude questions instead.
- “How many people visited my site last week?”
- “What are my most read pages this month?”
- “Which country sends me the most traffic?”
Claude will fetch the real numbers and answer you directly.
Here is everything you need to set it up!
Article content:
Step 1 — Create a read-only access pass for Claude
Step 2 — Give that access pass permission to see your analytics
Step 3 — Install one software package
Step 4 — Create a folder and add three files
File 1 — Copy this into a file named: SKILL.md
File 2 — Copy this into a file named: query_script.py
Step 5 — Test that it works
Step 6 — Ask Claude
If something goes wrong
What you will need before you start
→ A Google Analytics account (the current version, called GA4)
→ A Google account that has access to that Analytics property
→ Claude Cowork installed on your computer (or Claude Code)
→ Python installed on your computer (free — download from python.org if needed)
Step 1 — Create a read-only access pass for Claude
Think of this step as creating a special key that lets Claude read your analytics data — but nothing else. It cannot change anything, it cannot delete anything. Read only.
- Open your browser and go to: console.cloud.google.com. Sign in with the same Google account you use for Analytics.
- At the top of the page, click the project dropdown (it may say “Select a project”). Click New Project → give it any name (e.g. “My Analytics”) → click Create. Wait a few seconds, then select your new project from the dropdown.
- In the search bar at the top, type: Google Analytics Data API. Click the result → click the blue Enable button.
- In the left menu, click IAM & Admin → Service Accounts → + Create Service Account. In the name field, type anything — for example: claude-analytics-reader. Click Create and Continue → skip the next two steps → Done.
- You will now see your new account listed. Click on it. Go to the Keys tab → Add Key → Create new key → select JSON → click Create. A file will download to your computer. This is your access key — keep it safe. Also copy the email address shown for this account: claude-analytics-reader@your-project-name.iam.gserviceaccount.com – you will need this email in the next step.

Step 2 — Give that access pass permission to see your analytics
The key from Step 1 exists, but Google Analytics doesn’t know about it yet. You need to invite it.
- Go to: analytics.google.com
- Click the gear icon (⚙️) at the bottom left → this opens Admin settings.
- In the middle column (Property), click Property Access Management.
- Click the + button (top right) → Add users. Paste in the email address from Step 1. Set the role to Viewer. Uncheck “Notify new users by email” (it’s not a real inbox). Click Add.
- While still in Admin, click Property Settings (middle column). Find the Property ID — it is a plain number, like 123456789. Write this number down. You will need it in Step 4.
Step 3 — Install one software package
Open the Terminal app on your computer. (On Mac: press Cmd + Space, type Terminal, press Enter. On Windows: press the Windows key, type cmd, press Enter.)
Type the following and press Enter:
pip3 install google-analytics-data –break-system-packages
Wait until you see “Successfully installed”. That is all for this step.
(The –break-system-packages part is required on modern Mac and Linux computers. It just tells the installer to go ahead — it is safe.)
Step 4 — Create a folder and add three files
On your computer, navigate to this folder:
- Mac/Linux: the folder called .claude/skills/ inside your home folder
- Windows: C:\Users\YourName\.claude\skills\
If the skills folder does not exist yet, create it.
Inside that folder, create a new folder called: google-analytics. Move the key file you downloaded in Step 1 into this folder. Rename that file to: your-credentials.json
Inside the google-analytics folder, create two new text files. Copy the contents below exactly into each one. Your folder should look like this when done:
.claude/skills/google-analytics/
├── SKILL.md ← copy content from File 1 below
├── query_script.py ← copy content from File 2 below
└── your-credentials.json ← the key file from Step 1
File 1 — Copy this into a file named: SKILL.md
```
—
name: google-analytics
description: “Query Google Analytics 4 data. Use when the user asks about website traffic, page views, sessions, user counts, conversions, top pages, traffic sources, or any analytics/metrics questions. Trigger on keywords like \”analytics\”, \”traffic\”, \”visitors\”, \”page views\”, \”sessions\”, \”GA4\”, \”bounce rate\”, \”conversions\”, \”top pages\”, \”referrals\”.”
metadata:
version: 1.0.0
—
# Google Analytics 4 Skill
Query GA4 property data using the Google Analytics Data API v1.
## Setup
– **Credentials**: Service account JSON key — place it in the same folder as query_script.py with the filename your-credentials.json. The script resolves the path automatically.
– **Property ID**: 123456789
– **Python dependency**: google-analytics-data (install: pip3 install google-analytics-data –break-system-packages)
## How to Use
Run the query script using its absolute path:
python3 ~/.claude/skills/google-analytics/query_script.py –report <report_type> [options]
The credentials file is resolved automatically from the same directory as the script.
## Available Reports
overview — High-level summary: users, sessions, page views, bounce rate, engagement rate
pages — Top pages by views, with users and avg engagement time
sources — Traffic by source and medium, sessions, conversions
countries — Geographic breakdown: country, sessions, users, engagement rate
devices — Desktop / mobile / tablet split
daily — Day-by-day trend: date, users, sessions, page views
realtime — Active users on the site right now
custom — Any GA4 metric and dimension combination
## Common Options
–days N How many days to look back (default: 30)
–limit N Maximum number of rows to return (default: 10)
–start DATE Exact start date in format YYYY-MM-DD
–end DATE Exact end date in format YYYY-MM-DD
–output Output format: table, json, or csv
## Example Commands
python3 query_script.py –report overview –days 7
python3 query_script.py –report pages –days 30 –limit 20
python3 query_script.py –report sources –days 30
python3 query_script.py –report daily –days 14
python3 query_script.py –report realtime
```
—

File 2 — Copy this into a file named: query_script.py
Before copying, find the two lines near the top marked CHANGE THIS and update them:
→ Replace your-credentials.json with the actual filename of your key file (if you renamed it differently)
→ Replace 123456789 with your own Property ID from Step 2
```python
#!/usr/bin/env python3
“””
Google Analytics 4 Data API query tool.
Queries GA4 property data using a service account.
“””
import argparse
import json
import sys
import os
from datetime import datetime, timedelta
# ============================================================
# CHANGE THIS — update the two values below to match your setup
# ============================================================
CREDENTIALS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), “your-credentials.json”)
PROPERTY_ID = “123456789”
# ============================================================
def get_client():
“””Create and return a GA4 BetaAnalyticsDataClient.”””
os.environ[“GOOGLE_APPLICATION_CREDENTIALS”] = CREDENTIALS_PATH
from google.analytics.data_v1beta import BetaAnalyticsDataClient
return BetaAnalyticsDataClient()
def build_request(metrics, dimensions, days=30, start=None, end=None, limit=10, order_by_metric=None, desc=True):
“””Build a RunReportRequest.”””
from google.analytics.data_v1beta.types import (
RunReportRequest, Metric, Dimension, DateRange, OrderBy
)
end_date = end or datetime.now().strftime(“%Y-%m-%d”)
if start:
start_date = start
else:
start_date = (datetime.now() – timedelta(days=days)).strftime(“%Y-%m-%d”)
request = RunReportRequest(
property=f”properties/{PROPERTY_ID}”,
metrics=[Metric(name=m.strip()) for m in metrics],
dimensions=[Dimension(name=d.strip()) for d in dimensions] if dimensions else [],
date_ranges=[DateRange(start_date=start_date, end_date=end_date)],
limit=limit,
)
if order_by_metric:
request.order_bys = [
OrderBy(
metric=OrderBy.MetricOrderBy(metric_name=order_by_metric),
desc=desc,
)
]
return request
def format_response(response, output=”table”):
“””Format the API response into the desired output format.”””
headers = [h.name for h in response.dimension_headers] + [h.name for h in response.metric_headers]
rows = []
for row in response.rows:
values = [dv.value for dv in row.dimension_values] + [mv.value for mv in row.metric_values]
rows.append(values)
if output == “json”:
result = []
for row in rows:
result.append(dict(zip(headers, row)))
return json.dumps(result, indent=2)
elif output == “csv”:
lines = [“,”.join(headers)]
for row in rows:
lines.append(“,”.join(row))
return “\n”.join(lines)
else: # table
col_widths = [len(h) for h in headers]
for row in rows:
for i, val in enumerate(row):
col_widths[i] = max(col_widths[i], len(str(val)))
separator = “+” + “+”.join(“-” * (w + 2) for w in col_widths) + “+”
header_row = “|” + “|”.join(f” {h:<{col_widths[i]}} ” for i, h in enumerate(headers)) + “|”
lines = [separator, header_row, separator]
for row in rows:
line = “|” + “|”.join(f” {str(v):<{col_widths[i]}} ” for i, v in enumerate(row)) + “|”
lines.append(line)
lines.append(separator)
if response.row_count:
lines.append(f”\nTotal rows: {response.row_count}”)
return “\n”.join(lines)
def report_overview(client, args):
“””High-level site overview.”””
request = build_request(
metrics=[“totalUsers”, “newUsers”, “sessions”, “screenPageViews”,
“averageSessionDuration”, “engagementRate”, “bounceRate”],
dimensions=[],
days=args.days, start=args.start, end=args.end, limit=1,
)
response = client.run_report(request)
return format_response(response, args.output)
def report_pages(client, args):
“””Top pages by views.”””
request = build_request(
metrics=[“screenPageViews”, “totalUsers”, “averageSessionDuration”],
dimensions=[“pagePath”, “pageTitle”],
days=args.days, start=args.start, end=args.end, limit=args.limit,
order_by_metric=”screenPageViews”,
)
response = client.run_report(request)
return format_response(response, args.output)
def report_sources(client, args):
“””Traffic sources.”””
request = build_request(
metrics=[“sessions”, “totalUsers”, “engagementRate”, “conversions”],
dimensions=[“sessionSource”, “sessionMedium”],
days=args.days, start=args.start, end=args.end, limit=args.limit,
order_by_metric=”sessions”,
)
response = client.run_report(request)
return format_response(response, args.output)
def report_countries(client, args):
“””Geographic breakdown.”””
request = build_request(
metrics=[“sessions”, “totalUsers”, “engagementRate”],
dimensions=[“country”],
days=args.days, start=args.start, end=args.end, limit=args.limit,
order_by_metric=”sessions”,
)
response = client.run_report(request)
return format_response(response, args.output)
def report_devices(client, args):
“””Device category breakdown.”””
request = build_request(
metrics=[“sessions”, “totalUsers”, “engagementRate”],
dimensions=[“deviceCategory”],
days=args.days, start=args.start, end=args.end, limit=args.limit,
order_by_metric=”sessions”,
)
response = client.run_report(request)
return format_response(response, args.output)
def report_daily(client, args):
“””Day-by-day trend.”””
request = build_request(
metrics=[“totalUsers”, “sessions”, “screenPageViews”],
dimensions=[“date”],
days=args.days, start=args.start, end=args.end, limit=args.days or 30,
)
from google.analytics.data_v1beta.types import OrderBy
request.order_bys = [
OrderBy(dimension=OrderBy.DimensionOrderBy(dimension_name=”date”), desc=False)
]
response = client.run_report(request)
return format_response(response, args.output)
def report_realtime(client, args):
“””Realtime active users.”””
from google.analytics.data_v1beta.types import (
RunRealtimeReportRequest, Metric, Dimension
)
request = RunRealtimeReportRequest(
property=f”properties/{PROPERTY_ID}”,
metrics=[Metric(name=”activeUsers”)],
dimensions=[Dimension(name=”unifiedScreenName”)],
limit=args.limit,
)
response = client.run_realtime_report(request)
headers = [h.name for h in response.dimension_headers] + [h.name for h in response.metric_headers]
rows = []
for row in response.rows:
values = [dv.value for dv in row.dimension_values] + [mv.value for mv in row.metric_values]
rows.append(values)
if not rows:
return “No active users right now.”
if args.output == “json”:
result = [dict(zip(headers, row)) for row in rows]
return json.dumps(result, indent=2)
elif args.output == “csv”:
lines = [“,”.join(headers)]
for row in rows:
lines.append(“,”.join(row))
return “\n”.join(lines)
else:
col_widths = [len(h) for h in headers]
for row in rows:
for i, val in enumerate(row):
col_widths[i] = max(col_widths[i], len(str(val)))
separator = “+” + “+”.join(“-” * (w + 2) for w in col_widths) + “+”
header_row = “|” + “|”.join(f” {h:<{col_widths[i]}} ” for i, h in enumerate(headers)) + “|”
lines = [separator, header_row, separator]
for row in rows:
line = “|” + “|”.join(f” {str(v):<{col_widths[i]}} ” for i, v in enumerate(row)) + “|”
lines.append(line)
lines.append(separator)
return “\n”.join(lines)
def report_custom(client, args):
“””Custom query with user-specified metrics and dimensions.”””
if not args.metrics:
return “Error: –metrics required for custom report (comma-separated)”
metrics = [m.strip() for m in args.metrics.split(“,”)]
dimensions = [d.strip() for d in args.dimensions.split(“,”)] if args.dimensions else []
request = build_request(
metrics=metrics,
dimensions=dimensions,
days=args.days, start=args.start, end=args.end, limit=args.limit,
order_by_metric=metrics[0],
)
response = client.run_report(request)
return format_response(response, args.output)
REPORTS = {
“overview”: report_overview,
“pages”: report_pages,
“sources”: report_sources,
“countries”: report_countries,
“devices”: report_devices,
“daily”: report_daily,
“realtime”: report_realtime,
“custom”: report_custom,
}
def main():
parser = argparse.ArgumentParser(description=”Query Google Analytics 4 data”)
parser.add_argument(“–report”, required=True, choices=REPORTS.keys(), help=”Report type”)
parser.add_argument(“–days”, type=int, default=30, help=”Lookback period in days (default: 30)”)
parser.add_argument(“–start”, help=”Start date (YYYY-MM-DD), overrides –days”)
parser.add_argument(“–end”, help=”End date (YYYY-MM-DD), defaults to today”)
parser.add_argument(“–limit”, type=int, default=10, help=”Max rows (default: 10)”)
parser.add_argument(“–output”, choices=[“table”, “json”, “csv”], default=”table”, help=”Output format”)
parser.add_argument(“–metrics”, help=”Comma-separated metrics (for custom report)”)
parser.add_argument(“–dimensions”, help=”Comma-separated dimensions (for custom report)”)
args = parser.parse_args()
try:
client = get_client()
result = REPORTS[args.report](client, args)
print(result)
except Exception as e:
print(f”Error: {e}”, file=sys.stderr)
sys.exit(1)
if __name__ == “__main__”:
main()
```
—
Step 5 — Test that it works
Open your Terminal again. Type the following and press Enter:
python3 ~/.claude/skills/google-analytics/query_script.py –report overview –days 7
If you see a table with numbers, you are done. Those are your real analytics numbers. If you see an error, check the common fixes at the bottom of this article.
Step 6 — Ask Claude
Open Claude Cowork. Type any of these questions naturally:
- “How much traffic did my site get this week?”
- “What are my top pages in the last 30 days?”
- “Where is my traffic coming from?”
- “Show me a daily breakdown of visitors this month”
- “How many people are on my site right now?”
Claude will run the script automatically, pull your real data, and answer you. Available report types you can ask about:
→ Overview (total users, sessions, bounce rate)
→ Top pages
→ Traffic sources
→ Countries
→ Devices (desktop vs mobile)
→ Daily trends
→ Realtime (who is on your site right now)
If something goes wrong
“Permission denied” or “403 error”
→ You skipped or made a mistake in Step 2. Go back and make sure the service account email is added as a Viewer in your Analytics settings.
“No module named google”
→ The package from Step 3 didn’t install properly. Run the pip3 install command again.
“Property not found”
→ Your Property ID in query_script.py has a typo. It should be just the number (e.g. 123456789), nothing else.
“Claude doesn’t understand my question”
→ Try being specific: “Use the google-analytics skill to show me traffic for the last 7 days.”