← All use cases
Content & MarketingCSSFull Page

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.

Verid Use Cases·4 min read

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.

Ship this monitor today

5 monitors free, no credit card. Set up takes about a minute.

Get started free