How I automate Meta Ads quality-ranking checks with n8n

The short version

This n8n Meta Ads automation reads all three ad relevance diagnostics for every ad over the last 7 days, keeps the ones Meta marks below average that also spent more than 20 dollars, then sorts them by spend so the most expensive problem sits at the top instead of the worst-scoring trivial one.

Quality ranking is the metric everyone can define and almost nobody monitors. It sits in Ads Manager behind a column you have to go and add, so it gets checked when something is already wrong. This is the flow I run instead, what it actually looks at, and how to point it at your own account.

Why does a below-average quality ranking cost you money?

Meta defines a below average quality ranking as your ad's perceived quality landing among the lowest 20 percent of ads competing for the same audience. Two things follow from that definition and both are easy to miss.

First, it is relative. Your creative can be untouched for months and still slide below average because the other advertisers bidding for that audience got better. Nothing in your account changed, so nothing in your account alerts you.

Second, it is a delivery input, not a vanity score. A weak relevance signal makes the same audience more expensive to reach, so the damage shows up as drifting CPM long before it shows up as an obviously broken ad. By the time the ROAS drop is undeniable, you have already paid for weeks of it.

How do most teams check quality ranking today?

Almost every guide on this metric explains what the three diagnostics mean and then tells you to go and look at them. That is the whole advice. Here is what the realistic options actually cost you.

ApproachWhat it costsWhere it fails
Add the columns in Ads Manager and eyeball themFree, 10–20 min per checkNobody does it weekly. It happens after performance drops, not before.
Export to a sheet and filter manuallyFree, 20–30 min per checkGoes stale the day you stop. No spend weighting, so you fix the wrong ads first.
Third-party ad audit toolRecurring subscriptionBlack box thresholds you cannot see or change, and one more login.
This n8n flowFree to run, one-time buildYou own the thresholds, so you have to pick them. That is the trade.

The gap is not knowledge. It is that checking is manual, so it only happens after something breaks.

What does the workflow actually do?

Seven working nodes. It calls the Meta Graph API insights endpoint at ad level, requests the three ranking fields alongside spend, and hands the rows to one Code node that does the thinking.

That node keeps a row only if two things are true: the ad spent at least the configured floor, and at least one of quality_ranking, engagement_rate_ranking or conversion_rate_ranking comes back with a value beginning BELOW_AVERAGE. Everything that survives is tagged with which of the three it failed on, then sorted by spend, highest first.

It also totals the spend across every flagged ad into a single spend-at-risk figure. That number is what makes the report worth opening. Quality ranking on its own is a label most people ignore, but the same information expressed as a dollar amount tends to get acted on.

Want this watching your ad accounts?

If you would rather it just ran, we build these. Bring your Meta account and we will scope it in 30 minutes, free. You can also hire an n8n expert for the whole stack, or check what an n8n expert costs first.

How do I build it step by step?

1. Config node. One Code node holding two values, so you never hunt through the flow to tune it. Default is a 7 day window and a 20 dollar minimum spend:

return [{ json: {
  DATE_PRESET: 'last_7d',   // see the warning below, do not raise this
  MIN_SPEND: 20,            // only flag ads that spent at least this
} }];

The window is the whole ballgame, and it is the thing that will silently break this for you. The three ranking fields only populate over a recent rolling window. Ask for them over 30 days and Meta returns UNKNOWN for every single ad, so your digest reports zero problems forever and looks like it is working.

I ran this on a live account to be sure. Over last_30d, all 465 ads came back UNKNOWN, including one with 552,252 impressions. The same account over last_7d returned 192 ads with 43 rated average and 6 above average. Over last_3d, 94 ads and only 4 rated at all.

So this is not an impressions threshold, which is the usual explanation and it is wrong. Half a million impressions still reads UNKNOWN at 30 days. Keep the window at 7 days. Widening it does not give you more history, it gives you nothing at all.

2. Fetch the rankings. An HTTP Request node against the insights endpoint for your ad account, at ad level, asking for the three ranking fields plus spend, ad name, ad set and campaign. Keep the API version and account id in environment variables rather than pasting them into the node.

3. Flag the below-average ads. The Code node that matters. Note it tests with a prefix match, not equality, because Meta returns several distinct below-average values rather than one:

