Build a competitor-mention monitor
Summarise what Northwind's rivals are doing each week — feeds in, summary out.
Published Sep 12, 2025
Keeping half an eye on the competition is one of those tasks that everyone agrees matters and nobody actually does. Northwind’s rivals all publish blogs and changelogs, but reading every one each week is the kind of job that quietly falls off the list — until a client mentions a competitor’s new feature and catches the team flat-footed.
This script turns that habit into an automation. Once a week it pulls the latest posts from a list of competitor RSS feeds, hands the headlines to Claude, and asks for a five-bullet recap of what the rivals shipped. The summary lands in your inbox — no dashboards to open, no feeds to scroll, just a short briefing you can read with your coffee.
What you’ll need
- The RSS or Atom feed URL for each competitor blog or changelog you want to
watch. Most blogs expose one at
/rss,/feedor/atom.xml. - An Anthropic API key saved as
ANTHROPIC_API_KEYin Script Properties — see Store API keys and secrets securely. - The email address the weekly recap should be sent to.
The script
// The competitor feeds to watch. Add or remove URLs as your market shifts.
const FEEDS = [
'https://competitor1.example/blog/rss',
'https://competitor2.example/blog/rss',
];
// Where the weekly recap is emailed.
const RECIPIENT = '[email protected]';
// How many recent headlines to pull from each feed.
const HEADLINES_PER_FEED = 5;
/**
* Pulls recent headlines from each competitor feed, asks Claude to recap
* what they shipped, and emails the summary. Designed to run weekly.
*/
function competitorRecap() {
const items = [];
// 1. Fetch each feed and pull out its most recent headlines.
for (const feed of FEEDS) {
try {
const xml = UrlFetchApp.fetch(feed).getContentText();
// Grab every <title> in the feed. The first title is the feed's own
// name, so skip it and keep the next few — those are the posts.
const titles = [...xml.matchAll(/<title[^>]*>([^<]+)<\/title>/g)]
.map((m) => m[1])
.slice(1, 1 + HEADLINES_PER_FEED);
if (titles.length) {
items.push('From ' + feed + ': ' + titles.join('; '));
}
} catch (err) {
// A dead or slow feed should not sink the whole run.
Logger.log('Could not read ' + feed + ': ' + err);
}
}
// 2. If no feed returned anything, stop before calling the API.
if (!items.length) {
Logger.log('No headlines collected — nothing to summarise.');
return;
}
// 3. Ask Claude for a tight, five-bullet recap of the headlines.
const summary = callClaude(
'Summarise what these competitors of Northwind shipped this week, ' +
'in 5 bullets:\n' + items.join('\n'),
'claude-sonnet-4-6',
500
);
// 4. Email the recap.
GmailApp.sendEmail(RECIPIENT, 'Competitor recap', summary);
Logger.log('Sent competitor recap to ' + RECIPIENT + '.');
}
/**
* Minimal Anthropic API call. The key lives in Script Properties — it
* is never pasted into the code.
*/
function callClaude(prompt, model = 'claude-haiku-4-5-20251001', maxTokens = 400) {
const key = PropertiesService.getScriptProperties()
.getProperty('ANTHROPIC_API_KEY');
const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
payload: JSON.stringify({
model,
max_tokens: maxTokens,
messages: [{ role: 'user', content: prompt }],
}),
muteHttpExceptions: true,
});
return JSON.parse(res.getContentText()).content[0].text.trim();
}
How it works
competitorRecaploops over theFEEDSlist and fetches each feed’s raw XML withUrlFetchApp.- For each feed it pulls every
<title>element with a regular expression. The first title in an RSS feed is the feed’s own name, so it is skipped; the next five headlines (HEADLINES_PER_FEED) are the recent posts. - Each fetch is wrapped in a
try/catchso one dead or slow feed logs a warning and the run carries on with whatever else it collected. - If no feed returned anything, it logs a message and stops before spending an API call.
- The collected headlines are joined into a single block and sent to Claude Sonnet, which is asked for a five-bullet recap of what the rivals shipped.
- The summary is emailed to
RECIPIENTwithGmailApp.sendEmail.
Example run
Suppose the two feeds return these headlines this week:
- competitor1: “Now supporting SSO”, “New pricing tiers”, “API v3 is live”
- competitor2: “Faster exports”, “Dark mode arrives”, “Mobile app beta”
Claude turns that into an inbox-ready recap:
Competitor recap
- Competitor 1 shipped single sign-on, closing a gap enterprise buyers ask about.
- Competitor 1 reworked its pricing into new tiers — worth checking how it compares to ours.
- Competitor 1 released API v3, signalling a push on integrations.
- Competitor 2 focused on polish: faster exports and a long-requested dark mode.
- Competitor 2 opened a mobile app beta — a space Northwind does not yet play in.
That five-line briefing is the whole point: enough to know what changed, short enough to actually read.
Trigger it
Run this once a week so the recap arrives on a predictable day:
- In the Apps Script editor, open Triggers (the clock icon).
- Click Add Trigger.
- Choose the
competitorRecapfunction, an event source of Time-driven, a Week timer, the day you want (Monday works well), and an early-morning time slot. - Save and approve the authorisation prompt the first time.
Watch out for
- Feed formats vary. The regular expression assumes standard RSS where the feed
name is the first
<title>. Atom feeds and some hand-rolled feeds order things differently — check the first run and adjust the.sliceif a feed’s name leaks into the headlines. - It reads headlines, not full posts, so the recap is only as informative as the titles. A vendor with vague headlines (“Product update — March”) will produce a vague bullet.
- “This week” is a prompt instruction, not a real filter. The script always sends the latest five headlines per feed regardless of date; if a feed is quiet, old posts will reappear in the recap.
- Each run makes one paid API call. The cost is small at this scale, but every feed you add lengthens the prompt — keep an eye on it if you watch many rivals.
Related
Summarize chat and Slack exports
Digest Northwind's long Slack conversations into recaps — for catch-up after PTO.
Updated Dec 5, 2025
Digest daily news into a personal briefing
Summarise headlines on a schedule — Northwind morning briefing for Awadesh.
Updated Nov 19, 2025
Summarize pros and cons from reviews
Extract what Northwind customers love and hate about each product into a single roll-up.
Updated Nov 3, 2025
Summarize YouTube videos into notes
Turn transcripts into Northwind study summaries — one Doc per video.
Updated Oct 6, 2025
Summarize a folder of PDFs into a briefing
Digest Northwind's research PDFs into one structured briefing Doc.
Updated Aug 27, 2025