How I catch Meta creative fatigue before it burns budget with n8n
To catch Meta creative fatigue with n8n, you run one flow on a morning timer. It pulls the last 14 days of daily numbers for every ad, works out each ad's own average click rate, then flags any ad whose click rate just dropped 30 percent or more below its usual level and posts them to Slack, worst first.
I built this because I kept catching fatigue too late, after a week of a tired ad quietly bleeding CPMs while I was busy in another account. I still run it every morning. Here is the whole thing: why it matters, how people usually spot fatigue, how mine works, and how to point it at your own accounts.
The problem
Every ad tires out. The trouble is you find out days after the money has already gone.
A creative that used to work slowly stops earning the click. The click rate slips, the cost per result climbs, and the budget keeps flowing into an ad that has already peaked. You do not see it in the moment because you are busy in another account, and Ads Manager will not tell you an ad is fading. By the time the drop is obvious in the numbers, a week of spend is gone. Fatigue is easy to fix once you spot it. Spotting it in time is the hard part.
Part of why this bites harder now: Meta rolled out Andromeda, its machine-learning ad-retrieval engine, in late 2024, and reports a 6 percent recall gain and 8 percent better ad quality on some segments. It matches each ad to its best audience faster and tests more creative at once, and agencies now report creatives fatiguing in one to three weeks instead of monthly, with advice to run ten to fifteen fresh assets per campaign.
How most teams catch fatigue today
There are three common ways people try to spot a tiring ad. Each has a catch.
Watch the dashboards by hand
You open Ads Manager and eyeball CTR and frequency across your ads. It is free, but it eats time every day, it is easy to skip when you are busy, and you usually notice the drop only after it has already cost you.
Go on gut feel or lean on the agency
You wait until results dip, then guess that a creative is tired, or you wait for the agency to flag it in the weekly call. It is slow and subjective, and by the time anyone raises it the ad has been fading for days.
Buy a creative-analytics tool
Tools like Motion or Atria track creative performance for you. They are good, but they bill every month, add another login, and score ads against blanket thresholds rather than each ad's own normal click rate.
The fix
You own it, it costs almost nothing to run, and it judges every ad against itself.
A single n8n flow does the watching for you. Every morning it reads the last 14 days of numbers for each ad, learns what a normal click rate looks like for that ad, and tells you the moment one starts to slip. The report lands in Slack before you touch a budget. Here is what it does, in three parts.
Pulls daily ad numbers from Meta
On a morning schedule it lists your active ad accounts, then reads the last 14 days of daily numbers for each ad (click rate, spend, impressions). It pages through the results so a busy account never gets cut off at the API's 500-row limit.
Scores each ad against its own normal
It groups the rows by ad, averages the click rate across the earlier days that ad actually ran and spent, then measures how far yesterday's click rate fell below that average. Every ad is judged against itself, not one blanket benchmark.
Slacks a ranked fatigue report
Ads down 10 percent or more land in a neat table, worst first, tagged warning, watch or new, with yesterday and 7-day spend so you see the budget at risk. It is one message a day, with a coverage line per account.
Want this watching your ad accounts?
Take the file and set it up yourself, or book a free call and we wire it into your Meta and Slack for you.
Build it step by step
It is a short chain: a schedule, a config, one heavy Code node that does the fetching and the scoring, and a Slack post. Here is every node in order and how to set each one up.
- 1Every morning 9am · Schedule Trigger
Starts the flow once a day so nobody has to press run.
Set it up: Add a Schedule Trigger and set the cron expression to 0 10 * * *, which fires the run once each morning. That is the only trigger in the flow.
- 2Config · Set
Holds all the numbers you can tune, in one place away from the code.
Set it up: Add a Set node and add fields for the Graph API version (v21.0), the window (last_14d), the warn drop (0.3), the watch drop (0.2), minimum impressions (200), minimum spend (20), and the show cutoff (0.1). The Code node reads every value from here.
- 3Fetch + Detect Fatigue · Code
Does almost all the work: gets the data, scores each ad, and writes the Slack message.
Set it up: Add a Code node. It reads your ad accounts (all active ones on the token, or the list in META_AD_ACCOUNT_IDS), pages through the last 14 days of daily ad numbers, groups them by ad, averages the click rate over the days each ad ran and spent, measures yesterday's drop against that average, and builds the ranked message. It reads META_ACCESS_TOKEN from the environment.
- 4Slack: Fatigue Report · HTTP Request
Posts the finished report into your Slack channel.
Set it up: Add an HTTP Request node set to POST to your SLACK_WEBHOOK_URL. Send the body as JSON with the message text and mrkdwn set to true, so the table shows in a monospace block.
A sticky note named About sits on the canvas with the setup checklist, but it does not run. The live path is just Schedule Trigger to Config to Fetch and Detect Fatigue to Slack.
What made it tricky?
Three things ate most of the build time. If you rebuild this, they are where it goes wrong first.
Judging ads on days they ran
The first version compared yesterday to a flat 7-day average and screamed about ads that simply do not run on weekends. The fix was to average the click rate only over earlier days the ad cleared both minimum impressions and minimum spend, so ads that run certain days or hours are judged fairly.
The 500-row insights cap
Daily ad numbers over 14 days across several accounts blow past the API's 500-row limit fast. The Code node follows the next link per account until the pages run out, so a busy account never quietly drops ads off the end of the report.
Own average, not a benchmark
A single account-wide click-rate benchmark flags healthy low-CTR formats and misses fatigue in high-CTR ones. Scoring each ad against its own running average is what makes the signal trustworthy, so the drop maps to that creative tiring rather than to format differences.
How do I adapt it to my own setup?
The whole thing is two environment variables and a handful of numbers. Here is what to change to make it yours.
| What you set | Where | Why |
|---|---|---|
| META_ACCESS_TOKEN | Environment variable | Your Meta access token with ads_read, used for every Graph API call. |
| SLACK_WEBHOOK_URL | Environment variable | The incoming webhook for the channel where the report should land. |
| META_AD_ACCOUNT_IDS | Environment variable (optional) | A comma-separated act_ list to pin specific accounts. Leave it unset to scan every active account on the token. |
| warnPct / watchPct / minShowDrop | Config node | Loosen or tighten what counts as fatigue and what makes the table. |
| minImpr / minSpend | Config node | Raise these for high-volume accounts so tiny-delivery days never skew a baseline. |
| Cron expression | Schedule Trigger | Move the 9am run to whatever time you review budgets. |
Set the two required variables, drop your ad account id into the account list, and activate. That is the whole setup. If you would rather not deal with the token scopes and the Graph query yourself, this is exactly the kind of build you can hire an n8n expert to finish. It pairs well with my multi-platform ad spend logger and my Meta ad comment triage workflow.
Wondering what that costs? See what an n8n expert costs in 2026.
n8n creative fatigue questions, straight answers.
How do I detect Meta creative fatigue with n8n?
Run an n8n Schedule Trigger every morning, pull the last 14 days of ad-level daily insights from the Meta Graph API in a Code node, group the rows by ad, work out each ad's own average CTR across the days it actually ran, then flag any ad whose latest-day CTR fell 30 percent or more below that baseline to Slack.
What counts as creative fatigue in this workflow?
Fatigue here is a CTR drop against the ad's own recent form. The Fetch and Detect node compares yesterday's click-through rate to the average across the earlier days the ad ran and spent. A drop of 30 percent or more gets a warning flag, and 20 to 30 percent gets a watch flag.
Why measure CTR against the ad's own average instead of a fixed benchmark?
Every ad, audience and format has a different natural CTR, so one account-wide benchmark punishes some ads and lets others coast. Comparing each ad to its own running average catches the moment that specific creative starts to tire, which is the signal that predicts rising costs and wasted budget.
How does it avoid false alarms from ads that only run some days?
The Config node sets minimum impressions of 200 and minimum spend of 20 dollars per day. Detection only averages days where the ad cleared both thresholds, so an ad that runs weekends or certain hours is never judged against days it was barely live. That removes most false alarms.
How often should the fatigue monitor run?
My Schedule Trigger fires once a day at 9am, so the fatigue report is waiting with my morning coffee before I touch any budgets. Daily is the right cadence because CTR decay plays out over days, not minutes. Running it hourly would just resend the same picture and add noise.
Can it watch more than one ad account?
Yes. By default the Code node reads every active account from your token through the accounts endpoint, or you can pin a fixed list with the META_AD_ACCOUNT_IDS variable. It reads ad-level insights per account a page at a time, then the Slack report groups results by account so you see coverage across all of them at once.
Do I need frequency data to catch fatigue?
No. Classic fatigue advice watches rising frequency, but this template keys on CTR decline instead, which shows up earlier and needs only the standard insights fields. It pulls CTR, spend and impressions per ad per day. If you want frequency too, add it to the insights fields and show it in the message.
Want this watching your ad accounts?
30 minutes, free. Bring your Meta account and your Slack channel. You leave with a plan to get a daily fatigue report of every tiring ad, whether you build it or we do.
Keep reading.
See all your ad spend in one place
One flow pulls Meta, Google, TikTok, and Taboola spend into Airtable, cleaned up and refreshed all day.
Get the right jobs in front of a candidate
It gathers jobs all day, scores each one for the person, and emails only the roles that really fit.
Read every application in seconds
It scores each applicant against your must-haves, saves everyone for a human, and hands HR the top three.
Rather have it built for you?
Skip the build. A free 30-minute call and we set this up in your stack, live in a week or two.