Competitor Website Monitoring

Track competitor website changes with automated screenshots. Detect pricing updates, feature launches, and design changes before your team misses them.

Last updated: 2026-03-25

Try ScreenshotAPI free

5 free credits. No credit card required.

Start for free

Why Monitor Competitor Websites

Your competitors update their websites constantly. They change pricing, launch new features, A/B test landing pages, update messaging, and reposition their products. If you only check their sites manually once a quarter, you are always reacting to changes weeks or months after they happen.

Competitor website monitoring gives you an automated way to track these changes. Instead of relying on someone to remember to check competitor sites, you get alerts when something changes, complete with visual evidence of what is different.

The most valuable changes to track include:

  • Pricing changes: Rate increases, new tiers, discounted plans, or restructured pricing models.
  • Feature launches: New capabilities, integrations, or product expansions.
  • Messaging updates: Changed value propositions, new taglines, or repositioned target audiences.
  • Design overhauls: Major redesigns that signal a rebrand or new strategic direction.
  • New pages: Landing pages for new products, campaigns, or market segments.

How ScreenshotAPI Enables Competitor Tracking

ScreenshotAPI captures any public URL as a screenshot. Combined with pixel-diff comparison, this creates a complete competitor monitoring system:

  1. Configure targets: List competitor URLs you want to monitor (pricing pages, homepages, feature pages).
  2. Schedule captures: Run a daily or weekly cron job that captures each URL.
  3. Compare screenshots: Diff each new capture against the previous one.
  4. Alert on changes: When the difference exceeds your threshold, send a notification with the visual diff.
  5. Archive history: Store all captures for historical analysis.

This approach detects any visual change, not just text changes. A new hero image, a redesigned pricing table, or a repositioned CTA button all trigger detection.

Implementation Guide

Competitor Monitoring Script

JavaScript

javascript
const axios = require("axios"); const fs = require("fs"); const path = require("path"); const { PNG } = require("pngjs"); const pixelmatch = require("pixelmatch"); const crypto = require("crypto"); const API_KEY = process.env.SCREENSHOT_API_KEY; const SNAPSHOTS_DIR = "./competitor-snapshots"; const COMPETITORS = [ { name: "Competitor A", pages: [ { label: "pricing", url: "https://competitor-a.com/pricing" }, { label: "features", url: "https://competitor-a.com/features" }, { label: "homepage", url: "https://competitor-a.com" }, ], }, { name: "Competitor B", pages: [ { label: "pricing", url: "https://competitor-b.com/pricing" }, { label: "homepage", url: "https://competitor-b.com" }, ], }, ]; async function captureScreenshot(url) { const response = await axios.get("https://screenshotapi.to/api/v1/screenshot", { params: { url, width: 1440, height: 900, type: "png", waitUntil: "networkidle", delay: 2000, }, headers: { "x-api-key": API_KEY }, responseType: "arraybuffer", }); return Buffer.from(response.data); } function getSnapshotDir(competitorName, pageLabel) { const dir = path.join(SNAPSHOTS_DIR, competitorName, pageLabel); fs.mkdirSync(dir, { recursive: true }); return dir; } async function monitorCompetitor(competitor) { const changes = []; for (const page of competitor.pages) { const dir = getSnapshotDir(competitor.name, page.label); const latestPath = path.join(dir, "latest.png"); const newScreenshot = await captureScreenshot(page.url); if (fs.existsSync(latestPath)) { const previous = PNG.sync.read(fs.readFileSync(latestPath)); const current = PNG.sync.read(newScreenshot); const { width, height } = previous; const diff = new PNG({ width, height }); const mismatchedPixels = pixelmatch( previous.data, current.data, diff.data, width, height, { threshold: 0.1 } ); const diffPercent = (mismatchedPixels / (width * height)) * 100; if (diffPercent > 2.0) { const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); fs.writeFileSync(path.join(dir, `${timestamp}-diff.png`), PNG.sync.write(diff)); fs.writeFileSync(path.join(dir, `${timestamp}.png`), newScreenshot); changes.push({ competitor: competitor.name, page: page.label, url: page.url, diffPercent: diffPercent.toFixed(2), }); } } fs.writeFileSync(latestPath, newScreenshot); } return changes; } async function runMonitoringCycle() { const allChanges = []; for (const competitor of COMPETITORS) { const changes = await monitorCompetitor(competitor); allChanges.push(...changes); } if (allChanges.length > 0) { await sendNotification(allChanges); } return allChanges; } async function sendNotification(changes) { const message = changes .map((c) => `${c.competitor} - ${c.page}: ${c.diffPercent}% changed (${c.url})`) .join("\n"); await axios.post(process.env.SLACK_WEBHOOK_URL, { text: `Competitor changes detected:\n${message}`, }); } runMonitoringCycle();

