Google Analytics CLI & Skills for AI agents (and humans). Run custom reports with flexible dimensions and date ranges, monitor realtime active users, manage custom metrics from the GA4 API, and more.
Works with: OpenClaw, Claude Code, Cursor, Codex, and any agent that can run shell commands.
Tell your AI agent (e.g. OpenClaw):
Install this CLI and skills from https://github.com/Bin-Huang/google-analytics-cli
Or install manually:
npm install -g google-analytics-cli
# Add skills for AI agents (Claude Code, Cursor, Codex, etc.)
npx skills add Bin-Huang/google-analytics-cliOr run directly: npx google-analytics-cli --help
For development:
pnpm install
pnpm buildBuilt on Google's official APIs. Handles service account authentication and request signing. Every command outputs structured JSON to stdout, ready for agents to parse without extra processing.
- GA4 Admin API — account/property management, data streams, key events, change history, access reports
- GA4 Data API — standard/pivot/batch reports, realtime, audience exports, metadata
Under the hood it uses the official Node.js client libraries @google-analytics/admin and @google-analytics/data. All API responses are passed through as JSON — no transformation or aggregation.
- Open the Google Cloud Console and select a project. If you do not have a project, create one.
- Enable both APIs in the API Library. The CLI needs both:
- Google Analytics Admin API: used by
accounts,property,properties,change-history,access-report, and other admin commands. - Google Analytics Data API: used by
report,realtime,metadata, audience exports, and other reporting commands.
- Google Analytics Admin API: used by
- Go to IAM & Admin > Service Accounts in the same project.
- Click Create Service Account, give it a name (e.g.
analytics-reader), and click Done. - Click the new service account and open the Keys tab.
- Click Add Key > Create new key > JSON. The browser downloads the key file.
Note
Key creation blocked? Your organization can block service account key creation with the constraints/iam.managed.disableServiceAccountKeyCreation policy. Organizations created on or after May 3, 2024 enforce this policy by default. See Secure-by-default organizations and Create and delete service account keys.
To fix this, ask your organization admin to override the policy for this project. Or use Application Default Credentials instead of a key.
The CLI looks for credentials in this order and uses the first one it finds:
--credentials <path>flag--profile <name>flag (orGA_CLI_PROFILEenv var) →~/.config/google-analytics-cli/profiles/<name>.json(see Managing multiple GA accounts)GOOGLE_APPLICATION_CREDENTIALSenv var~/.config/google-analytics-cli/credentials.json(default path)- gcloud Application Default Credentials (see below)
Choose one of these options:
# Option A: Default path (recommended)
mkdir -p ~/.config/google-analytics-cli
cp ~/Downloads/your-key-file.json ~/.config/google-analytics-cli/credentials.json
# Option B: Environment variable
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your-key-file.json"
# Option C: Pass per command
google-analytics-cli accounts --credentials /path/to/your-key-file.json--credentials and --profile cannot be used together; --credentials overrides GA_CLI_PROFILE.
The service account needs a role on each Google Analytics account or property. You must be an Administrator of the account or property to add users to it.
- Open Google Analytics.
- Go to Admin (gear icon at bottom-left).
- Under Account settings, click Account access management. For a single property, use Property access management under Property settings instead.
- Click + > Add users.
- Enter the service account email. Copy it from the
client_emailfield in your key file, e.g.my-sa@my-project.iam.gserviceaccount.com. - Choose a role:
- Viewer (read-only): enough for all commands except
change-historyandaccess-report. - Editor or higher: needed for
change-history. - Administrator: needed for
access-report.
- Viewer (read-only): enough for all commands except
- Click Add.
A role at the Account level applies to all properties under that account. A role at the Property level applies to that property only.
If Google Analytics shows "Email not found" or "This email doesn't match a Google Account", see Troubleshooting.
google-analytics-cli accountsThe command prints the accounts and properties that the credentials can access. If it prints [], the access from Step 4 is not active yet. The CLI also writes a warning to stderr in this case. See Troubleshooting.
Use this method to sign in with your own Google account instead of a service account. Use it when Google Analytics does not accept the service account email, or when your organization blocks key creation. The CLI then has the same Google Analytics access as your Google account.
Before you start:
- Install the gcloud CLI.
- In the project from Step 1, configure the OAuth consent screen. If you are not in a Google Workspace organization, choose the External audience.
- With the External audience, add your own Google account as a test user on the Audience page.
Steps:
-
Go to Google Auth Platform > Clients and click Create client.
-
Choose the application type Desktop app, then click Create.
-
Download the client JSON file, e.g. as
client_secret.json. This file is not a service account key. Do not pass it to--credentials. -
Sign in with your own OAuth client:
gcloud auth application-default login \ --client-id-file=/path/to/client_secret.json \ --scopes="https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform" -
Bill API usage to the project from Step 1:
gcloud auth application-default set-quota-project YOUR_PROJECT_ID
-
Run
google-analytics-cli accountsto verify.
Notes:
- gcloud requires the
cloud-platformscope. Forchange-history, also addhttps://www.googleapis.com/auth/analytics.editto--scopes. - Without
--client-id-file, gcloud uses its own OAuth client. Google usually blocks that client for the Analytics scopes and shows "This app is blocked". - The CLI uses Application Default Credentials only when it finds no key file (see the order in Step 3). Unset
GOOGLE_APPLICATION_CREDENTIALSandGA_CLI_PROFILE, and remove or rename~/.config/google-analytics-cli/credentials.jsonif it exists. - While the OAuth app uses the External audience and its publishing status is Testing, Google expires the refresh token after 7 days (Google docs). Run the login command again every 7 days.
- This method is good for local development. For automation, use a service account.
| Symptom | Cause | Fix |
|---|---|---|
| Google Analytics shows "Email not found" when you add the service account | You entered a different value, such as client_id or an OAuth client ID. |
Enter the client_email value from the key file. It ends in .iam.gserviceaccount.com. Make sure the service account is enabled in the Cloud Console. |
Google Analytics shows "Email not found" for the correct client_email |
This is a known Google-side problem. Google Analytics can reject a new service account for several hours to several weeks (forum thread). The CLI cannot fix it. | Try again later. To start now, use Application Default Credentials. Some users report that the Admin API can add the service account while the UI rejects it. See Add the service account with the Admin API. |
accounts prints [] (stderr has a warning) |
The credentials work, but they have no access to any account or property yet. | Do Step 4. Check that you added the same client_email that is in your key file. With Application Default Credentials, check that your Google account has access in Google Analytics. |
{"error": "No credentials found. ..."} |
The CLI found no key file and no Application Default Credentials. | Place the key file as described in Step 3. |
... is an OAuth client file, not a service account key |
The credentials file is an OAuth client file. It has an installed or web field. |
Download a service account key from the Keys tab (Step 2). Or use the OAuth client file with gcloud --client-id-file (Application Default Credentials). |
403 with SERVICE_DISABLED or "has not been used in project" |
The Admin API or the Data API is not enabled in the project of the credentials (or the quota project for Application Default Credentials). The error message names the API. | Enable the Admin API or the Data API in that project. Wait a few minutes, then try again. |
403 "User does not have sufficient permissions for this property" (or PERMISSION_DENIED) |
The credentials have no access to this property, or their role is too low for the command. | Add the account to the property (Step 4). change-history needs Editor or higher. access-report needs Administrator. Check that you passed the numeric property ID, not a G- measurement ID. |
Google shows "This app is blocked" during gcloud auth application-default login |
gcloud's own OAuth client cannot request the Analytics scopes. | Create your own OAuth client and add --client-id-file (Application Default Credentials). |
Application Default Credentials stop working after 7 days (invalid_grant) |
The OAuth app uses the External audience and is in Testing status. Google expires its refresh tokens after 7 days. | Run gcloud auth application-default login again with the same flags. |
A service account key fails with 16 UNAUTHENTICATED: Request had invalid authentication credentials |
The key or the service account was deleted or disabled. | Create a new key (Step 2) and replace the old key file. If you deleted the service account, create a new one and repeat Step 4. |
Some users report that this works when the Google Analytics UI shows "Email not found". This is not an official workaround from Google. You need Application Default Credentials for an account that is an Administrator in Google Analytics, signed in with the https://www.googleapis.com/auth/analytics.manage.users scope (add it to --scopes). The call uses the accessBindings method of the Admin API v1alpha.
curl -X POST \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "x-goog-user-project: YOUR_PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{"user": "my-sa@my-project.iam.gserviceaccount.com", "roles": ["predefinedRoles/viewer"]}' \
https://analyticsadmin.googleapis.com/v1alpha/accounts/ACCOUNT_ID/accessBindingsReplace ACCOUNT_ID with the numeric account ID. To grant access to one property only, use properties/PROPERTY_ID instead of accounts/ACCOUNT_ID.
A single Service Account can already access multiple GA accounts if you grant it Viewer access in each one. You only need separate profiles when the Service Accounts themselves are different — e.g. different GCP projects, different clients, or permission boundaries you want to keep apart.
Drop each Service Account key into the profiles/ directory and switch between them with --profile:
mkdir -p ~/.config/google-analytics-cli/profiles
cp ~/Downloads/account-a-key.json ~/.config/google-analytics-cli/profiles/account-a.json
cp ~/Downloads/account-b-key.json ~/.config/google-analytics-cli/profiles/account-b.json
# Use a profile per command
google-analytics-cli accounts --profile account-a
# Or set it for the shell session
export GA_CLI_PROFILE=account-b
google-analytics-cli accounts
# List configured profiles
google-analytics-cli profiles--credentials and --profile cannot be used together; --credentials overrides GA_CLI_PROFILE.
All commands output pretty-printed JSON by default. Use --format compact for compact single-line JSON.
You can pass a property ID as an argument, via --property, or set the GA_PROPERTY_ID environment variable. Both raw numbers and properties/ prefixed IDs are accepted (e.g. 123456789 or properties/123456789).
export GA_PROPERTY_ID=123456789To find your property ID, run google-analytics-cli accounts. Each entry in propertySummaries has a property field such as properties/123456789. You can also find it in Google Analytics under Admin > Property settings > Property > Property details. The property ID is a number. It is not the measurement ID of a web data stream (G-XXXXXXXXXX). The CLI rejects values that start with G-.
Commands such as properties and change-history take an account ID instead. The account ID is also a number, e.g. 987654321. Find it in the account field of accounts (accounts/987654321).
List all GA4 accounts and their properties.
google-analytics-cli accountsGet details about a specific property.
google-analytics-cli property 123456789List Google Ads links for a property.
google-analytics-cli ads-links 123456789List annotations (notes) for a property. Uses the Admin API v1alpha.
google-analytics-cli annotations 123456789List properties for an account.
google-analytics-cli properties 987654321
google-analytics-cli properties 987654321 --show-deletedList data streams for a property.
google-analytics-cli data-streams 123456789List key events for a property.
google-analytics-cli key-events 123456789List custom dimensions for a property (Admin API).
google-analytics-cli admin-custom-dimensions 123456789List custom metrics for a property (Admin API).
google-analytics-cli admin-custom-metrics 123456789Get data retention settings for a property.
google-analytics-cli data-retention 123456789Search change history events for an account. Requires the Editor or Administrator role on the account. With Application Default Credentials, the login also needs the https://www.googleapis.com/auth/analytics.edit scope (see Application Default Credentials).
google-analytics-cli change-history 987654321
google-analytics-cli change-history 987654321 \
--earliest-change-time 2025-01-01T00:00:00Z \
--actor-email user@example.comOptions:
--filter-property <id>-- filter by property ID--earliest-change-time <timestamp>-- earliest change time (RFC3339)--latest-change-time <timestamp>-- latest change time (RFC3339)--resource-type <json>-- JSON array of resource types--action <json>-- JSON array of action types--actor-email <email>-- filter by actor email
Run an access report for a property. Shows who accessed the property's data and how much quota they used. Requires the Administrator role.
google-analytics-cli access-report 123456789 \
--dimensions "userEmail,accessedPropertyName" \
--metrics "dataApiQuotaPropertyTokensConsumed" \
--date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]'Options:
--dimensions <names>-- comma-separated dimension names (required)--metrics <names>-- comma-separated metric names (required)--date-ranges <json>-- JSON array of date ranges (required)--dimension-filter <json>-- JSON FilterExpression for dimensions--metric-filter <json>-- JSON FilterExpression for metrics--order-by <json>-- JSON array of OrderBy objects--limit <n>-- max rows to return--offset <n>-- row offset for pagination--time-zone <tz>-- time zone (e.g. America/Los_Angeles)--return-entity-quota-- include entity quota in response--include-all-users-- include users who have never accessed the API--expand-groups-- expand group memberships
The dimensions accessMechanism and epochTimeMicros and the metric accessCount are available only for Google Analytics 360 properties. See the Access report schema.
Get custom dimensions and metrics for a property.
google-analytics-cli custom-dims 123456789Get full metadata (all dimensions and metrics) for a property.
google-analytics-cli metadata 123456789Check compatibility of dimensions and metrics before running a report.
google-analytics-cli check-compatibility 123456789 \
--dimensions "date,country" \
--metrics "activeUsers,sessions"Options:
--dimensions <names>-- comma-separated dimension names--metrics <names>-- comma-separated metric names--dimension-filter <json>-- JSON FilterExpression for dimensions--metric-filter <json>-- JSON FilterExpression for metrics
Run a GA4 report with dimensions, metrics, and date ranges.
# Basic report
google-analytics-cli report 123456789 \
--dimensions "date,country" \
--metrics "activeUsers,sessions" \
--date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]'
# With filters and ordering
google-analytics-cli report 123456789 \
--dimensions "eventName" \
--metrics "eventCount" \
--date-ranges '[{"startDate": "7daysAgo", "endDate": "today"}]' \
--dimension-filter '{"filter": {"fieldName": "eventName", "stringFilter": {"matchType": "BEGINS_WITH", "value": "page"}}}' \
--order-by '[{"metric": {"metricName": "eventCount"}, "desc": true}]' \
--limit 10
# With currency and quota info
google-analytics-cli report 123456789 \
--dimensions "date" \
--metrics "totalRevenue" \
--date-ranges '[{"startDate": "2024-01-01", "endDate": "2024-01-31"}]' \
--currency-code USD \
--return-property-quotaRun a pivot report with cross-tabulated dimensions.
google-analytics-cli pivot-report 123456789 \
--dimensions "country,browser" \
--metrics "sessions" \
--date-ranges '[{"startDate": "30daysAgo", "endDate": "yesterday"}]' \
--pivots '[{"fieldNames": ["browser"], "limit": 5}]'Options:
--dimensions <names>-- comma-separated dimension names (required)--metrics <names>-- comma-separated metric names (required)--date-ranges <json>-- JSON array of date ranges (required)--pivots <json>-- JSON array of pivot definitions (required)--dimension-filter <json>-- JSON FilterExpression for dimensions--metric-filter <json>-- JSON FilterExpression for metrics--currency-code <code>-- ISO4217 currency code--keep-empty-rows-- include rows with all zero metric values--return-property-quota-- include property quota in response
Run multiple reports in a single batch (max 5).
google-analytics-cli batch-report 123456789 \
--requests '[{"dimensions": [{"name": "date"}], "metrics": [{"name": "activeUsers"}], "dateRanges": [{"startDate": "7daysAgo", "endDate": "yesterday"}]}]'Run a realtime report (no date ranges or currency code).
google-analytics-cli realtime 123456789 \
--dimensions "country" \
--metrics "activeUsers"
# With ordering and limit
google-analytics-cli realtime 123456789 \
--dimensions "unifiedScreenName" \
--metrics "activeUsers" \
--order-by '[{"metric": {"metricName": "activeUsers"}, "desc": true}]' \
--limit 5Create an audience export.
google-analytics-cli audience-export-create 123456789 \
--audience "properties/123456789/audiences/1" \
--dimensions "deviceId"List audience exports for a property.
google-analytics-cli audience-exports 123456789Get an audience export by name.
google-analytics-cli audience-export properties/123456789/audienceExports/abc123Query rows from an audience export.
google-analytics-cli audience-export-query properties/123456789/audienceExports/abc123
google-analytics-cli audience-export-query properties/123456789/audienceExports/abc123 --limit 100 --offset 0List credentials profiles in ~/.config/google-analytics-cli/profiles/ and show whether the default credentials.json exists. See Managing multiple GA accounts.
google-analytics-cli profilesErrors are written to stderr as JSON with an error field. For Google API errors, code and details are included when available:
{"error": "Permission denied", "code": 7}Warnings are written to stderr as JSON with a warning field. They do not change stdout or the exit code. For example, accounts writes a warning when it finds no accounts.
- google-search-console-cli -- Google Search Console CLI & Skills for AI agents (and humans)
- youtube-analytics-cli -- YouTube Analytics CLI & Skills for AI agents (and humans)
- x-analytics-cli -- X Analytics CLI & Skills for AI agents (and humans)
- camoufox-cli -- Anti-detect browser CLI & Skills for AI agents
- google-ads-open-cli -- Google Ads CLI & Skills for AI agents (and humans)
- meta-ads-open-cli -- Meta Ads CLI & Skills for AI agents (and humans)
- microsoft-ads-cli -- Microsoft Ads CLI & Skills for AI agents (and humans)
- amazon-ads-open-cli -- Amazon Ads CLI & Skills for AI agents (and humans)
- tiktok-ads-cli -- TikTok Ads CLI & Skills for AI agents (and humans)
- linkedin-ads-cli -- LinkedIn Ads CLI & Skills for AI agents (and humans)
- x-ads-cli -- X Ads CLI & Skills for AI agents (and humans)
- snapchat-ads-cli -- Snapchat Ads CLI & Skills for AI agents (and humans)
- pinterest-ads-cli -- Pinterest Ads CLI & Skills for AI agents (and humans)
- reddit-ads-cli -- Reddit Ads CLI & Skills for AI agents (and humans)
- spotify-ads-cli -- Spotify Ads CLI & Skills for AI agents (and humans)
- apple-ads-cli -- Apple Ads CLI & Skills for AI agents (and humans)
Apache-2.0