Google Analytics Data API Usage
Why pull GA4 data via API instead of the UI
The GA4 UI is fine for exploration but breaks down as a reporting backend: it can't feed a custom dashboard, can't be scheduled outside Looker Studio's refresh cadence, and can't be joined programmatically with CRM or ad spend data without manual export. The Google Analytics Data API (the successor to the old Reporting API v4, now built on GA4's data model) lets you query dimensions and metrics directly via a REST or client library call, returning structured JSON you can pipe into a warehouse, a Slack alert, or a custom dashboard.
runReport vs. runRealtimeReport vs. batchRunReports
The Data API exposes a few distinct endpoints worth knowing apart. runReport is the standard, finalized-data query — equivalent to a GA4 standard report, subject to the same data processing latency as the UI. runRealtimeReport queries the last ~30 minutes of activity for live dashboards, but supports a much smaller set of dimensions and metrics than historical reporting. batchRunReports lets you submit up to five report requests in a single call, which matters because the API enforces per-property quota limits and batching reduces the number of billed requests against that quota.
import { BetaAnalyticsDataClient } from "@google-analytics/data";
const analyticsDataClient = new BetaAnalyticsDataClient();
async function getSessionsBySource(propertyId: string) {
const [response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [{ startDate: "30daysAgo", endDate: "today" }],
dimensions: [{ name: "sessionSource" }, { name: "sessionMedium" }],
metrics: [{ name: "sessions" }, { name: "conversions" }],
limit: 50,
});
return response.rows?.map((row) => ({
source: row.dimensionValues?.[0].value,
medium: row.dimensionValues?.[1].value,
sessions: Number(row.metricValues?.[0].value),
conversions: Number(row.metricValues?.[1].value),
}));
}Dimension and metric compatibility isn't universal
checkCompatibility method, or Google's Query Explorer tool, to validate a dimension/metric combination before building it into production code; a silent 400 error mid-pipeline is far more annoying to debug than catching it upfront.Quotas, sampling, and thresholding on the API side
The Data API inherits GA4's same sampling and thresholding behavior as the UI for standard properties — queries against high-cardinality dimension combinations or long date ranges can get sampled, and thresholding can suppress rows with too few users to protect anonymity. The API also has its own quota (tokens per day, per property, replenishing hourly) separate from the BigQuery export's pricing model — a key reason teams with heavy automated reporting needs prefer querying BigQuery directly once volume grows, rather than scaling against Data API quotas.
Data API vs. BigQuery export for programmatic access
| Dimension | Data API | BigQuery export |
|---|---|---|
| Setup effort | Low — API call, no linking required beyond property access | Requires linking GA4 to a BigQuery project |
| Data granularity | Aggregated per dimension/metric combination | Fully unsampled, event-level raw rows |
| Cost model | Free within quota | Pay per byte scanned (can be optimized, but is real cost) |
| Best for | Dashboards, scheduled reports, lightweight integrations | Deep joins, custom attribution, ML feature pipelines |
Service account auth, not your personal OAuth token, for production
What's Next?
The Data API is the lightweight path to programmatic GA4 access; once reporting needs grow into real joins against CRM, ad spend, or product data, that same querying logic naturally migrates toward the BigQuery export and SQL.
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.