Competitor Ad and Landing Page Copy Tracking
Watch competitor landing pages for headline, CTA, and positioning changes - early signal that a campaign or product launch is coming.
The scenario
A competitor is about to relaunch positioning. The first signal is usually a small wording change on the homepage hero: an old tagline gives way to a new one, the CTA shifts from "Get a demo" to "Start free," the hero image is swapped. A week later they buy ads matching the new line. A month later there's a press release.
You'd like to catch the first signal - the wording change - within hours, not weeks.
The problem
You can't manually re-read fifteen competitor homepages every day. Wayback Machine helps after the fact, not in real time. Screenshot-diff tools fire on cosmetic changes (a button color, a font load) that aren't strategic signal.
How Verid solves it
Use a CSS extractor to pin specific elements - hero headline, sub-headline, primary CTA, secondary CTA, social-proof line. Verid extracts each as a named field. The predicate fires on a change to any of them.
For a coarser watch that catches less-predictable layouts (e.g. competitor homepages that change layout entirely on each redesign), use a full_page extractor as a separate secondary monitor.
Build the monitor
Extraction config - Targeted CSS
{
"method": "css",
"fields": {
"hero_headline": "section.hero h1",
"hero_subheadline": "section.hero p.hero-subtitle",
"primary_cta": "section.hero a.btn-primary",
"secondary_cta": "section.hero a.btn-secondary",
"social_proof_line": ".social-proof span"
}
}
Extraction config - Full-page backup
{ "method": "full_page" }
Predicate
For the targeted monitor:
{ "type": "any_field_changes" }
For the full-page hash:
{ "type": "any_field_changes" }
You'll run them as two separate monitors with different delivery destinations - the targeted one is the actionable channel, the full-page one is the "FYI, something changed" backup.
Create the monitor
curl -X POST https://api.verid.dev/v1/monitors \
-H "Authorization: Bearer vrd_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Competitor homepage copy - competitor.com",
"url": "https://competitor.com",
"schedule_interval_seconds": 14400,
"fetch_mode": "browser",
"extract_config": {
"method": "css",
"fields": {
"hero_headline": "section.hero h1",
"hero_subheadline": "section.hero p.hero-subtitle",
"primary_cta": "section.hero a.btn-primary",
"secondary_cta": "section.hero a.btn-secondary",
"social_proof_line": ".social-proof span"
}
},
"diff_predicate": { "type": "any_field_changes" },
"deliveries": [
{ "type": "slack", "webhookUrl": "https://hooks.slack.com/services/..." }
]
}'
SDK:
import { VeridClient } from '@verid.dev/sdk';
const client = new VeridClient({ apiKey: 'vrd_your_api_key' });
await client.monitors.create({
name: 'Competitor homepage copy - competitor.com',
url: 'https://competitor.com',
schedule_interval_seconds: 14400,
fetch_mode: 'browser',
extract_config: {
method: 'css',
fields: {
hero_headline: 'section.hero h1',
hero_subheadline: 'section.hero p.hero-subtitle',
primary_cta: 'section.hero a.btn-primary',
secondary_cta: 'section.hero a.btn-secondary',
social_proof_line: '.social-proof span',
},
},
diff_predicate: { type: 'any_field_changes' },
deliveries: [
{ type: 'slack', webhookUrl: 'https://hooks.slack.com/services/...' },
],
});
What the webhook delivers
{
"id": "del_01H...",
"fired_at": "2026-05-08T09:00:00Z",
"diff": {
"fields_changed": ["hero_headline", "hero_subheadline", "primary_cta"],
"before": {
"hero_headline": "The fastest API for change detection",
"hero_subheadline": "Built for developers.",
"primary_cta": "Get a demo",
"secondary_cta": "View docs",
"social_proof_line": "Trusted by 1,200+ teams"
},
"after": {
"hero_headline": "Detect changes anywhere - at any scale",
"hero_subheadline": "From a single SKU to your entire competitive set.",
"primary_cta": "Start free",
"secondary_cta": "View docs",
"social_proof_line": "Trusted by 1,500+ teams"
}
}
}
That's a positioning pivot: from "fast API for developers" to "broad capability for everyone," with a free-trial-first CTA and a social-proof bump. A marketing team would notice that within minutes.
Caveats & tips
- Pin to one URL per landing page. Homepage, pricing page, and feature pages each warrant their own monitor - they change independently.
- A/B test rotations cause noise. If a competitor runs A/B tests on the hero headline, you'll see it flip between variants on subsequent runs. That's actually useful intelligence - you're seeing them experiment in real time - but consider grouping the related fields into a composite predicate or rate-limiting the alert downstream.
- Browser mode matters. Most modern marketing sites lazy-load text inside React or another framework. Without
fetch_mode: 'browser', you may extract empty strings. - Schedule quietly. Every 4-6 hours is plenty. Marketing copy doesn't change minute-by-minute, and a quieter cadence means your Slack channel only sees real signal.
Related use cases
For competitor pricing-page copy, see competitor price tracking. For tech-stack swaps that precede repositioning, see competitor tech stack changes. For SEO meta-tag drift, see SEO title & meta description drift.
Related use cases
Ship this monitor today
5 monitors free, no credit card. Set up takes about a minute.
Get started free