Google Analytics 4 from your terminal.
ga4 is a small, agent-first CLI for reports, comparisons, metadata, realtime analytics, and Google Analytics Admin API operations. It ships as one Go binary and produces clean table, JSON, Markdown, or CSV outputβwithout Node, npm, or a dependency tree.
Read data freely. Preview every mutation. Apply it only when you mean it.
With Homebrew on macOS or Linux:
brew tap dannolan/tap
brew install ga4-cli
ga4 versionOr build from source with the Go version declared in go.mod:
git clone https://github.com/dannolan/ga4-cli.git
cd ga4-cli
go build -o ga4 ./cmd/ga4
./ga4 versionPulling a quick GA4 report should not require a dashboard session or a custom script. This CLI keeps the useful path short:
- π Purpose-built commands for events, pages, custom reports, and period comparisons
- π€ JSON output and a discoverable command manifest for agents and shell scripts
- π Raw access to GA4 Data API and Admin API methods when the shortcuts are not enough
- π Local OAuth credentials and tokens, with no hosted intermediary
- π§― Dry-run-by-default mutations; Google is called only when
--applyis present - π¦ One cross-platform Go binary with no runtime dependency tree
- A Google Analytics 4 property you can access
- A Google Cloud OAuth 2.0 Desktop app client
- Google Analytics Data API enabled for that OAuth project
- Google Analytics Admin API enabled when using
ga4 admin - A browser on the same machine for the initial OAuth login;
--no-browsersuppresses automatic opening and prints the URL instead
If you do not already have a client ID, client secret, and numeric property ID, follow the complete Google OAuth setup guide first. It covers Google Cloud project creation, both Analytics APIs, consent and test-user settings, OAuth scopes, the Desktop client, the GA4 property ID, and common setup errors.
ga4-cli uses interactive user OAuth. It does not currently accept service-account JSON or Application Default Credentials.
The fastest setup writes your OAuth client and default property to ~/.config/ga4-cli/config.json:
ga4 config init \
--client-id "YOUR_CLIENT_ID" \
--client-secret "YOUR_CLIENT_SECRET" \
--property "YOUR_GA4_PROPERTY_ID"
ga4 auth login
ga4 doctorUse the numeric property ID, not a measurement ID such as G-XXXXXXXXXX. ga4 doctor returns a JSON readiness report covering configuration, OAuth, the property, and both Google APIs.
Configuration can also come from environment variables or ~/.config/ga4-cli/env:
export CLIENT_ID="..."
export CLIENT_SECRET="..."
export GA4_PROPERTY_ID="526319832"For credentials, precedence is environment variables, config.json, then the env file. A --property flag overrides every configured property value. Pass --config-dir to use an isolated config and token directory. Tokens are stored at ~/.config/ga4-cli/token.json; the legacy ~/.ga4-cli/token.json path is still read.
OAuth requests the analytics.readonly and analytics.edit scopes. Add both under Google Auth Platform Data Access before login. The Admin client needs the broader scope for some read methods, including change history; mutations remain locally gated behind --apply.
Quick reports default from seven days ago through today:
ga4 events --limit 10
ga4 pages --start-date 2026-07-01 --end-date 2026-07-31
ga4 report --metrics users,sessions --dimensions country --order-by=-sessionsCompare two periods:
ga4 compare \
--metrics sessions,activeUsers \
--dimensions country \
--current-start 2026-07-01 \
--current-end 2026-07-31 \
--previous-start 2026-06-01 \
--previous-end 2026-06-30 \
--format markdownUse --property to override the configured property for one call and --output to write report results to a file.
The Data API commands cover metadata, realtime and compatibility checks, raw reports, pivot reports, batch requests, and audience exports:
ga4 data metadata
ga4 data realtime --metrics activeUsers --dimensions country
ga4 data compatibility --metrics sessions --dimensions country --compatibility COMPATIBLE
printf '%s' '{"dateRanges":[{"startDate":"2026-07-01","endDate":"2026-07-31"}],"metrics":[{"name":"sessions"}],"limit":"1"}' \
| ga4 data run-reportJSON request bodies are read from stdin by default. Use --body request.json to read a file instead.
Admin examples:
ga4 admin account-summaries list
ga4 admin accounts list
ga4 admin properties list --filter 'parent:accounts/123456'
ga4 admin properties get properties/123456789
ga4 admin property-resources data-streams list properties/123456789See docs/api-coverage.md for the full method map. If Admin commands fail, run ga4 doctor; service-disabled and insufficient-scope responses include the relevant fix.
Every mutating command is a dry run unless --apply is explicitly passed. The dry run emits the operation, target, request, and the flag needed to proceed:
printf '%s' '{"displayName":"New Property","timeZone":"Australia/Sydney","currencyCode":"AUD","parent":"accounts/123456"}' \
| ga4 admin properties createOnly this form sends the request to Google:
printf '%s' '{"displayName":"New Property","timeZone":"Australia/Sydney","currencyCode":"AUD","parent":"accounts/123456"}' \
| ga4 admin properties create --applyRun ga4 manifest to discover all commands and check each command's mutation field before automation.
The repository includes SKILL.md, an agent skill with command-selection guidance, readiness checks, output rules, and mutation guardrails. Copy or symlink it into your agent's skill directory, or let the agent read it directly from the checkout.
Start discovery with:
ga4 version
ga4 config show
ga4 manifest --pretty=falseMachine-output rules:
events,pages,report, andcompareuse--format json.data,admin,doctor,smoke,manifest,version, and auth/config status commands emit JSON directly.--pretty=falsecompacts direct JSON output; report JSON is currently pretty-printed.- Successful output goes to stdout. Errors and OAuth instructions go to stderr.
- Command errors exit
1.doctorcan exit successfully while reporting"ok": false, so inspect the field. - Dates use
YYYY-MM-DD. Raw request bodies follow Google's generated GA4 API JSON shapes.
Example:
ga4 report \
--metrics sessions,keyEvents \
--dimensions sessionDefaultChannelGroup \
--format json \
| jq '.data | sort_by(.sessions | tonumber) | reverse[:5]'For unattended jobs, complete ga4 auth login interactively first and protect the config directory as a credential store.
- Property required: pass
--property 123456789, setGA4_PROPERTY_ID, or rerunga4 config init. - Wrong identifier: use the numeric GA4 property ID, not the
G-measurement ID. - Admin API disabled: enable
analyticsadmin.googleapis.comin the OAuth client's Google Cloud project, then rerunga4 doctor. - Insufficient OAuth scope: run
ga4 auth loginagain to replace the token with one carrying the current scopes. - Expired or broken token: inspect
ga4 auth status; if needed, runga4 auth logoutfollowed byga4 auth login. - Metric/dimension error: use
ga4 data metadataandga4 data compatibilitybefore building the report.
go test ./...
go build -o ga4 ./cmd/ga4Live verification is opt-in because it uses local Google credentials and a real property:
scripts/smoke-readonly.shRead CONTRIBUTING.md before changing command behavior or output contracts. The current verification record is in docs/verification.md.
Spottable β marketplace alerts and deal intelligence for finding better local deals.
MIT. See LICENSE.