Skills google-analytics-cli
git clone https://github.com/openclaw/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/bin-huang/google-analytics-cli" ~/.claude/skills/openclaw-skills-google-analytics-cli && rm -rf "$T"
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/bin-huang/google-analytics-cli" ~/.openclaw/skills/openclaw-skills-google-analytics-cli && rm -rf "$T"
skills/bin-huang/google-analytics-cli/SKILL.mdGoogle Analytics CLI Skill
You have access to
google-analytics-cli, a read-only CLI for the GA4 API. Use it to run reports, monitor realtime activity, and explore account/property structure.
Quick start
# Check if the CLI is available google-analytics-cli --help # List accounts and properties google-analytics-cli accounts
If the CLI is not installed, install it:
npm install -g google-analytics-cli
Authentication
The CLI uses Google service account credentials. Credentials are resolved in this order:
flag (per-command)--credentials <path>
env varGOOGLE_APPLICATION_CREDENTIALS
(auto-detected)~/.config/google-analytics-cli/credentials.json- gcloud Application Default Credentials
Before running any command, verify credentials by running
google-analytics-cli accounts. If it fails, ask the user to set up a service account and grant it Viewer access in Google Analytics.
Property ID
Most commands require a GA4 property ID. It can be provided as:
- A positional argument:
google-analytics-cli report 123456789 ... - The
flag:--propertygoogle-analytics-cli report --property 123456789 ... - The
env var:GA_PROPERTY_IDexport GA_PROPERTY_ID=123456789
Both raw numbers (
123456789) and prefixed format (properties/123456789) are accepted.
Output format
All commands output pretty-printed JSON by default. Use
--format compact for single-line JSON.
Admin commands
# List all accounts and their properties google-analytics-cli accounts # Get details about a specific property google-analytics-cli property 123456789 # List properties for an account google-analytics-cli properties 987654321 google-analytics-cli properties 987654321 --show-deleted # List data streams for a property google-analytics-cli data-streams 123456789 # List key events (conversions) for a property google-analytics-cli key-events 123456789 # List custom dimensions for a property (Admin API) google-analytics-cli admin-custom-dimensions 123456789 # List custom metrics for a property (Admin API) google-analytics-cli admin-custom-metrics 123456789 # Get data retention settings for a property google-analytics-cli data-retention 123456789 # List Google Ads links for a property google-analytics-cli ads-links 123456789 # List annotations for a property (alpha API) google-analytics-cli annotations 123456789 # Get custom dimensions and metrics for a property (Data API, filters by customDefinition) google-analytics-cli custom-dims 123456789
Change history
Search change history events for an account:
google-analytics-cli change-history 987654321 google-analytics-cli change-history 987654321 --filter-property 123456789 google-analytics-cli change-history 987654321 --earliest-change-time 2026-01-01T00:00:00Z google-analytics-cli change-history 987654321 --earliest-change-time 2026-01-01T00:00:00Z --latest-change-time 2026-03-01T00:00:00Z google-analytics-cli change-history 987654321 --actor-email user@example.com google-analytics-cli change-history 987654321 --resource-type '["PROPERTY","DATA_STREAM"]' google-analytics-cli change-history 987654321 --action '["CREATED","UPDATED"]'
Access report
Run an access report to see who accessed the property:
google-analytics-cli access-report 123456789 \ --dimensions "accessorEmail,accessMechanism" \ --metrics "accessCount" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]'
Options:
--dimension-filter, --metric-filter, --order-by, --limit, --offset, --time-zone, --return-entity-quota, --include-all-users, --expand-groups.
Note: Access report uses
dimensionName/metricName (not name) internally and entity (not property).
Running reports
The
report command runs a GA4 Data API report.
google-analytics-cli report <property_id> \ --dimensions "<comma-separated>" \ --metrics "<comma-separated>" \ --date-ranges '<JSON array>' \ [--dimension-filter '<JSON>'] \ [--metric-filter '<JSON>'] \ [--order-by '<JSON array>'] \ [--limit <n>] \ [--offset <n>] \ [--currency-code <ISO4217>] \ [--keep-empty-rows] \ [--return-property-quota]
Required options:
--dimensions, --metrics, --date-ranges.
Date ranges
The
--date-ranges option takes a JSON array of date range objects:
# Relative dates --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' # Absolute dates --date-ranges '[{"startDate": "2026-01-01", "endDate": "2026-01-31"}]' # Compare two periods --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday", "name": "current"}, {"startDate": "60daysAgo", "endDate": "31daysAgo", "name": "previous"}]'
Valid date values:
- Absolute:
(e.g.YYYY-MM-DD
)2026-01-01 - Relative:
(e.g.NdaysAgo
,30daysAgo
)7daysAgo - Special:
,yesterdaytoday - Up to 4 date ranges per request
Dimension filters
The
--dimension-filter option takes a JSON FilterExpression:
# Exact match --dimension-filter '{"filter": {"fieldName": "country", "stringFilter": {"matchType": "EXACT", "value": "United States"}}}' # Contains --dimension-filter '{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "CONTAINS", "value": "/blog"}}}' # Regex --dimension-filter '{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "PARTIAL_REGEXP", "value": "^/products/[0-9]+"}}}' # In list --dimension-filter '{"filter": {"fieldName": "eventName", "inListFilter": {"values": ["page_view", "scroll", "click"]}}}' # AND group --dimension-filter '{"andGroup": {"expressions": [{"filter": {"fieldName": "country", "stringFilter": {"matchType": "EXACT", "value": "United States"}}}, {"filter": {"fieldName": "deviceCategory", "stringFilter": {"matchType": "EXACT", "value": "mobile"}}}]}}' # OR group --dimension-filter '{"orGroup": {"expressions": [{"filter": {"fieldName": "country", "stringFilter": {"matchType": "EXACT", "value": "United States"}}}, {"filter": {"fieldName": "country", "stringFilter": {"matchType": "EXACT", "value": "Canada"}}}]}}' # NOT --dimension-filter '{"notExpression": {"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "EXACT", "value": "/"}}}}'
StringFilter matchType values:
EXACT, BEGINS_WITH, ENDS_WITH, CONTAINS, FULL_REGEXP, PARTIAL_REGEXP.
Metric filters
The
--metric-filter option takes the same FilterExpression structure but with numeric filters:
# Greater than --metric-filter '{"filter": {"fieldName": "sessions", "numericFilter": {"operation": "GREATER_THAN", "value": {"int64Value": "100"}}}}' # Between --metric-filter '{"filter": {"fieldName": "bounceRate", "betweenFilter": {"fromValue": {"doubleValue": 0.5}, "toValue": {"doubleValue": 0.9}}}}'
NumericFilter operations:
EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL.
NumericValue: use
{"int64Value": "string"} for integers or {"doubleValue": number} for decimals.
Ordering
The
--order-by option takes a JSON array:
# By metric descending --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}]' # By dimension alphabetically --order-by '[{"dimension": {"dimensionName": "date", "orderType": "ALPHANUMERIC"}}]' # Multiple sort keys --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}, {"dimension": {"dimensionName": "country"}}]'
DimensionOrderBy orderType values:
ALPHANUMERIC, CASE_INSENSITIVE_ALPHANUMERIC, NUMERIC.
Pivot reports
Run a pivot report for cross-tabulation analysis:
google-analytics-cli pivot-report 123456789 \ --dimensions "country,browser" \ --metrics "sessions,activeUsers" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --pivots '[{"fieldNames": ["browser"], "limit": 5, "orderBys": [{"metric": {"metricName": "sessions"}, "desc": true}]}]'
Options:
--dimension-filter, --metric-filter, --currency-code, --keep-empty-rows, --return-property-quota.
Batch reports
Run up to 5 reports in a single request:
google-analytics-cli batch-report 123456789 \ --requests '[{"dimensions": [{"name": "date"}], "metrics": [{"name": "activeUsers"}], "dateRanges": [{"startDate": "7daysAgo", "endDate": "yesterday"}]}, {"dimensions": [{"name": "country"}], "metrics": [{"name": "sessions"}], "dateRanges": [{"startDate": "7daysAgo", "endDate": "yesterday"}]}]'
Metadata and compatibility
# Get full metadata (all available dimensions and metrics) google-analytics-cli metadata 123456789 # Check if dimensions and metrics are compatible google-analytics-cli check-compatibility 123456789 \ --dimensions "date,country" \ --metrics "sessions,activeUsers"
Options:
--dimension-filter, --metric-filter (same FilterExpression format as report).
Audience exports
# Create an audience export google-analytics-cli audience-export-create 123456789 \ --audience "properties/123456789/audiences/456" \ --dimensions "deviceId,userId" # Get status of an audience export google-analytics-cli audience-export 123456789 properties/123456789/audienceExports/789 # List all audience exports google-analytics-cli audience-exports 123456789 # Query rows from a completed audience export google-analytics-cli audience-export-query 123456789 properties/123456789/audienceExports/789 \ --limit 1000 --offset 0
Realtime reports
The
realtime command monitors current activity. No date ranges or currency code.
google-analytics-cli realtime <property_id> \ --dimensions "<comma-separated>" \ --metrics "<comma-separated>" \ [--dimension-filter '<JSON>'] \ [--metric-filter '<JSON>'] \ [--order-by '<JSON array>'] \ [--limit <n>] \ [--return-property-quota]
Realtime reports cover the last 30 minutes of activity.
Realtime dimensions (limited set)
appVersion, audienceId, audienceName, audienceResourceName, city, cityId, country, countryId, deviceCategory, eventName, minutesAgo, platform, streamId, streamName, unifiedScreenName
Note:
minutesAgo is "00" for the current minute, "01" for the previous, up to "29".
Realtime metrics (4 only)
activeUsers, eventCount, keyEvents, screenPageViews
Common dimensions reference
Traffic source (session-scoped):
sessionSource, sessionMedium, sessionSourceMedium, sessionDefaultChannelGroup, sessionCampaignName, sessionCampaignId
Traffic source (first-user acquisition):
firstUserSource, firstUserMedium, firstUserSourceMedium, firstUserDefaultChannelGroup, firstUserCampaignName
Page/Screen:
pagePath, pageTitle, pagePathPlusQueryString, landingPage, landingPagePlusQueryString, hostName, fullPageUrl, contentGroup
Event:
eventName, isKeyEvent
User:
newVsReturning, userAgeBracket, userGender, firstSessionDate
Geography:
country, countryId, city, cityId, region, continent, language
Device:
deviceCategory, browser, operatingSystem, operatingSystemVersion, screenResolution, mobileDeviceBranding, platform
Time:
date, dateHour, day, dayOfWeek, dayOfWeekName, hour, month, week, year, yearMonth, nthDay
Ecommerce:
transactionId, itemName, itemId, itemCategory, itemBrand, itemVariant, orderCoupon
Google Ads:
sessionGoogleAdsCampaignName, sessionGoogleAdsAdGroupName, sessionGoogleAdsKeyword, sessionGoogleAdsAdNetworkType, googleAdsAccountName
Audience:
audienceId, audienceName
Custom dimensions:
customEvent:parameter_name (event-scoped), customUser:parameter_name (user-scoped), customItem:parameter_name (item-scoped)
Common metrics reference
User:
activeUsers, newUsers, totalUsers
Session:
sessions, sessionsPerUser, averageSessionDuration, bounceRate
Engagement:
engagedSessions, engagementRate, screenPageViews, screenPageViewsPerSession, screenPageViewsPerUser, userEngagementDuration
Event:
eventCount, eventCountPerUser, eventValue, eventsPerSession
Key events (conversions):
keyEvents, sessionKeyEventRate, userKeyEventRate
Ecommerce/Revenue:
ecommercePurchases, purchaseRevenue, totalRevenue, transactions, averagePurchaseRevenue, averageRevenuePerUser, itemRevenue, addToCarts, checkouts, cartToViewRate
Advertising:
advertiserAdClicks, advertiserAdCost, advertiserAdCostPerClick, advertiserAdImpressions, returnOnAdSpend
Custom metrics:
customEvent:parameter_name (sum), averageCustomEvent:parameter_name (average), countCustomEvent:parameter_name (count)
Report examples
Traffic overview (last 30 days):
google-analytics-cli report 123456789 \ --dimensions "sessionDefaultChannelGroup" \ --metrics "sessions,activeUsers,engagementRate,keyEvents,totalRevenue" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}]'
Top pages by views:
google-analytics-cli report 123456789 \ --dimensions "pagePath" \ --metrics "screenPageViews,activeUsers,averageSessionDuration,bounceRate" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "screenPageViews"}, "desc": true}]' \ --limit 20
Daily trend:
google-analytics-cli report 123456789 \ --dimensions "date" \ --metrics "activeUsers,sessions,screenPageViews,keyEvents" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"dimension": {"dimensionName": "date"}}]'
Traffic source breakdown:
google-analytics-cli report 123456789 \ --dimensions "sessionSource,sessionMedium" \ --metrics "sessions,activeUsers,engagementRate,bounceRate,keyEvents" \ --date-ranges '[{"startDate": "7daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}]' \ --limit 20
Landing page performance:
google-analytics-cli report 123456789 \ --dimensions "landingPage" \ --metrics "sessions,activeUsers,bounceRate,keyEvents,averageSessionDuration" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}]' \ --limit 20
Device breakdown:
google-analytics-cli report 123456789 \ --dimensions "deviceCategory" \ --metrics "activeUsers,sessions,engagementRate,bounceRate,screenPageViews" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]'
Geography:
google-analytics-cli report 123456789 \ --dimensions "country" \ --metrics "activeUsers,sessions,engagementRate,keyEvents" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "activeUsers"}, "desc": true}]' \ --limit 20
Event analysis:
google-analytics-cli report 123456789 \ --dimensions "eventName" \ --metrics "eventCount,eventCountPerUser,eventValue" \ --date-ranges '[{"startDate": "7daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "eventCount"}, "desc": true}]' \ --limit 20
Period comparison:
google-analytics-cli report 123456789 \ --dimensions "sessionDefaultChannelGroup" \ --metrics "sessions,activeUsers,keyEvents" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday", "name": "current"}, {"startDate": "60daysAgo", "endDate": "31daysAgo", "name": "previous"}]' \ --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}]'
Ecommerce performance:
google-analytics-cli report 123456789 \ --dimensions "itemName" \ --metrics "itemRevenue,itemsPurchased,addToCarts,cartToViewRate" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "itemRevenue"}, "desc": true}]' \ --limit 20
New vs returning users:
google-analytics-cli report 123456789 \ --dimensions "newVsReturning" \ --metrics "activeUsers,sessions,engagementRate,screenPageViews,keyEvents" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]'
User acquisition (first-touch):
google-analytics-cli report 123456789 \ --dimensions "firstUserDefaultChannelGroup" \ --metrics "newUsers,activeUsers,sessions,keyEvents,totalRevenue" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "newUsers"}, "desc": true}]'
Campaign (UTM) performance:
google-analytics-cli report 123456789 \ --dimensions "sessionCampaignName,sessionSourceMedium" \ --metrics "sessions,activeUsers,engagementRate,keyEvents,totalRevenue" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "sessions"}, "desc": true}]' \ --limit 20
Google Ads campaign performance:
google-analytics-cli report 123456789 \ --dimensions "sessionGoogleAdsCampaignName" \ --metrics "sessions,keyEvents,advertiserAdCost,advertiserAdClicks,returnOnAdSpend" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --order-by '[{"metric": {"metricName": "advertiserAdCost"}, "desc": true}]'
Filtered report (blog pages only, US mobile users):
google-analytics-cli report 123456789 \ --dimensions "pagePath" \ --metrics "screenPageViews,activeUsers,engagementRate" \ --date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \ --dimension-filter '{"andGroup": {"expressions": [{"filter": {"fieldName": "pagePath", "stringFilter": {"matchType": "BEGINS_WITH", "value": "/blog"}}}, {"filter": {"fieldName": "country", "stringFilter": {"matchType": "EXACT", "value": "United States"}}}, {"filter": {"fieldName": "deviceCategory", "stringFilter": {"matchType": "EXACT", "value": "mobile"}}}]}}' \ --order-by '[{"metric": {"metricName": "screenPageViews"}, "desc": true}]' \ --limit 20
Realtime -- active users by country:
google-analytics-cli realtime 123456789 \ --dimensions "country" \ --metrics "activeUsers" \ --order-by '[{"metric": {"metricName": "activeUsers"}, "desc": true}]'
Realtime -- active pages:
google-analytics-cli realtime 123456789 \ --dimensions "unifiedScreenName" \ --metrics "activeUsers,screenPageViews" \ --order-by '[{"metric": {"metricName": "activeUsers"}, "desc": true}]' \ --limit 10
Important rules
-
Dimension/metric compatibility. Not all dimensions and metrics can be used together. If you get a compatibility error, reduce the number of dimensions or try different combinations. Use the
command to test before running a report, or usecheck-compatibility
to see all available dimensions and metrics.metadata -
Max 9 dimensions per report request.
-
Max 4 date ranges per report request.
-
Max 250,000 rows per response. Use
and--offset
for pagination.--limit -
Realtime is limited. Only 15 dimensions and 4 metrics. No date ranges, no currency code, no offset. Covers the last 30 minutes.
-
Thresholded dimensions.
,userAgeBracket
, anduserGender
/audienceId
may return thresholded (sampled) data for privacy when row counts are too low.audienceName -
Quota. Use
to check current quota status. Standard properties get 200,000 tokens/day.--return-property-quota -
Filter scope.
only accepts dimension field names.--dimension-filter
only accepts metric field names. They are applied independently by the API.--metric-filter -
Batch reports. Max 5 report objects per batch request.
-
Audience exports. Creating an export returns a long-running operation. Poll with
until state isaudience-export
, then query rows withACTIVE
.audience-export-query
Workflow guidance
Quick overview
- Run
to find propertiesgoogle-analytics-cli accounts - Run a traffic overview report with
and key metricssessionDefaultChannelGroup - Present trends with daily date dimension
Deep analysis
- Start broad: traffic channels and top-level metrics
- Drill down: specific pages, events, or sources that underperform
- Compare periods to identify trends
- Check device and geography breakdowns for segment-specific issues
- Use
to isolate specific segments--dimension-filter
Property exploration
- Run
to see all propertiesgoogle-analytics-cli properties <account_id> - Run
to see data streamsgoogle-analytics-cli data-streams <property_id> - Run
to see configured conversionsgoogle-analytics-cli key-events <property_id> - Run
for all available dimensions and metricsgoogle-analytics-cli metadata <property_id> - Run
before complex reportsgoogle-analytics-cli check-compatibility <property_id>
Error handling
- Permission denied -- the service account needs Viewer access in Google Analytics
- Incompatible dimensions/metrics -- use
to test, reduce dimensions or try different combinationscheck-compatibility - Empty results -- check property ID, date range, and whether data exists for those dimensions
- Quota exceeded -- use
to check usage; reduce request frequency or complexity--return-property-quota