What this workflow does
- Daily at 6am starts the run.
- Get Google Ads campaigns (yesterday) uses n8n’s Google Ads node to read campaign cost, impressions, interactions and conversions for yesterday.
- Get Meta campaign insights (yesterday) calls the Meta Marketing API insights endpoint at campaign level.
- Normalize rows maps both platforms to one schema: date, platform, account, campaign, spend, impressions, clicks, conversions and conversion value. n8n’s Google Ads node does not return conversion value, so that column is filled for Meta only, and Google clicks use the interactions metric. You choose which Meta action counts as a conversion.
- Upsert daily rows in BigQuery runs a single
MERGEkeyed on date, platform and campaign, so reruns and restated days overwrite older values. - Is it Monday? keeps the load daily and the report weekly.
- Get last 14 days by campaign sums this week and last week per campaign in SQL.
- Compute changes and anomalies totals each platform and applies three rules: spend up more than 50%, CPA up more than 40%, and spend over a threshold with zero conversions. Each flag comes with a reason string. It also warns when campaigns are missing days.
- Write weekly commentary asks Claude for four to six sentences and three suggested checks, using only payload figures and no guessed causes. The output follows a strict JSON schema.
- Check numbers rejects commentary containing any number not in the payload, and handles refusals, truncated replies, API errors and responses with no text block. The table always posts.
- Post weekly summary and Alert: ad report needs attention deliver the report and any problems to Slack.
Before you import
- A current n8n version, cloud or self-hosted.
- A Google Ads developer token approved for your account, plus Google Ads OAuth2 in n8n.
- A Meta access token with
ads_readfor the ad account. - A Google Cloud project with BigQuery and a dataset you can write to.
- An Anthropic API key and a Slack app that can post.
Setup
- Create the
ad_dailytable with the SQL in the yellow note. - Import the JSON and attach credentials: Google Ads on its node, Facebook Graph API on the Meta node, BigQuery on both BigQuery nodes, Anthropic on the Claude node, Slack on both Slack nodes.
- Replace
REPLACE_WITH_GOOGLE_ADS_CUSTOMER_ID(two places) andREPLACE_WITH_GOOGLE_ADS_MANAGER_ID(use the same ID if you have no manager account). - Replace
REPLACE_WITH_META_AD_ACCOUNT_IDwith the digits of your ad account ID. - Replace
REPLACE_WITH_GCP_PROJECTandREPLACE_WITH_DATASETin both BigQuery nodes and the project fields. - Set
META_CONVERSION_ACTIONin Normalize rows, and tune thresholds in Compute changes and anomalies. - Replace
REPLACE_WITH_REPORT_CHANNELandREPLACE_WITH_ALERTS_CHANNEL.
Test it
Run the workflow manually and check that yesterday’s rows appear in BigQuery for both platforms, and that the spend roughly matches each platform’s UI. Run it again and confirm the row count does not change. To see the Monday report, force the Monday check to true once you have a few days of data.
Make it production-ready
- Trailing windows. Swap the Google Ads node for an HTTP Request with your own GAQL query to reload the last 30 days daily, add conversion value and go to ad level.
- Reconcile spend. Compare last week’s warehouse spend with each platform’s UI and post a data warning when they differ.
- More platforms. Add LinkedIn Ads and GA4 extractors that write to the same table.
- Error workflow and retries. Add an n8n error workflow; expired tokens are a common cause of failures.
- Dashboards. Point Looker Studio at the same table so the Slack note links to detail.
The ad performance reporting guide covers API access, definitions and anomaly design. For help with multi-account setups, contact us.