Read-only client for Google Analytics Data API (GA4). Mirrors the structure used in the GSC SDK.
- Installed-app OAuth and Service Account auth
- Small HTTP client with retries/backoff
- Typed Pydantic request/response models
- Quickstart scripts
- Mocked tests (no real creds required)
Use your existing workspace's Python. This folder includes a pyproject.toml with deps similar to the GSC SDK.
Copy .env.example and set values, or export directly:
GA_CLIENT_SECRETS(optional for installed-app)GA_TOKEN_PATH(installed-app token cache)GA_SA_KEY(service account JSON key path)GA_SCOPE=https://www.googleapis.com/auth/analytics.readonlyGA_PROPERTY_ID=<your numeric property id>GA_HTTP_TIMEOUT=30GA_USER_AGENT=CyberOni-GA4-SDK/1.0
scripts/quickstart_ga4.py(installed-app)scripts/quickstart_ga4_service_account.py(service account)
- 401/403: SA/user missing property access; wrong scope; wrong project
- Invalid argument: wrong dimension/metric; call
get_metadata()to discover - Quota: handle 429/5xx with backoff
- Do not commit secrets.
- Add the service account email as a user on the GA4 property in Admin.
- Create/select a Google Cloud Project
- Visit Google Cloud Console → IAM & Admin → Create Project
- Note the Project ID
- Enable the Analytics Data API
- APIs & Services → Library → search "Analytics Data API" → Enable
- Create a Service Account
- IAM & Admin → Service Accounts → Create Service Account
- Assign minimal role (Viewer is sufficient for API usage); roles can be tightened later
- Create key → JSON → Download (store securely)
- Grant GA4 property access to the Service Account
- In Google Analytics → Admin (gear) → Property Access Management
- Add user → use the service account email (…@.iam.gserviceaccount.com)
- Give Viewer/Analyst or higher per your needs
- Set environment variables (see
.env.example)
GA_SA_KEY=/absolute/path/to/service-account.jsonGA_SCOPE=https://www.googleapis.com/auth/analytics.readonlyGA_PROPERTY_ID=<your GA4 numeric property id>
- Verify access quickly
- Use the service account quickstart below to call
runReport
Use when each end-user must consent. Steps:
- APIs & Services → OAuth consent screen → Configure (External or Internal)
- Create Credentials → OAuth client ID → Application type: Desktop (or Web)
- Download client secrets JSON; set
GA_CLIENT_SECRETSandGA_TOKEN_PATH - First run prompts the user; refresh token is stored and used automatically later
File: backend/app/core/landing_page/google_analytics/scripts/quickstart_ga4_service_account.py
from app.core.landing_page.google_analytics import GA4Client, build_run_report_request
import os
property_id = os.environ["GA_PROPERTY_ID"]
client = GA4Client.from_service_account(key_path=os.environ["GA_SA_KEY"]) # uses readonly scope by default
req = build_run_report_request(
dimensions=["date"],
metrics=["activeUsers"],
last_n_days=7,
limit=10,
)
resp = client.run_report(property_id, req)
print(resp.rowCount, [r.dimensionValues for r in resp.rows])File: backend/app/core/landing_page/google_analytics/scripts/quickstart_ga4.py
import os
from app.core.landing_page.google_analytics import GA4Client, build_run_report_request
client = GA4Client.from_installed_app(
client_secrets_file=os.environ["GA_CLIENT_SECRETS"],
token_cache_path=os.environ.get("GA_TOKEN_PATH", ".ga4_token.json"),
)
req = build_run_report_request(
dimensions=["country"], metrics=["activeUsers"], last_n_days=3
)
resp = client.run_report(os.environ["GA_PROPERTY_ID"], req)
print(resp.rowCount)You can wire the client into routes using simple utilities.
- Dependency that builds a
GA4Clientfrom a Bearer token header:get_ga4_client_dependencyingoogle_analytics/__init__.py(fromapi/deps.py). - Convenience request builders:
build_run_report_request,build_realtime_request.
Example route usage:
# file: app/api/routes/analytics_example.py
from fastapi import APIRouter, Depends
from app.core.landing_page.google_analytics import (
get_ga4_client_dependency, build_run_report_request, GA4Client,
)
router = APIRouter(prefix="/analytics", tags=["analytics"])
@router.get("/active-users")
def active_users_last_7_days(ga: GA4Client = Depends(get_ga4_client_dependency)):
req = build_run_report_request(dimensions=["date"], metrics=["activeUsers"], last_n_days=7)
resp = ga.run_report("<PROPERTY_ID>", req)
return resp.model_dump()If you prefer service accounts in the API layer (no user token):
from app.core.landing_page.google_analytics import GA4Client, build_run_report_request
import os
ga = GA4Client.from_service_account(key_path=os.environ["GA_SA_KEY"]) # initialize once at startup
def handler():
req = build_run_report_request(dimensions=["pagePath"], metrics=["screenPageViews"], last_n_days=1)
return ga.run_report(os.environ["GA_PROPERTY_ID"], req).model_dump()- 401/403
- Service account lacks GA4 property access, or user token is invalid
- Wrong Cloud project or API not enabled
- Invalid argument
- Dimension/metric name incorrect; call
get_metadata()to inspect available fields
- Dimension/metric name incorrect; call
- Quotas / 429 / 5xx
- Retries with backoff are built-in; tune timeouts if needed