Competitor Website Monitoring
Take screenshots of competitor websites with a simple API, then automate competitor monitoring to catch pricing, feature, and design changes. JavaScript and Python examples.
Last updated: 2026-07-05
Try ScreenshotAPI free
200 free screenshots/month. No credit card required.
To take a screenshot of a competitor's website, send its public URL to a screenshot API and save the image it returns — no browser or Puppeteer setup. With ScreenshotAPI that's a single GET https://screenshotapi.to/api/v1/screenshot?url=https://competitor.com&fullPage=true request authenticated with your x-api-key header. Capture one competitor page on demand, or run the same call on a schedule and pixel-diff each new capture against the last to turn ad-hoc competitor screenshots into automated competitor monitoring. This page shows both: a one-line capture first, then a full monitoring pipeline in JavaScript and Python.
Take a Single Competitor Screenshot
If you just need to grab a competitor's page right now — for a teardown, a pitch deck, or a swipe file — one request does it. Point the API at the public URL and write the response to a file:
bashcurl "https://screenshotapi.to/api/v1/screenshot?url=https://competitor.com/pricing&fullPage=true&blockAds=true&removeCookieBanners=true" \ -H "x-api-key: sk_live_your_key_here" \ --output competitor-pricing.png
The same call in JavaScript, saving the image to disk:
javascriptimport { writeFile } from "node:fs/promises"; const params = new URLSearchParams({ url: "https://competitor.com/pricing", fullPage: "true", blockAds: "true", removeCookieBanners: "true", }); const response = await fetch( `https://screenshotapi.to/api/v1/screenshot?${params}`, { headers: { "x-api-key": process.env.SCREENSHOT_API_KEY } } ); await writeFile("competitor-pricing.png", Buffer.from(await response.arrayBuffer()));
A few parameters matter when the target is a competitor's site:
fullPage=truecaptures the entire scrollable page (whole pricing table, whole landing page), not just the viewport.blockAds=trueandremoveCookieBanners=truestrip ads and consent dialogs so the image is clean and diffs stay stable.stealthMode=truemasks the headless-browser fingerprint when a competitor's site uses bot detection.type=pdfreturns an archivable PDF instead of a PNG when you want a paper-trail record of what a page looked like on a given date.
That single call is the building block. To catch changes over time instead of eyeballing captures manually, schedule it and compare each new screenshot against the previous one — the rest of this page builds exactly that.
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:
- Configure targets: List competitor URLs you want to monitor (pricing pages, homepages, feature pages).
- Schedule captures: Run a daily or weekly cron job that captures each URL.
- Compare screenshots: Diff each new capture against the previous one.
- Alert on changes: When the difference exceeds your threshold, send a notification with the visual diff.
- 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
javascriptconst 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
pythonimport 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:
yamlname: 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:
javascriptconst 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:
javascriptconst 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
| Pack | Credits | Price | Per screenshot |
|---|---|---|---|
| Starter | 1,000 | $9 | $0.0090 |
| Growth | 5,000 | $29 | $0.0058 |
| Pro | 25,000 | $99 | $0.0040 |
| Scale | 100,000 | $299 | $0.0030 |
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
| Feature | Visualping / Rival CI | ScreenshotAPI + Custom Code |
|---|---|---|
| Setup time | Minutes | Hours |
| Customization | Limited | Full control |
| Per-page pricing | Yes ($$$) | Per-credit (flexible) |
| API access | Webhooks only | Full REST API |
| Region cropping | Some tools | You build it |
| Data ownership | Vendor-hosted | Your infrastructure |
| Alerting | Built-in | You 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
Get 200 free screenshots per month. No credit card required. Sign up free →
Frequently asked questions
How do I take a screenshot of a competitor's website?
Send the competitor's public URL to a screenshot API and save the image it returns. With ScreenshotAPI: GET https://screenshotapi.to/api/v1/screenshot?url=https://competitor.com&fullPage=true with your x-api-key header, and write the response to a PNG. No browser or Puppeteer setup required. Add fullPage=true to capture the entire scrollable page, blockAds=true and removeCookieBanners=true for a clean image, stealthMode=true if the site blocks bots, and type=pdf if you want an archivable PDF instead.
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 200 free screenshots per month to try the API. No credit card required.