Automating a weekly Google Analytics 4 (GA4) report with n8n
Published 31 July 2026 · 6 min read
Opening Google Analytics every Monday, reconstructing last week's numbers from memory, comparing them against the week before, then pasting it all into a Slack message: that ritual takes twenty minutes and never survives a busy week. With n8n's native Google Analytics node, the GA4 Data API does exactly the same job — sessions, active users, conversions, traffic sources — on a weekly trigger, plus an AI-written summary and an automatic alert if traffic drops. Here is how to build that workflow end to end.
Connecting n8n to GA4 with the Google Analytics node
Unlike Search Console, Google Analytics has a native node in n8n, built on the GA4 Data API. The connection uses a standard Google OAuth2 credential:
- In Google Cloud Console, enable the "Google Analytics Data API" on your project, then create OAuth 2.0 credentials (Web Application type) with the redirect URL n8n provides.
- In n8n, create the OAuth2 credential attached to the Google Analytics node and run the authorization once: the refresh token is stored and renewed automatically. The detailed procedure is the same as in our guide to setting up Google OAuth2 in n8n.
- In the node, select your GA4 property (not a legacy Universal Analytics view — more on that below).
The authenticated Google account needs at least Viewer access on the GA4 property: the node exposes nothing beyond what the interface already allows.
Choosing the report's metrics and dimensions
GA4 thinks in metric/dimension pairs, and the temptation is to ask for everything. For an actionable weekly report, a small core is enough:
- Metrics:
sessions,activeUsers,conversions(or your key events), optionallyengagementRate. - Dimensions:
sessionSourceandsessionMediumto know where traffic comes from,landingPageto know where it lands.
Two queries beat one: a first broken down by source/medium (the "where from"), a second by landing page (the "where to"). Crossing both in the same query multiplies rows and drowns the signal. If the native node doesn't cover a specific need, the Data API remains callable through an HTTP Request node with the same credential — notably, the runReport endpoint accepts two date ranges in a single request, which simplifies the comparison:
{
"dateRanges": [
{ "startDate": "2026-07-20", "endDate": "2026-07-26" },
{ "startDate": "2026-07-13", "endDate": "2026-07-19" }
],
"dimensions": [{ "name": "sessionSource" }, { "name": "sessionMedium" }],
"metrics": [{ "name": "sessions" }, { "name": "activeUsers" }, { "name": "conversions" }],
"limit": 100
}
The date boundaries are generated dynamically by an upstream Set node ({{ $now.minus({ days: 8 }).toFormat('yyyy-MM-dd') }} and friends), so the workflow stays valid indefinitely with no maintenance. Note the one-day offset — we never include yesterday, for a reason covered in the pitfalls.
Comparing the week against the previous week
A Code node merges the two periods on the source + medium key (or landing page), then computes the absolute and percentage variation for each row. Three rules keep the result readable:
- Sort by absolute session variation, not percentage: "+400%" on a source going from 2 to 10 sessions means nothing.
- Keep only the top 10 gains and drops: a 200-row report never gets read.
- Compute a global total (sessions, active users, conversions, week vs. previous week) that will headline the report and feed the drop alert.
This period-over-period comparison pattern is exactly the one in our weekly SEO report with the Search Console API: the two workflows complement each other nicely in the same Slack channel — one tells you what Google sends you, the other what visitors do once they arrive.
AI summary, Slack delivery and archiving
An AI node then turns the variation table into three or four sentences: overall trend, sources on the rise, anomalies (a landing page losing 60% of its sessions, a paid channel collapsing). The prompt must be constrained — comment only on the supplied figures, flag deviations above a threshold, never speculate on unverifiable external causes. It's the same "structured data in, constrained summary out" principle as the AI-generated audit summary report.
The report then goes out on Slack (formatted blocks: totals up top, top variations as a list, AI summary below — see our Slack bot with n8n guide for formatting) or by email for an external client.
For long-term trends, archive every run before sending:
- Google Sheets for simplicity: one row per week with the totals and the main source/medium pairs, as described in our guide to automating Google Sheets with n8n. Readable by everyone, sufficient for one site.
- Postgres as soon as volume or analysis needs grow: a
ga4_weeklytable queryable in SQL for moving averages or year-over-year comparisons, using the n8n Postgres node. And if your marketing data crosses several sources at scale, the natural next step is BigQuery as a marketing data warehouse — GA4 even ships a native BigQuery export.
This reflex of steering by data rather than intuition isn't just about comfort: a study by Erik Brynjolfsson, Lorin Hitt and Heekyung Kim published in 2011, "Strength in Numbers: How Does Data-Driven Decisionmaking Affect Firm Performance?", conducted on 179 large firms, shows that companies anchoring their decisions in data achieve 5 to 6% higher productivity than their other investments would predict. A report that lands by itself every Monday is the small-scale version of that principle.
Automatic alert on traffic drops
The weekly report isn't enough if traffic collapses on a Tuesday. Add a second, lighter workflow on a daily trigger:
- A GA4 query on sessions from two days ago (a consolidated day) and the same day of the previous week.
- An IF node: if the drop exceeds a threshold — 30% is a good starting point, to be tuned to your site's natural volatility — the workflow continues; otherwise it stops quietly.
- An immediate Slack message with the figure, the threshold crossed, and the three sources that explain most of the drop.
Comparing against the same day of the previous week (Tuesday vs. Tuesday) rather than the day before avoids false alerts caused by weekly seasonality — a Monday is always different from a Sunday. And align the cron with the GA4 property's time zone, as detailed in our guide to the Schedule Trigger and time zones.
GA4-specific pitfalls
- Yesterday's data is incomplete. GA4 can take 24 to 48 hours to consolidate a day. A report that includes yesterday will compare partial data against complete data and "detect" imaginary drops. Always shift the window back by at least one day, two to be safe.
- Data API quotas are counted in tokens. Each request consumes tokens according to its complexity (dimensions, date range, cardinality), with caps per property, per hour and per day. A weekly report never gets close; a loop querying fifty properties every hour does. On a quota error, space out the calls rather than retrying immediately.
- (not set) everywhere. A dimension with no value (landing page on certain events, source on poorly tagged traffic) comes back as
(not set). Filter or group those rows in the Code node rather than letting them pollute the report's top 10. - Universal Analytics is gone. Legacy UA properties stopped processing data when Google shut them down, and the metrics changed logic along the way (sessions and users are no longer computed the same way, bounce rate gave way to engagement rate). Never compare a UA history against GA4 numbers row by row: treat GA4 as a fresh start.
Finally, if your reporting also needs to cover paid acquisition, the same workflow skeleton extends to ad platforms — see our guide to Google Ads and Meta Ads reporting with n8n and AI.
Key takeaways
- n8n's native Google Analytics node queries GA4 through the Data API, with a simple Google OAuth2 credential.
- A useful report fits in two queries (source/medium and landing pages), compared week over week and trimmed to a top 10 sorted by absolute variation.
- A constrained AI summary turns the table into three readable sentences; Slack or email for delivery, Google Sheets or Postgres for history.
- A daily threshold alert (X% drop vs. the same day of the previous week) complements the weekly report.
- Respect GA4's 24-48 hour consolidation delay, keep an eye on the Data API's token quotas, and neutralize
(not set)rows before publishing.
FAQ
Frequently asked questions
Does n8n's Google Analytics node work with GA4?
Yes. n8n's native Google Analytics node supports Google Analytics 4 through the Data API: you select the GA4 property, then the metrics and dimensions you want directly in the node parameters. Legacy Universal Analytics views no longer return data since Google stopped processing them.
Why do my GA4 numbers in n8n differ from the Google Analytics interface?
Three common causes: data from the last 24 to 48 hours is still being processed on the GA4 side, the interface sometimes applies sampling or privacy thresholds, and some rows get grouped under (not set). For a reliable weekly report, only query fully consolidated days — never the immediately preceding one.
What quota does the Google Analytics Data API enforce?
The Data API runs on a token system: each request consumes tokens, with caps per property, per hour and per day. A weekly report sending a handful of requests stays far below the limits; quotas only start to matter if you query many properties at high frequency or with lots of dimensions.
Should I store the history in Google Sheets or Postgres?
Google Sheets is enough for a simple record that non-developers can read: one row per week, one tab per site. Postgres becomes preferable as soon as you want trend queries (moving averages, year-over-year comparisons), multiple properties, or several years of history without slowdown.
Bundle FlowKit Complet
€269