Competitor Ad Email Digest: The Weekly Issue Your Team Will Actually Read
Build a weekly competitor ad email digest your team actually reads: scheduled API scan, diff, LLM summary, and HTML email via Resend, SES, or SMTP.

Sections
Your competitor dashboard has a problem nobody talks about: someone has to open it. A competitor ad email digest flips that. Instead of asking your team to remember a URL, you put the week's new competitor creative directly in the one inbox they already check forty times a day. This guide builds that digest end to end, from scheduled scan to sent email.
TL;DR: Dashboards wait to be opened; email arrives. Build a weekly competitor ad email digest with four moving parts: a scheduled scan that pulls each competitor's recent ads through an ad library API, a diff that keeps only what's new, an LLM pass that turns raw JSON into three analyst sentences, and an HTML email sent through Resend, SES, or SMTP. Cap it at five creatives, one velocity table, and a three-minute read. Total cost: roughly 10-15 API credits a week for a five-competitor list.
Why a Digest Beats Your Dashboard
Every team that takes competitor ad monitoring seriously eventually builds a dashboard. Airtable, Notion, a Looker board, something. And every one of those dashboards follows the same curve: heavy traffic for two weeks, a trickle for a month, then silence. The data keeps updating. Nobody looks.
That is not a discipline failure. It's an interface mismatch. A dashboard is pull-based: it requires someone to feel curious, remember the tool exists, and go open it during a week already full of launches and client fires. Curiosity loses that fight almost every time.
Email is push-based. It lands in the inbox your media buyers triage every morning regardless of how busy the week gets. The marketing lead who would never open a competitor board will skim a tight weekly email between two meetings, because skimming email is already part of the job. You're borrowing an existing habit instead of trying to install a new one.
There's a second advantage that gets less attention. A digest forces editorial judgment. A dashboard shows everything, that's exactly why it overwhelms. An email with five creatives and a one-paragraph summary shows only what an analyst decided matters. The constraint is the feature. Slack alerts work on the same principle at a faster cadence, and a Slack competitor ad alert pairs well with this build, but Slack is where messages go to scroll away. Email persists, gets forwarded to creative teams, and shows up in Monday planning docs.
The Competitor Ad Email Digest Spec
Before writing a line of code, define what one issue of the competitor ad email digest contains. Vague digests die. The spec below fits a three-minute read and has a clear answer for every section's reason to exist.
1. The headline number. One sentence at the top: "Competitors launched 23 new ads this week, up from 14 last week." A single velocity signal tells your team whether the market is heating up before they read anything else.
2. New ads by competitor. A short table: competitor name, number of new creatives detected this week, delta versus last week. Five rows maximum. The deltas matter more than the absolutes. A brand jumping from 2 launches to 11 is preparing a push, and that's the kind of thing a media buyer wants to know on Monday rather than discover in the auction as rising CPMs three weeks later (the CPM calculator helps you put a number on what that pressure costs you).
3. Notable creative. Three to five ads, each with a thumbnail, the hook, the platform, and one sentence on why it earned a slot. Longest runtime, highest heat score, or a brand-new creative angle the competitor hasn't tried before. Not "everything new." The best of what's new.
4. The analyst paragraph. Two to four LLM-written sentences synthesizing the week: "Both DTC competitors shifted spend toward UGC video on TikTok, while Brand X retired its long-running discount angle." This is the part people actually quote in meetings.
5. One suggested action. A single line: brief the creative team on the new angle, check the ad fatigue curve on your own top performer, nothing more. Digests that demand work get filtered. Digests that offer one cheap action get read.
That's the whole issue. No "in case you missed it" section, no archive links, no padding.
Pipeline Overview: Scan, Diff, Summarize, Send
The build has four stages, and each is small enough to write in an afternoon:
- Scheduled scan. A cron-triggered script pulls each tracked competitor's recent ads through the AdLibrary API. One pull per brand, all platforms at once.
- Diff. Compare the pull against a stored set of ad identifiers from previous weeks. Keep only ads you haven't seen. Compute per-competitor counts and deltas.
- Summarize. Feed the new ads' copy and metadata to an LLM with a tight editorial prompt. Get back the analyst paragraph and per-ad one-liners.
- Send. Render an HTML template and ship it through Resend, Amazon SES, or your SMTP server.
You can assemble the same flow in n8n, Make, or Zapier if your team prefers visual builders. This guide uses plain Node.js because the logic is simple enough that a no-code canvas adds more overhead than it removes, and because a script in a repo survives team turnover better than a workflow in someone's personal account. The broader architecture pattern is covered in how to automate competitor ad monitoring end to end. The digest is that pattern with an editorial layer on top.
A note on the data source. Meta's own Ad Library API is free and worth knowing, but it returns political and social-issue ads (plus all ad types for the EU and UK over the last 12 months), covers only Meta platforms, and requires identity verification and app review before your first call. For a commercial digest spanning multiple platforms, this build uses the AdLibrary API: a paid upgrade with one adl_ key, eleven platforms, and performance signals like heat score and estimated spend on every ad. The trade-offs between the two are unpacked in free vs paid ad library APIs.
The Scheduled Scan: One Pull Per Competitor
Resolving and saving competitors happens once, not weekly. The advertiser search endpoint is free and maps a brand name to its platform IDs across Meta, Google, and LinkedIn in one call:
curl -G "https://adlibrary.com/api/advertisers/search" \
-H "Authorization: Bearer adl_your_api_key" \
--data-urlencode "q=gymshark" \
--data-urlencode "country=US"
Take the IDs from best_match, then save the brand so future pulls are a single request:
curl -X POST "https://adlibrary.com/api/advertisers" \
-H "Authorization: Bearer adl_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name":"Gymshark","domain":"gymshark.com","meta_page_ids":["123456789"]}'
The weekly scan then calls curate on each saved advertiser. One credit per brand per session, and it returns recent ads across every platform the brand runs on, deduplicated:
// scan.js — runs every Friday via cron
const KEY = process.env.ADLIBRARY_API_KEY;
async function pullAds(advertiserId) {
const res = await fetch(
`https://adlibrary.com/api/advertisers/${advertiserId}/curate`,
{ method: "POST",
headers: { Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json" },
body: "{}" }
);
if (!res.ok) throw new Error(`curate ${advertiserId}: ${res.status}`);
const data = await res.json();
return [
...(data.meta?.ads ?? []),
...(data.google?.ads ?? []),
...(data.linkedin?.ads ?? []),
];
}
If you also want keyword-level coverage, say every new ad in your category rather than from named brands, add one search call with daysBack: 7 and sortField: "-first_seen" so the newest creatives come first. Each search costs one credit and a failed search refunds automatically. Respect the documented limits (10 requests per minute, 10,000 per day per key) and honor the Retry-After header on any 429. A five-competitor scan sits nowhere near them. More patterns for this layer live in the Python ad library scripts cookbook and the Node.js ad intelligence service guide.
The Diff: Separating New From Seen
The diff is what turns a data pull into a digest. Every ad in the response carries an ad_key, the stable identifier across calls. Store every key you've already reported, and this week's "new ads" are simply the keys you haven't:
import fs from "node:fs";
const SEEN_FILE = "./seen-ad-keys.json";
const seen = new Set(
fs.existsSync(SEEN_FILE) ? JSON.parse(fs.readFileSync(SEEN_FILE, "utf8")) : []
);
function diff(ads) {
const fresh = ads.filter((ad) => !seen.has(ad.ad_key));
fresh.forEach((ad) => seen.add(ad.ad_key));
fs.writeFileSync(SEEN_FILE, JSON.stringify([...seen]));
return fresh;
}
A JSON file is genuinely enough for a handful of competitors. Swap in SQLite when the set grows past a few thousand keys or when multiple scripts need it, which is also the point where you might graduate the whole system into a proper automated swipe file.
Velocity deltas fall out of the same data. Count new ads per competitor this week, store the count alongside last week's, and you have the digest's table:
const velocity = {};
for (const ad of fresh) {
velocity[ad.advertiser_name] = (velocity[ad.advertiser_name] ?? 0) + 1;
}
// { "Gymshark": 11, "Alo Yoga": 4, "Vuori": 2 }
One subtlety: platforms sometimes re-index creatives, so an "old" ad can resurface with a new key. A cheap guard is to also check first_seen. If the timestamp is older than 30 days, treat the ad as a re-index, count it nowhere, and keep your deltas honest. Inflated counts kill the digest's credibility fast, and credibility is the whole product.

The LLM Summary: Analyst Notes, Not Raw JSON
Raw ad data makes a terrible email. "Brand X launched 11 ads" is a fact; "Brand X moved its entire new-creative budget into 9:16 UGC video and dropped the discount messaging it ran all spring" is intelligence. The LLM layer's job is converting the first into the second.
Two inputs make the summary good. The first is the ad metadata you already have: advertiser name, platform, ad copy, format, runtime, heat score. The second, for the two or three most interesting creatives, is a deeper teardown from the enrichment endpoint. One credit per ad, and it returns a structured analysis of the hook, offer, and persuasion mechanics, plus a full transcript for video. That's the same engine behind AI ad enrichment in the app, and enriching competitor creatives via API covers it in depth. Reserve it for the digest's featured ads rather than running it on everything. The curation rules further down decide which ads earn the spend.
The prompt matters more than the model. Constrain it hard:
You are writing 3 sentences for a weekly competitor ad digest
read by a busy paid-social team. Input: JSON array of this
week's new competitor ads (copy, platform, format, runtime).
Rules:
- Lead with the single most decision-relevant change.
- Name brands and platforms. No hedging, no "it seems".
- Mention an ad only if it implies a strategy shift.
- Never invent numbers not present in the input.
- 3 sentences. No greeting, no sign-off.
The "never invent numbers" line is load-bearing. An LLM that hallucinates a spend figure into one issue poisons trust in every future issue. Keep impressions as the bucketed ranges the platforms report and label spend as estimated, because that's what the underlying data actually is.
Per-ad one-liners use the same approach with an even tighter frame: one sentence, name the creative angle, say why it matters. You're producing the caption under each thumbnail, nothing longer.
The HTML Email: Build It, Then Send It
Email HTML is stuck in 2008 and fighting it wastes your afternoon. Tables for layout, inline styles, a single column 600px wide, and system fonts. Your digest needs exactly four components: a header with the headline number, the velocity table, three to five creative cards (thumbnail, brand, platform badge, one-liner), and the analyst paragraph. A creative card is thirty lines of template:
const card = (ad, note) => `
<table width="100%" style="margin-bottom:24px">
<tr><td width="180">
<img src="${ad.preview_img_url}" width="180" alt="${ad.advertiser_name} ad"/>
</td><td style="padding-left:16px;font-family:sans-serif">
<strong>${ad.advertiser_name}</strong> · ${ad.platform}
· ${ad.days_count} days live
<p style="margin:8px 0 0">${note}</p>
</td></tr>
</table>`;
Hotlink the preview_img_url from the API response rather than re-hosting. The URLs are stable enough for an email that gets read within a week. Keep total weight under 100KB so Gmail doesn't clip the message, which is another argument for five creatives instead of twenty.
For sending, three options cover every team:
Resend is the fastest path for a developer-built digest. Three lines of Node and the free tier's 100 emails per day is far more than a weekly internal digest needs:
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: "[email protected]",
to: ["[email protected]"],
subject: `Competitor ads weekly — ${headline}`,
html: renderDigest(fresh, velocity, summary),
});
Amazon SES makes sense when your infrastructure already lives on AWS and you want the digest inside the same IAM and billing perimeter. The SES developer guide walks through domain verification and moving out of the sandbox. Budget an hour for that the first time.
Plain SMTP via Nodemailer works when the company mail server is non-negotiable, or when IT would rather grant an SMTP relay than approve a new vendor. Slightly more config, zero new accounts.
Whichever you pick, send from an authenticated domain. SPF, DKIM, and DMARC are no longer optional even for internal mail. Google's sender guidelines apply DMARC requirements to anyone mailing Gmail or Workspace inboxes, and your team's inboxes almost certainly qualify. Schedule the send for Friday at 2pm or Monday at 8am local time. Mid-week sends compete with everything. Edge-of-week sends get read during planning.
Curation Rules That Keep the Digest Short
Most automated digests fail the same way: they grow. Someone asks for one more section, the ad count creeps from five to fifteen, and within a quarter the email takes ten minutes to read so nobody reads it. Codify the constraints in the script, where feature requests can't erode them:
- Five creatives, hard cap. When the week produces forty new ads, rank and cut. Score each ad on runtime, heat, and novelty, then take the top five. The scoring can be three lines:
runtime * 0.4 + heat/1000 * 0.4 + isNewAngle * 0.2. - Novelty beats volume. A competitor's eleventh variation of a proven concept is less digest-worthy than another competitor's first attempt at a new format. Variations belong in your swipe file. Firsts belong in the email.
- No section without a decision attached. Before adding anything, name the decision it informs. Velocity table → budget pacing (pressure-test the implications with the ad spend estimator). Featured creative → next creative brief. Can't name one? It doesn't go in.
- One link out per item, maximum. The digest links each featured ad to its source and stops. It is a briefing, never a portal.
- Empty weeks ship honestly. When competitors launch nothing, send two sentences saying so. "Quiet week: 3 new ads, no strategy shifts" maintains the habit and is itself a signal. Skipped weeks break the ritual, and the ritual is most of the value.
Resist personalization, too. One digest for the whole team creates shared context: the media buyer and the creative lead argue from the same five ads. Per-person versions fragment that conversation. If clients want their own flavor, that's a separate artifact with its own economics, covered in automating client competitor ad reports.
A Worked Example Issue
Here's a full competitor ad email digest issue for a fictional DTC activewear brand tracking four competitors. Reading time: about two and a half minutes.
Subject: Competitor ads weekly — 23 new ads, Gymshark up 5x
23 new competitor ads this week, up from 14. Most of the jump is one brand.
Competitor New ads vs last week Gymshark 11 +9 Alo Yoga 6 +1 Vuori 4 -2 Ten Thousand 2 -4 This week's creative:
- Gymshark · TikTok · video — 9 of 11 new ads are 9:16 creator videos with a "gym anxiety" hook. First time they've led with an emotional angle over product features.
- Gymshark · Facebook · carousel — New five-frame carousel pairing each product with a creator testimonial. Heat score 740 within four days.
- Alo Yoga · Instagram · image — Studio-shot stills returning after two quarters of pure UGC. Possibly a brand-refresh test.
- Vuori · YouTube · video — First 16:9 long-form spot in our tracking window, 62 days live and still running, which suggests it's converting.
Analyst note: Gymshark is staging a creator-led push around an emotional hook rather than product features, and committing real volume to it. Alo's return to studio photography is worth one week of watching before reading anything into it. Vuori's long-form YouTube spot has out-lived everything else they've launched this quarter.
One action: Brief creative on an emotional-hook UGC test before Gymshark's angle saturates the feed.
Notice what's absent. No charts, no "trends to watch" listicle, no methodology section. Every line either reports a change or names an action, and the unbucketed numbers are all things the API actually returns: ad counts, runtimes, heat scores.
Unsubscribe-Proofing Your Competitor Ad Email Digest
Internal digests don't get unsubscribed with a button. They get unsubscribed with a Gmail filter, silently, one teammate at a time. Treating "keep it useful" as an engineering requirement is what separates a competitor ad email digest that survives a year from one that dies in six weeks.
The failure modes are predictable. The digest repeats itself, because the diff is leaky and last week's ads resurface. It gets long, because nobody enforced the cap. It goes generic, because the LLM prompt drifted toward summary-speak nobody would say aloud. Each one teaches readers the email can be skipped, and that lesson only needs teaching once. The fixes are the disciplines already built above: a strict diff, a hard cap, a constrained prompt.
Beyond those, three habits keep read rates up:
- Make the subject line carry the headline. "Competitor ads weekly — Gymshark up 5x" gets opened on the subject alone. "Your weekly digest" trains people to archive unread.
- Close the loop publicly. When the digest catches something early, say so in the next issue: "The UGC shift we flagged on the 6th is now Gymshark's dominant format." Proof the email has predictive value is the strongest retention mechanic available.
- Audit quarterly with one question. Ask three readers: "What did the digest change about your work this quarter?" Blank stares mean the curation rules need tightening, never that the email needs more sections.
And even for an internal list, wire in the courtesy of a real opt-out. RFC 8058 one-click unsubscribe headers come free with Resend and SES configuration, and an honest exit beats a resentful filter. If someone does leave, that's feedback, and it arrives with a name attached.
Ship the First Issue This Friday
The complete build is a scheduled scan, a JSON diff, one constrained LLM prompt, and an email template: maybe 150 lines of code and an afternoon. The weekly run costs a credit per tracked competitor for the curate pull, a credit for any category-wide search, and a few enrichment credits for the featured creatives. Call it 10-15 credits a week for a five-competitor digest, which leaves a Business plan's 1000+ monthly credits mostly untouched for the rest of your research.
Don't aim for the perfect first issue. Ship a version with just the velocity table this Friday, add the LLM summary the week after, and let the worked example above be the target rather than the starting point. The habit you're building in your readers matters more than any single issue's polish.
API access ships with the Business plan at €329/mo, including 1000+ monthly credits and free integration help, meaning the team will get on a call and wire this exact pipeline into your stack with you. A weekly competitor ad email digest is the highest-read intelligence artifact you can automate, and it's one cron job away. Compare plans and send issue #1 before the week ends.
Frequently Asked Questions
How much does a weekly competitor ad email digest cost to run?
For five tracked competitors: one curate credit per brand per weekly scan, one credit for an optional category keyword search, and two or three enrichment credits for featured creatives. Roughly 10-15 credits a week, or 40-60 a month, on the AdLibrary Business plan (€329/mo, 1000+ credits). Email sending is effectively free at internal-list volume on Resend's free tier or SES.
Can I build the digest with Meta's free Ad Library API instead?
Partly. Meta's free API covers political and social-issue ads worldwide, and all ad types only for the EU and UK over the last 12 months, on Meta platforms alone. It also requires identity verification and app review, and tokens expire every 60 days. If your competitors advertise commercially across TikTok, YouTube, or Google, the free API can't see those ads, so the digest would have blind spots exactly where the velocity story often is.
Why email instead of a Slack alert or a dashboard?
They serve different cadences. Slack alerts work for same-day signals like a competitor launching a big campaign. Dashboards work for ad-hoc deep dives. A weekly competitor ad email digest is the synthesis layer: push-based like Slack, curated unlike a dashboard, and persistent enough to be forwarded and referenced in planning. Many teams run the Slack alert and the weekly digest from the same scan-and-diff pipeline.
How do I stop the digest from repeating ads it already showed?
Dedupe on the ad_key field, the stable identifier each ad carries across API calls. Store every key you've reported in a JSON file or SQLite table and filter incoming ads against it. Also check first_seen timestamps: if a "new" ad's first-seen date is over 30 days old, it's likely a re-indexed creative and shouldn't count toward velocity deltas.
What should the LLM summary never do?
Invent numbers, hedge, or pad. Constrain the prompt to three sentences, require brand and platform names, and forbid any figure not present in the input data. Impressions should stay as the bucketed ranges platforms report, and spend should always be labeled as estimated. One hallucinated number costs the digest the trust that took months to build.
Related Articles

