85 lines
3.1 KiB
Markdown
85 lines
3.1 KiB
Markdown
# Google Analytics (GA4)
|
|
|
|
Proxy over the GA4 **Data API** (`analyticsdata.googleapis.com/v1beta`) and
|
|
**Admin API** (`analyticsadmin.googleapis.com/v1beta`).
|
|
|
|
## Credentials
|
|
|
|
Token has precedence over the service account:
|
|
|
|
| Header | Meaning |
|
|
| --- | --- |
|
|
| `X-GA-Access-Token` | Ready OAuth2 access token, used directly as `Authorization: Bearer`. |
|
|
| `X-GA-Credentials` | **Base64** of a Google service-account JSON key. The proxy mints a short-lived token (scope `https://www.googleapis.com/auth/analytics.readonly`) via `google-auth` and caches it in memory until ~60 s before expiry. |
|
|
| `X-GA-Quota-Project` | Optional GCP project id → upstream `x-goog-user-project`. |
|
|
|
|
At least one of `X-GA-Access-Token` / `X-GA-Credentials` is required (otherwise
|
|
`401 missing_credentials`).
|
|
|
|
The service account (or token) must have access to the GA4 property — add its
|
|
`client_email` as a viewer in GA Admin → Property Access Management.
|
|
|
|
> Encoding the key: `base64 -w0 service-account.json` (Linux) or
|
|
> `[Convert]::ToBase64String([IO.File]::ReadAllBytes("service-account.json"))`
|
|
> (PowerShell).
|
|
|
|
## Data API endpoints
|
|
|
|
`property_id` may be the bare number (`123456789`) or `properties/123456789`.
|
|
|
|
| Method | Path | Upstream |
|
|
| --- | --- | --- |
|
|
| POST | `/ga/data/properties/{id}/runReport` | `:runReport` |
|
|
| POST | `/ga/data/properties/{id}/runPivotReport` | `:runPivotReport` |
|
|
| POST | `/ga/data/properties/{id}/batchRunReports` | `:batchRunReports` |
|
|
| POST | `/ga/data/properties/{id}/batchRunPivotReports` | `:batchRunPivotReports` |
|
|
| POST | `/ga/data/properties/{id}/runRealtimeReport` | `:runRealtimeReport` |
|
|
| POST | `/ga/data/properties/{id}/checkCompatibility` | `:checkCompatibility` |
|
|
| GET | `/ga/data/properties/{id}/metadata` | `/metadata` |
|
|
|
|
The POST body is the GA4 request object, forwarded unchanged. Example
|
|
`runReport` body:
|
|
|
|
```json
|
|
{
|
|
"dateRanges": [{ "startDate": "7daysAgo", "endDate": "today" }],
|
|
"dimensions": [{ "name": "country" }],
|
|
"metrics": [{ "name": "activeUsers" }]
|
|
}
|
|
```
|
|
|
|
## Admin API endpoints (read)
|
|
|
|
| Method | Path | Notes |
|
|
| --- | --- | --- |
|
|
| GET | `/ga/admin/accounts` | `pageSize`, `pageToken` |
|
|
| GET | `/ga/admin/accountSummaries` | accounts + their properties |
|
|
| GET | `/ga/admin/properties?accountId=123` | builds `filter=parent:accounts/123` |
|
|
| GET | `/ga/admin/properties/{id}` | single property |
|
|
| GET | `/ga/admin/properties/{id}/dataStreams` | data streams |
|
|
|
|
## Errors
|
|
|
|
`UpstreamError` is returned as JSON with the upstream status and body:
|
|
|
|
```json
|
|
{
|
|
"error": "upstream_error",
|
|
"status": 403,
|
|
"detail": "User does not have sufficient permissions for this property.",
|
|
"upstream_status": 403,
|
|
"upstream_body": { "error": { "code": 403, "status": "PERMISSION_DENIED" } }
|
|
}
|
|
```
|
|
|
|
Timeouts → `504`, unreachable/transport → `502`, token minting failure → `401`.
|
|
|
|
## curl example
|
|
|
|
```bash
|
|
curl -X POST "https://services.csbot.cz/apps/analytics/ga/data/properties/123456789/runReport" \
|
|
-H "X-GA-Access-Token: ya29...." \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"dateRanges":[{"startDate":"7daysAgo","endDate":"today"}],"metrics":[{"name":"activeUsers"}]}'
|
|
```
|