
Google Analytics Cli
- 75 installs
- 29 repo stars
- Updated May 8, 2026
- bin-huang/google-analytics-cli
google-analytics-cli is a skill wrapping a read-only GA4 API CLI to run reports, monitor realtime users and explore account and property structure, returning JSON.
About
This skill drives google-analytics-cli, a read-only CLI for the GA4 API. A developer uses it to run Data API reports with dimensions, metrics and date ranges, monitor realtime users, and explore accounts, properties, data streams and key events. It documents service-account authentication, multi-account profiles, and property-ID handling, returning pretty-printed JSON. It is invoked to check GA4 traffic, sessions, engagement or user behavior.
- Read-only CLI over the GA4 API for reports, realtime users and account/property structure
- Runs GA4 Data API reports with dimensions, metrics, date ranges and filters, output as JSON
- Supports multi-account profiles, change history, access reports and admin custom dimensions/metrics
Google Analytics Cli by the numbers
- 75 all-time installs (skills.sh)
- Ranked #1,202 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 1, 2026 (Skillselion catalog sync)
google-analytics-cli capabilities & compatibility
CLI is free (npm); requires a Google service account with GA4 Viewer access.
- Capabilities
- web analytics · traffic reporting · realtime monitoring
- Works with
- gcp
- Use cases
- data analysis · marketing · seo
- Runs
- Runs locally
- Pricing
- Bring your own API key
What google-analytics-cli says it does
You have access to `google-analytics-cli`, a read-only CLI for the GA4 API.
The CLI uses Google service account credentials.
The `report` command runs a GA4 Data API report.
npx skills add https://github.com/bin-huang/google-analytics-cli --skill google-analytics-cliAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 75 |
|---|---|
| repo stars | ★ 29 |
| Last updated | May 8, 2026 |
| Repository | bin-huang/google-analytics-cli ↗ |
What it does
Run GA4 reports with dimensions and metrics, monitor realtime users, and explore property structure from a read-only CLI.
Who is it for?
Read-only GA4 reporting and property exploration from the command line.
Skip if: Editing GA4 configuration; it grants Viewer-level read access only.
When should I use this skill?
The user wants GA4 traffic, active users, page views, traffic sources, sessions, engagement, realtime users or bounce rate.
What you get
JSON GA4 reports, realtime user counts, and account/property/stream metadata on demand.
- GA4 report JSON
- realtime user data
- account/property/data-stream metadata
By the numbers
- up to 4 date ranges per request
- 5-step credential resolution order
Files
Google 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 accountsIf the CLI is not installed, install it:
npm install -g google-analytics-cliAuthentication
The CLI uses Google service account credentials. Credentials are resolved in this order:
1. --credentials <path> flag (per-command) 2. --profile <name> flag or GA_PROFILE env var → ~/.config/google-analytics-cli/profiles/<name>.json 3. GOOGLE_APPLICATION_CREDENTIALS env var 4. ~/.config/google-analytics-cli/credentials.json (auto-detected) 5. 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.
If the user manages multiple GA accounts (e.g. several clients or organizations), run google-analytics-cli profiles to list available profiles, then pass --profile <name> (e.g. accounts --profile account-a) to target a specific one. --credentials and --profile cannot be used together.
Property ID
Most commands require a GA4 property ID. It can be provided as:
- A positional argument:
google-analytics-cli report 123456789 ... - The
--propertyflag:google-analytics-cli report --property 123456789 ... - The
GA_PROPERTY_IDenv var:export 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
# List configured credentials profiles (for users with multiple GA accounts)
google-analytics-cli profilesChange 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:
YYYY-MM-DD(e.g.2026-01-01) - Relative:
NdaysAgo(e.g.30daysAgo,7daysAgo) - Special:
yesterday,today - 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 0Realtime 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 20Daily 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 20Landing 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 20Device 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 20Event 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 20Period 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 20New 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 20Google 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 20Realtime -- 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 10Important rules
1. 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 check-compatibility command to test before running a report, or use metadata to see all available dimensions and metrics.
2. Max 9 dimensions per report request.
3. Max 4 date ranges per report request.
4. Max 250,000 rows per response. Use --offset and --limit for pagination.
5. Realtime is limited. Only 15 dimensions and 4 metrics. No date ranges, no currency code, no offset. Covers the last 30 minutes.
6. Thresholded dimensions. userAgeBracket, userGender, and audienceId/audienceName may return thresholded (sampled) data for privacy when row counts are too low.
7. Quota. Use --return-property-quota to check current quota status. Standard properties get 200,000 tokens/day.
8. Filter scope. --dimension-filter only accepts dimension field names. --metric-filter only accepts metric field names. They are applied independently by the API.
9. Batch reports. Max 5 report objects per batch request.
10. Audience exports. Creating an export returns a long-running operation. Poll with audience-export until state is ACTIVE, then query rows with audience-export-query.
Workflow guidance
Quick overview
1. Run google-analytics-cli accounts to find properties 2. Run a traffic overview report with sessionDefaultChannelGroup and key metrics 3. Present trends with daily date dimension
Deep analysis
1. Start broad: traffic channels and top-level metrics 2. Drill down: specific pages, events, or sources that underperform 3. Compare periods to identify trends 4. Check device and geography breakdowns for segment-specific issues 5. Use --dimension-filter to isolate specific segments
Property exploration
1. Run google-analytics-cli properties <account_id> to see all properties 2. Run google-analytics-cli data-streams <property_id> to see data streams 3. Run google-analytics-cli key-events <property_id> to see configured conversions 4. Run google-analytics-cli metadata <property_id> for all available dimensions and metrics 5. Run google-analytics-cli check-compatibility <property_id> before complex reports
Error handling
- Permission denied -- the service account needs Viewer access in Google Analytics
- Incompatible dimensions/metrics -- use
check-compatibilityto test, reduce dimensions or try different combinations - Empty results -- check property ID, date range, and whether data exists for those dimensions
- Quota exceeded -- use
--return-property-quotato check usage; reduce request frequency or complexity
API documentation references
Related skills
FAQ
How does it authenticate?
It uses a Google service account with Viewer access, resolved from a flag, profile, env var, config file or gcloud ADC.
Can it handle multiple GA accounts?
Yes, it supports named profiles; run 'profiles' and pass --profile <name> to target a specific account.