const isLow = (v) =>
  typeof v === 'string' && v.indexOf('BELOW_AVERAGE') === 0;

Then skip anything under MIN_SPEND, collect which of the three diagnostics tripped into a below_on array, sort by spend descending, and total the spend at risk.

4. Explain the fixes (optional). The flagged rows go to OpenAI with a prompt asking what to change for each. Useful, but delete it and you still have the digest.

5. Print or route the report. Swap the final node for Slack, Gmail or a sheet. Everything upstream stays identical.

What made it tricky?

Below average is not one value. The obvious implementation compares the field to the string BELOW_AVERAGE and silently matches almost nothing, because Meta reports degrees of below average rather than a single flag. Prefix matching is why this works. It was also the bug that cost me the most time.

Low-spend ads generate noise. Without a spend floor the digest fills with ads that spent a couple of dollars and never stabilised, and a report you skim is a report you stop reading. The floor exists to keep it short.

Severity is the wrong sort order. My first version sorted worst-ranking first. It surfaced a genuinely awful ad that had spent 11 dollars while a 3,000 dollar ad sat below the fold. Sorting by spend fixed the report in one line.

How do I adapt it to my own setup?

Do not widen the window. DATE_PRESET takes any Meta preset, but anything much beyond 7 days returns UNKNOWN rankings and an empty digest. Shorten it to last_3d if you want, though fewer ads will have a rating at all.

Raise the floor. On accounts spending five figures a month, 20 dollars is far too low and the digest gets long. Set it wherever the list stays short enough to read.

Watch one diagnostic only. If you only care about creative quality, drop engagement and conversion from the low array and the digest narrows to quality ranking.

Run it across accounts. Loop the fetch over a list of account ids and group the output by account. Same detection logic, one report.

Track the recovery. Append each run to a sheet and you get the thing Ads Manager will not give you: whether the fix you shipped last week actually moved the ranking.

n8n Meta Ads quality ranking questions, straight answers.

How do I check Meta ad quality ranking automatically with n8n?

Call the Meta Graph API insights endpoint at ad level and request the quality_ranking, engagement_rate_ranking and conversion_rate_ranking fields alongside spend. A Code node then keeps any row whose ranking value starts with BELOW_AVERAGE and spent over your floor, and sorts what is left by spend.

What does below average quality ranking actually mean?

Meta defines a below average quality ranking as your ad's perceived quality landing among the lowest 20 percent of ads competing for the same audience. It is a relative score, not an absolute one, so an ad can fall below average without anything about the creative changing, purely because competitors improved.

Why sort flagged ads by spend instead of by how bad the ranking is?

Because a below average ad spending 12 dollars a month costs you almost nothing, while one spending 4,000 dollars is quietly taxing every impression. Sorting by ranking severity puts trivial ads at the top of your list. Sorting by spend puts the money first, which is the order you should actually fix them in.

What is the minimum spend threshold for and why 20 dollars?

New and low-volume ads produce noisy rankings, so the config node has a MIN_SPEND floor set to 20 dollars by default. Ads under that are skipped entirely. Twenty is a starting point, not a rule. On a large account raise it, because you want the digest short enough that you actually read it.

Does this workflow check all three ad relevance diagnostics?

Yes. It reads quality ranking, engagement rate ranking and conversion rate ranking on every ad, and records which of the three each flagged ad failed on in a below_on field. That matters because the fix differs. Quality points at the creative, engagement at the hook, conversion at the landing page.

How often should the quality ranking digest run?

Weekly, and keep the window at 7 days. Meta only populates the ranking fields over a recent rolling window, so a 30 day window returns UNKNOWN for every ad and the digest silently reports nothing. Weekly also gives each fix time to land before the next report checks it.

Can I use this without the AI explanation step?

Yes. The AI step is optional and only turns the flagged rows into readable fix suggestions. Delete that node and the workflow still produces the digest, which is the part that matters. The detection logic sits entirely in the Code node before it and has no AI dependency at all.

Want this watching your ad accounts?

30 minutes, free. Bring your Meta account. You leave with a plan for a weekly digest of every below-average ad ranked by the money behind it, whether you build it or we do.

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.