Python

python
import os import io import hashlib from pathlib import Path from datetime import datetime import httpx from PIL import Image, ImageChops import numpy as np API_KEY = os.environ["SCREENSHOT_API_KEY"] SNAPSHOTS_DIR = Path("./competitor-snapshots") COMPETITORS = [ { "name": "competitor-a", "pages": [ {"label": "pricing", "url": "https://competitor-a.com/pricing"}, {"label": "features", "url": "https://competitor-a.com/features"}, ], }, { "name": "competitor-b", "pages": [ {"label": "pricing", "url": "https://competitor-b.com/pricing"}, {"label": "homepage", "url": "https://competitor-b.com"}, ], }, ] def capture_screenshot(url: str) -> bytes: response = httpx.get( "https://screenshotapi.to/api/v1/screenshot", params={ "url": url, "width": 1440, "height": 900, "type": "png", "waitUntil": "networkidle", "delay": 2000, }, headers={"x-api-key": API_KEY}, ) response.raise_for_status() return response.content def compare_screenshots(previous: bytes, current: bytes) -> float: img_prev = Image.open(io.BytesIO(previous)).convert("RGB") img_curr = Image.open(io.BytesIO(current)).convert("RGB") diff = ImageChops.difference(img_prev, img_curr) diff_array = np.array(diff) changed = np.count_nonzero(diff_array.sum(axis=2)) total = diff_array.shape[0] * diff_array.shape[1] return (changed / total) * 100 def monitor_all(): changes = [] for competitor in COMPETITORS: for page in competitor["pages"]: snapshot_dir = SNAPSHOTS_DIR / competitor["name"] / page["label"] snapshot_dir.mkdir(parents=True, exist_ok=True) latest_path = snapshot_dir / "latest.png" current = capture_screenshot(page["url"]) if latest_path.exists(): previous = latest_path.read_bytes() diff_pct = compare_screenshots(previous, current) if diff_pct > 2.0: timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%S") (snapshot_dir / f"{timestamp}.png").write_bytes(current) changes.append({ "competitor": competitor["name"], "page": page["label"], "url": page["url"], "diff_percent": round(diff_pct, 2), }) latest_path.write_bytes(current) if changes: send_notification(changes) return changes def send_notification(changes: list): webhook = os.environ.get("SLACK_WEBHOOK_URL") if webhook: lines = [f"{c['competitor']} - {c['page']}: {c['diff_percent']}% changed" for c in changes] httpx.post(webhook, json={"text": "Competitor changes:\n" + "\n".join(lines)}) monitor_all()

GitHub Actions Schedule

Run competitor monitoring on a daily schedule without infrastructure:

yaml
name: Competitor Monitor on: schedule: - cron: "0 9 * * *" # Daily at 9 AM UTC workflow_dispatch: jobs: monitor: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Restore snapshots uses: actions/cache@v4 with: path: ./competitor-snapshots key: competitor-snapshots-${{ github.run_number }} restore-keys: competitor-snapshots- - name: Run competitor monitor env: SCREENSHOT_API_KEY: ${{ secrets.SCREENSHOT_API_KEY }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} run: | pip install httpx pillow numpy python monitor.py

What to Monitor

Pricing Pages

Pricing changes are the highest-value intelligence. Track the main pricing page plus any subpages for individual plans or enterprise tiers.

Feature and Product Pages

