Google Analytics
Overview
The Google Analytics Core API mirrors the GA4 Data API and GA4 Admin API 1:1 — arbitrary reports, batched reports, realtime active users, and property discovery.
| Base path | /api/core/google-analytics |
| Auth | the calling organization's own stored Google OAuth credentials, resolved from its google_analytics integration. You never pass a Google token yourself |
| Every request | requires ?organizationId=<uuid> |
This is the one Core API domain that is read-only. GA4 is a reporting surface — there is nothing to mutate — so a read-scoped API token reaches all four routes.
Choosing a property. Calls default to the GA4 property stored on the integration. An org holding several property rows can target any of them with ?propertyId= (the bare numeric id, no properties/ prefix). Discover them with GET /api/core/google-analytics/properties.
API Endpoints
| Endpoint | GA4 Real Endpoint | Description |
|---|---|---|
GET /properties | Admin accountSummaries.list | Every GA4 property the token can read |
POST /reports/run | Data properties/{id}:runReport | Run one report |
POST /reports/batch-run | Data properties/{id}:batchRunReports | Run up to 5 reports in one call |
GET /reports/realtime | Data properties/{id}:runRealtimeReport | Active users, last 30 minutes |
Properties
GET /api/core/google-analytics/properties
GET /api/core/google-analytics/propertiesThe raw accountSummaries.list response — every account and the GA4 properties under it. Use a property's numeric id as ?propertyId= on the report routes.
curl "https://analify.scalyax.ai/api/core/google-analytics/properties?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}"Reports
POST /api/core/google-analytics/reports/run
POST /api/core/google-analytics/reports/runThe request body is the GA4 runReport body, forwarded verbatim — dimensions, metrics, dateRanges, dimensionFilter, metricFilter, orderBys, limit, offset, keepEmptyRows. Analify adds nothing.
curl -X POST "https://analify.scalyax.ai/api/core/google-analytics/reports/run?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
-d '{
"dimensions": [{ "name": "date" }, { "name": "sessionDefaultChannelGroup" }],
"metrics": [{ "name": "sessions" }, { "name": "totalRevenue" }],
"dateRanges": [{ "startDate": "2026-08-01", "endDate": "2026-08-16" }]
}'The response is GA4's own runReport payload — dimensionHeaders, metricHeaders, rows[].dimensionValues[].value / metricValues[].value. Every GA4 metric value arrives as a string, including numbers; parse before arithmetic.
POST /api/core/google-analytics/reports/batch-run
POST /api/core/google-analytics/reports/batch-runSame contract, but the body carries an array of report requests. GA4 caps a batch at 5 — a sixth request is rejected by Google, not by Analify.
curl -X POST "https://analify.scalyax.ai/api/core/google-analytics/reports/batch-run?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}" -H "Content-Type: application/json" \
-d '{
"requests": [
{
"dimensions": [{ "name": "date" }],
"metrics": [{ "name": "sessions" }],
"dateRanges": [{ "startDate": "2026-08-01", "endDate": "2026-08-16" }]
},
{
"dimensions": [{ "name": "landingPage" }],
"metrics": [{ "name": "bounceRate" }],
"dateRanges": [{ "startDate": "2026-08-01", "endDate": "2026-08-16" }]
}
]
}'Batching is one HTTP round trip and one quota charge per contained report — prefer it over N sequential run calls.
GET /api/core/google-analytics/reports/realtime
GET /api/core/google-analytics/reports/realtimeActive users over the last 30 minutes — GA4's realtime window, which is fixed and takes no date range.
curl "https://analify.scalyax.ai/api/core/google-analytics/reports/realtime?organizationId={organizationId}" \
-H "Authorization: Bearer {anlfy_token}"Things GA4 itself constrains
- History depends on the property, roughly 2–14 months depending on its retention setting. A
startDatebeyond retention returns fewer rows rather than an error. - Sampling and thresholding. GA4 may sample large queries and will withhold rows that risk identifying individuals; check
metadata.samplingMetadatasand(other)rows before treating a total as exact. - Metric values are strings. See above — this is GA4's contract, preserved.
Error Shape
| Code | Status | Meaning |
|---|---|---|
GA4_NOT_CONNECTED | 400 | The organization has no connected Google Analytics integration |
GA4_API_ERROR | 502 | Any failure reported by GA4, carrying Google's own message |
GA4_UNKNOWN_ERROR | 500 | A non-platform failure inside the route |
{
"success": false,
"error": {
"message": "Field metrics[0].name contains an invalid metric: 'sessionz'.",
"code": "GA4_API_ERROR"
}
}Updated about 2 months ago