Slack Competitor Ad Alerts: A 30-Minute Build
Build Slack competitor ad alerts in 30 minutes: an incoming webhook, a Node poll script with first_seen filtering, dedup on ad_key, and cron scheduling.

Automate Client Competitor Ad Reports: The Section That Builds Itself
Automate the competitor ad section of client reports: watchlists, scheduled curate-and-diff, AI enrichment, and Slides or PDF output at ~10 credits/client.

n8n Competitor Ad Monitoring: 6 Workflows on the AdLibrary API
Build n8n competitor ad monitoring with 6 workflows on the AdLibrary API: daily digests, Slack alerts, Sheets logs, enrichment, and winners scans.

How to Automate Competitor Ad Monitoring End to End
Build Slack competitor ad alerts in 30 minutes: an incoming webhook, a Node poll script with first_seen filtering, dedup on ad_key, and cron scheduling.

Python Ad Library API Scripts: A Working Cookbook
Five copy-paste Python ad library API scripts: resolve brands, search into pandas, track watchlists, scan winners, and batch-enrich ads with caching.

How to Monitor Competitor Ads: The Ongoing Playbook for Diff-Detection and Alerts
Stop refreshing ad libraries manually. Build a competitor ad monitoring system with scheduled API pulls, diff-detection, and Slack alerts that tells you what changed since last week.

Competitor Ad Monitoring: Setup Guide
A practitioner setup guide for competitor ad monitoring — manual spot-checks, semi-automated tracking, alert cadences, and multi-platform coverage explained step by step.

Run Paid Ads from Claude Code: The Terminal-First Ad-Ops Playbook
Step-by-step playbook for Google, Meta, and LinkedIn ad ops from Claude Code in your terminal — read-before-write rollout, weekly rituals, what stays human.