GA4 Data Health (ga4-data-health)
Summary
GA4 API health: real-time users, daily metrics, drops, and broken tracking signals (requires service account secret).
Classification
- Category:
SEO - Plugin id:
ga4-data-health - Version:
1.0.0 - Target types:
WEBSITE,WEBSHOP - Weight class:
LIGHT - Risk level: not set on plugin object
- Browser required: No (HTTP / Node / other — see source)
- Tier / group: Tier 1 — HTTP-only
Schedule
- scheduleHints:
{ defaultInterval: '0 */6 * * *', priority: 5 } - Prerequisites: None declared on this plugin.
Configurable inputs
Public keys are derived from inputSchema: z.object({ ... }) (underscore-prefixed keys are runtime-only and omitted here).
| Field | Notes |
|---|---|
ga4PropertyId |
Required numeric GA4 property ID (e.g. 123456789). |
ga4ServiceAccount |
Injected at runtime from target secret ga4_service_account (JSON key). |
minDailyUsers |
Default 10 — floor for “low traffic today” vs 7-day average comparison. |
maxRealtimeDropPct |
Declared in schema (default 80) but not read by the plugin; day-over-day drop uses a hardcoded 80% threshold. |
Runtime / injected config
- Target secret
ga4_service_account(JWT) for GA4 APIs.
Checks that produce findings
These checks run inside this plugin during a single test run. They are not separate tests in the database — each becomes a Finding linked to the Run for ga4-data-health.
| Check | Typical severity | Why |
|---|---|---|
| GA4 not configured (no property ID) | INFO | Test cannot run until ga4PropertyId is set; no API data is queried. |
| Service account credentials missing | WARNING | Property ID is set but secret ga4_service_account is absent — API calls cannot authenticate. |
| Invalid service account JSON | WARNING | Secret value is not parseable JSON; auth never attempted. |
| GA4 authentication failed | WARNING | JWT exchange to Google OAuth failed (permissions, clock, or key issue). |
| No GA4 data collected today | WARNING | Today’s users, sessions, and events are all zero — broken tag, wrong property, or genuinely no traffic. |
| Unusually low traffic today vs 7-day average | WARNING | todayUsers < minDailyUsers (default 10) while weekAvgUsers ≥ minDailyUsers — possible partial outage or tracking regression. |
| ≥80% traffic drop vs yesterday | WARNING | Both days have users; (yesterday − today) / yesterday × 100 ≥ 80. Sudden collapse worth investigating. |
| No real-time users (when weekly average ≥ 50) | INFO | Realtime API returned 0 active users on a site averaging ≥50 users/day — may be quiet moment or realtime pipeline lag. |
How each check works
- Configuration gate — Requires
ga4PropertyIdand parsedga4ServiceAccountfrom config (merged from target secret at run time). - Auth — Build RS256 JWT (
analytics.readonlyscope), POST tooauth2.googleapis.com/token, obtain bearer token. - Realtime —
runRealtimeReportwith metricactiveUsers(failures logged,realtimeUsersstays 0). - Daily —
runReportwith date ranges today and yesterday; metricsactiveUsers,sessions,eventCount. - Weekly baseline —
runReportfor7daysAgo–yesterday, divide total active users by 7 →weekAvgUsers. - Findings — Apply thresholds above; no CRITICAL severities in this plugin.
- Score — Start 100; subtract 30/15/5 per CRITICAL/WARNING/INFO finding (CRITICAL unused in practice).
Caveats and concerns
- Secret required — Without
ga4_service_accountin the secrets vault, only setup findings are emitted. - Property/timezone — “Today” follows GA4 property timezone, not necessarily the monitor’s UTC clock.
- False positives on low-traffic sites — Zero today may be normal; compare with
weekAvgUserscontext in evidence. - Unused config —
maxRealtimeDropPctis documented in schema but not wired into the drop calculation (80% is fixed in code). - API errors swallowed — Failed daily/weekly/realtime calls log warnings but may leave metrics at 0 without a dedicated “API failed” finding.
- No snippet verification — Does not check whether the site’s measurement ID matches the configured property.
Source
packages/test-plugins/src/plugins/ga4-data-health.ts