Claude Code skill pack for Google Analytics 4 — 5 starter skills covering auth (OAuth + service account), Data API v1 queries (runReport, filters, sampling), Realtime API, canonical reports (DAU/MAU/retention/top pages/funnel), and BigQuery export with event-level SQL recipes.
Installation
Open Claude Code and run this command:
/plugin install ga4-pack@claude-code-plugins-plus
Use --global to install for all projects, or --project for current project only.
What It Does
> Claude Code skill pack for Google Analytics 4. 5 starter skills covering the operationally-leveraged paths: auth setup, Data API v1 queries, Realtime API, common reports (DAU / MAU / retention / top pages), and BigQuery export.
Install: /plugin install ga4-pack@claude-code-plugins-plus
Links: Tons of Skills · GA4 Data API v1 docs · GA4 BigQuery export
Skills (5)
Configure auth for the GA4 Data API — OAuth user credentials for interactive use, or a service account for automation / CI.
GA4 Auth Setup
GA4 has two production-grade auth paths. Pick before you start; mixing them mid-flight is the most common failure mode.
| Path | When | Credential file |
|---|---|---|
| Service account | Automation, CI, server-side scripts. Token is long-lived, scoped, revocable. | ~/.config/gcloud/sa-ga4.json (or any path you choose) |
| OAuth user creds | Interactive use, multiple GA4 properties, ad-hoc analyst work. Token refreshes from a ~/.config/gcloud/applicationdefaultcredentials.json file. |
ADC |
Recommendation: service account for any pipeline / report-runner / agent use. OAuth for a human poking around. Don't share OAuth user creds across machines — that's an audit-trail mess.
Path A — Service account (recommended for automation)
1. Create the SA in GCP
PROJECT=your-gcp-project # the project that will own the SA
SA_NAME=ga4-reader
SA_EMAIL="${SA_NAME}@${PROJECT}.iam.gserviceaccount.com"
gcloud iam service-accounts create "$SA_NAME" \
--display-name="GA4 read-only API access" \
--project="$PROJECT"
# Generate a key (file lands locally)
gcloud iam service-accounts keys create ~/.config/gcloud/sa-ga4.json \
--iam-account="$SA_EMAIL"
2. Grant the SA access to your GA4 property
This is the step everyone forgets. GA4 has property-level access control that lives in the Google Analytics web UI, NOT in GCP IAM. The service account email needs to be added there.
- Open
- Admin (bottom-left gear) → Property column → Property Access Management
- Add user: paste
$SA_EMAIL(e.g.ga4-reader@your-project.iam.gserviceaccount.com) - Role: Viewer (read-only — anything more is over-privilege)
- Save
3. Enable the Data API in the SA's project
gcloud services enable analyticsdata.googleapis.com --project="$PROJECT"
4. Test the auth round-trip
GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/sa-ga4.json \
PROPERTY_ID=123456789 \
python3 -c "
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import RunReportRequest, DateRange, Metric, Dimension
import os
client = BetaAnalyticsDataClient()
req = RunReportRequest(
property=f'properties/{os.environ[\"PROPERTY_ID\"]}',
date_ranges=[DateRange(start_date='7daysAgo', end_date='today')],
metrics=[Metric(name='activeUsers')],
dimensions=[Dimension(name='date')],
)
resp = clientWire GA4 → BigQuery for unsampled, queryable event-level data.
GA4 → BigQuery Export
The Data API is good but bounded — sampled past a threshold, capped at ~150 dimensions, no cohort joins. BigQuery export gives you the raw event stream as SQL-queryable tables, free at the standard GA4 tier (up to 1M events/day), with no sampling and full event payloads.
This skill: one-time setup, then the SQL recipes for what the Data API can't do well.
Setup — one time
1. Link your GA4 property to a GCP project
In
- Admin → Property column → BigQuery Links
- Create a link → pick your GCP project
- Data location: pick the BQ region for the export tables (US multi-region is fine for most cases; EU if you need data residency)
- Export type:
- Daily — single
events_YYYYMMDDtable per day, written ~24h after midnight. Fine for most reporting. - Streaming —
eventsintradayYYYYMMDDtable written ~near-real-time. Costs more, useful for hot ops dashboards. - Most setups: enable both. Streaming for "today", daily for everything else.
- Include advertising identifiers — uncheck unless you specifically need device-graph data (most don't)
- Save
The first daily table lands within 24h. The first streaming table is near-immediate. After that you have a new events_YYYYMMDD every day forever, no maintenance needed.
2. Verify the export is working
PROJECT=your-gcp-project
DATASET=analytics_123456789 # auto-named after the property ID
bq ls "$PROJECT:$DATASET" 2>&1 | head -10
# Expect: events_YYYYMMDD tables + events_intraday_YYYYMMDD if streaming enabled
If you see no dataset, the link is configured but the first export hasn't fired yet — wait 24h.
3. Authorize a service account for querying
The SA from ga4-auth-setup only has Data API access. For BQ queries, grant the same SA:
SA_EMAIL=ga4-reader@your-project.iam.gserviceaccount.com
gcloud projects add-iam-policy-binding "$PROJECT" \
--member="serviceAccount:$SA_EMAIL" \
--role="roles/bigquery.dataViewer"
gcloud projects add-iam-policy-binding "$PROJECT" \
--member="serviceAccount:$SA_EMAIL" \
--role="roles/bigquery.jobUser"
dataViewer reads tables; jobUser lets the SA run queries (queries are jobs in BQ's model).
The events table schema (the important columns)
Every row in events_YYYYMMDD is one event. The schema is denormalized — user + session + event + page + device all flat in each row.
| Column | Type | Notes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Metric | What it counts | Notes | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
activeUsers |
Unique users with engagement in the window | The "users" people mean by default | ||||||||||||||||||||||||||||||||
newUsers |
First-seen users in the window | |||||||||||||||||||||||||||||||||
totalUsers |
All users (engaged or not) — superset of activeUsers |
|||||||||||||||||||||||||||||||||
sessions |
Sessions started in the window | Re-engages after 30min inactivity | ||||||||||||||||||||||||||||||||
engagedSessions |
Sessions ≥10s OR ≥2 pageviews OR ≥1 conversion | The "good" sessions | ||||||||||||||||||||||||||||||||
screenPageViews |
Pageviews + app screenviews combined | What people mean by "pageviews" | ||||||||||||||||||||||||||||||||
eventCount |
Total event count (every event, not just page_view) |
Often misleadingly large | ||||||||||||||||||||||||||||||||
bounceRate |
(sessions - engagedSessions) / sessions |
Lower is better | ||||||||||||||||||||||||||||||||
averageSessionDuration |
Avg seconds per session | Across sessions, not engagedSessions |
||||||||||||||||||||||||||||||||
eventsPerSession |
eventCount / sessions |
|||||||||||||||||||||||||||||||||
conversions |
Events flagged as conversions in the property setup | Property-specific | ||||||||||||||||||||||||||||||||
totalRevenue |
Sum of purchase e
Pull current-session / active-user data from the GA4 Realtime endpoint — a separate API surface from runReport with different metrics, dimensions, and freshness guarantees (~30 min rolling window instead of T-48h).
Bash(python3:*)Bash(curl:*)
GA4 Realtime APIThe Realtime API is GA4's "what's happening right now" endpoint. Different from
Don't try to use Minimum viable call
No Realtime metrics (the full list)
Custom-event aggregates (e.g. Realtime dimensions (the full list) |