Pull current-session / active-user data from the GA4 Realtime endpoint — a separate API surface from runReport with different metrics, dimensions, and freshness guarantees (~30 min rolling window instead of T-48h). Trigger with "GA4 realtime", "active users right now", "GA4 current sessions", "who's on my site now".
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill ga4-realtime-api --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ga4 Realtime Api?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-ga4-realtime-api-a22d3f80)More formats (shields.io, HTML) on the badges page.
---
name: ga4-realtime-api
description: |
Pull current-session / active-user data from the GA4 Realtime endpoint —
a separate API surface from runReport with different metrics, dimensions, and
freshness guarantees (~30 min rolling window instead of T-48h). Trigger with
"GA4 realtime", "active users right now", "GA4 current sessions",
"who's on my site now".
allowed-tools: Bash(python3:*), Bash(curl:*)
version: 1.3.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags: [saas, analytics, google-analytics, ga4, realtime]
compatibility: Designed for Claude Code
---
# GA4 Realtime API
## Overview
The Realtime API is GA4's "what's happening right now" endpoint. Different from `runReport`:
| | `runReport` (Data API) | `runRealtimeReport` (Realtime) |
|---|---|---|
| Freshness | ~24-48h lag, stable | Last ~30 min, rolling |
| Window | Any date range | Implicit — last 30 min |
| Metrics | ~50 supported | ~10 supported (subset) |
| Dimensions | ~150 supported | ~15 supported (subset) |
| Quota | Per-property daily | Separate Realtime quota |
| Use case | Reports, dashboards, trend analysis | Live dashboards, monitoring, "are we down?" |
Don't try to use `runReport` for now-data — its freshest data point is yesterday. Use `runRealtimeReport`.
## Prerequisites
- A GA4 Data API credential with access to the target property, configured through `ga4-auth-setup`.
- Python with `google-analytics-data` installed.
- A numeric property ID; realtime data is inherently a rolling, roughly 30-minute view and is not a replacement for historical reporting.
## Instructions
## Examples
## Minimum viable call
```python
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import (
RunRealtimeReportRequest, Metric, Dimension,
)
client = BetaAnalyticsDataClient()
resp = client.run_realtime_report(RunRealtimeReportRequest(
property="properties/123456789",
metrics=[Metric(name="activeUsers")],
))
# Single-row response when there are no dimensions
total = int(resp.rows[0].metric_values[0].value) if resp.rows else 0
print(f"Active users right now: {total}")
```
No `date_ranges` block — the implicit window is the last 30 min. Adding one will error.
## Realtime metrics (the full list)
| Metric | What it counts |
|---|---|
| `activeUsers` | Unique users in the last 30 min |
| `screenPageViews` | Pageviews + screenviews in the last 30 min |
| `eventCount` | Total events in the last 30 min |
| `conversions` | Conversion events in the last 30 min |
| `keyEvents` | Key events (post-2024 rename of conversions) |
Custom-event aggregates (e.g. `purchase_revenue`) are NOT in the Realtime API. If you need realtime revenue, derive it from `eventCount` filtered to `eventName=="purchase"` plus your average AOV.
## Realtime dimensions (the full list)
| Dimension | Use |
|---|---|
| `country`, `city` | Geo of currently-active users |
| `deviceCategory` | desktop / mobile / tablet split |
| `unifiedScreenName` / `unifiedScreenClass` | App screen / web title |
| `eventName` | Event-type breakdown |
| `streamId`, `streamName` | When property has multiple data streams (web + iOS + Android) |
| `platform` | web / ios / android |
| `appVersion`, `audienceName`, `audienceId` | When defined in the property |
That's the full list. ~15 dims total. Compare to `runReport`'s ~150.
## Common realtime queries
### "How many people are on my site right now?"
```python
resp = client.run_realtime_report(RunRealtimeReportRequest(
property="properties/123456789",
metrics=[Metric(name="activeUsers")],
))
print(int(resp.rows[0].metric_values[0].value) if resp.rows else 0)
```
### "Active users by country, right now"
```python
resp = client.run_realtime_report(RunRealtimeReportRequest(
property="properties/123456789",
metrics=[Metric(name="activeUsers")],
dimensions=[Dimension(name="country")],
limit=20,
))
for r in resp.rows:
print(f"{r.dimension_values[0].value:25s} {r.metric_values[0].value}")
```
### "Which events are firing in the last 30 min?"
```python
resp = client.run_realtime_report(RunRealtimeReportRequest(
property="properties/123456789",
metrics=[Metric(name="eventCount")],
dimensions=[Dimension(name="eventName")],
limit=30,
))
```
This is the live event firehose — useful to verify a new tracking deployment is actually firing.
### "Top pages right now"
```python
resp = client.run_realtime_report(RunRealtimeReportRequest(
property="properties/123456789",
metrics=[Metric(name="screenPageViews")],
dimensions=[Dimension(name="unifiedScreenName")], # NOT pagePath — that's Data-API-only
limit=20,
))
```
Realtime doesn't expose `pagePath` directly. Use `unifiedScreenName` (the page title) or `unifiedScreenClass`. To get path-level granularity in realtime, push a custom event with the path as a parameter, then query by `eventName` + that custom dimension.
## Filters
Same shape as `runReport` — `FilterExpression` / `Filter` blocks. Realtime supports `dimension_filter` and `metric_filter` but not the full set of dimensions / metrics; check the [Realtime API schema](https://developers.google.com/analytics/devguides/reporting/data/v1/realtime-api-schema) before writing complex filters.
## Quotas — different from Data API
Realtime has its own quota bucket. Defaults (2026):
- 5,000 requests per project per day
- 250 requests per property per day
- 60 requests per minute per property
For a live dashboard polling every 10s: that's 6 RPM, well within limits. For a hot incident where you want minute-by-minute data, you can poll up to 60x/min per property.
## Don't poll faster than 30s
The data window is the last 30 min. Polling faster than ~30s wastes quota without meaningful resolution change. For most "live" use cases, 60s polling is plenty.
## Output
The API returns the current rolling-window aggregate or a bounded set of dimension rows, such as active users by country or event count by name. A valid empty `rows` list means no currently reportable activity, so callers should render zero rather than treat it as a transport failure.
## Error Handling
| Issue | Why |
|---|---|
| `activeUsers` doesn't match the GA4 web UI's "Realtime" overview | The UI uses a slightly different window (~5 min default) and may include in-flight events not yet reportable via API. Web UI > API for instant-incidents. |
| Empty rows on a busy site | Property may be using a different stream you didn't filter for. Add `Dimension(name="streamId")` to see splits. |
| `400 INVALID_ARGUMENT: Realtime reports do not support dimension X` | Using a Data-API-only dimension (e.g. `pagePath`, `sessionSource`). Use a Realtime dimension. |
| Latency between front-end event and Realtime visibility | ~10-30 seconds is normal. If >2 minutes, check the GA4 DebugView for event delivery issues. |
## Resources
- `ga4-auth-setup` — prerequisite
- `ga4-data-api-query` — for any window longer than 30 min
- `ga4-common-reports` — for canonical reports (DAU/MAU/retention) which are NOT realtime-able
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!