New feature announcements, integration pages, and product comparison charts reveal your competitor's roadmap priorities.

Homepage and Landing Pages

Messaging changes on the homepage signal shifts in positioning, target audience, or value proposition. New landing pages often indicate marketing campaigns or new market segments.

Job Postings Page

While not a visual monitoring target, changes to a competitor's careers page (new engineering roles, new offices) signal growth and strategic direction.

Advanced Monitoring Patterns

Region-Specific Comparison

Focus your comparison on the most relevant page sections by cropping before diffing:

javascript
const sharp = require("sharp"); async function comparePricingTable(previousBuffer, currentBuffer) { const region = { left: 100, top: 400, width: 1200, height: 600 }; const previousCrop = await sharp(previousBuffer).extract(region).toBuffer(); const currentCrop = await sharp(currentBuffer).extract(region).toBuffer(); return compareImages(previousCrop, currentCrop); }

Mobile and Desktop Variants

Monitor how competitors optimize for different devices:

javascript
const viewports = [ { name: "desktop", width: 1440, height: 900 }, { name: "mobile", width: 375, height: 812 }, ];

Historical Trend Reports

Build a dashboard that displays how competitor pages have evolved over time using your archived snapshots. This turns raw change detection into strategic intelligence.

Pricing Estimate

ScenarioCompetitor PagesFrequencyCredits/MonthRecommended Plan
2 competitors, 3 pages each6Daily180Starter (500 credits, $20)
5 competitors, 5 pages each25Daily750Growth (2,000 credits, $60)
10 competitors, 10 pages each100Daily3,000Pro (10,000 credits, $200)
20 competitors, 10 pages each200Daily6,000Pro (10,000 credits, $200)

Each screenshot uses one credit. For mobile + desktop monitoring, double the count. Credits never expire, so unused credits carry forward. Visit the pricing page for full plan details.

Competitor Monitoring vs. Dedicated Tools

FeatureVisualping / Rival CIScreenshotAPI + Custom Code
Setup timeMinutesHours
CustomizationLimitedFull control
Per-page pricingYes ($$$)Per-credit (flexible)
API accessWebhooks onlyFull REST API
Region croppingSome toolsYou build it
Data ownershipVendor-hostedYour infrastructure
AlertingBuilt-inYou choose (Slack, email, etc.)

Dedicated tools like Visualping and Rival CI are excellent for non-technical teams. For developers and product teams that want full control, ScreenshotAPI provides the capture layer while you own the comparison logic, storage, and alerting. See the website monitoring use case for monitoring your own site, or the archiving use case for long-term visual records.

Getting Started

  1. Sign up for 5 free credits.
  2. List 3-5 competitor URLs you want to track competitor changes on.
  3. Run the monitoring script manually to establish baselines.
  4. Set up a daily schedule via cron or GitHub Actions.
  5. Configure Slack or email alerts for detected changes.

Read the API documentation for the full parameter reference.

Frequently asked questions

Is it legal to take screenshots of competitor websites?

Taking screenshots of publicly accessible web pages is generally legal and is a common competitive intelligence practice. However, always respect robots.txt, terms of service, and applicable laws in your jurisdiction. Consult legal counsel if you have concerns about specific monitoring activities.

How often should I monitor competitor sites?

For pricing pages and feature lists, daily monitoring catches time-sensitive changes. For design and branding changes, weekly captures are sufficient. For general competitive intelligence, monthly snapshots provide a good historical record without excessive credit usage.

Can I monitor specific sections of a page instead of the whole page?

Yes. Capture the full page with ScreenshotAPI, then use an image processing library (sharp, Pillow) to crop specific regions like pricing tables, feature comparison charts, or navigation menus. This lets you run more focused comparisons.

How do I reduce false positives from dynamic content?

Set a higher pixel-diff threshold (2-5%) to ignore minor changes like ad rotations or timestamps. You can also crop out known dynamic regions before comparison, or focus on specific page sections that matter most.

Related resources

Start capturing screenshots today

Create a free account and get 5 credits to try the API. No credit card required. Pay only for what you use.