Monitor competitor pricing pages with scheduled screenshots + webhooks

Product managers want to know when a competitor changes their pricing. Sales wants to know when a competitor updates their comparison page. Compliance wants a dated archive proving what a page looked like on a specific day.

All three needs point at the same primitive: capture a URL on a schedule and route the result somewhere useful. Every hour, every day, every Monday morning. Getsnap.dev's scheduled captures do exactly this, with HMAC-signed webhook delivery so your downstream system can verify the payload actually came from us.

End result: 15 minutes of setup, ~$3/month at 30 pages checked daily. Here's the exact recipe.

The setup

Post a schedule for each URL you want tracked. You can do this via cURL, Node.js, or Python:

curl -X POST https://api.getsnap.dev/v1/schedules \
  -H "X-API-Key: sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Vercel pricing page",
    "cron": "0 8 * * *",
    "timezone": "Europe/Berlin",
    "options": {
      "url": "https://vercel.com/pricing",
      "format": "png",
      "full_page": true,
      "viewport_width": 1440,
      "remove_popups": true,
      "block_ads": true
    },
    "webhook_url": "https://your-app.com/webhooks/getsnap",
    "webhook_secret": "generate-32-random-bytes-here"
  }'

Response includes the schedule ID and computed next_run_at in UTC:

{
  "id": "sched_abc123",
  "name": "Vercel pricing page",
  "next_run_at": "2026-09-21T06:00:00.000Z",
  "is_active": true,
  ...
}

Repeat for each competitor page. To pause a schedule without deleting it, PATCH is_active: false. To fire it right now for testing, POST to /v1/schedules/:id/run.

The webhook receiver

When each scheduled capture completes, we POST a JSON payload to your webhook_url. Because you set a webhook_secret, every delivery includes an X-Webhook-Signature header you can verify.

Node.js example that also forwards the image to Slack:

import express from "express";
import crypto from "node:crypto";

const app = express();

app.post("/webhooks/getsnap",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    // Verify signature
    const ts   = req.header("X-Webhook-Timestamp") ?? "";
    const sig  = req.header("X-Webhook-Signature") ?? "";
    const body = req.body.toString("utf8");
    const expected = "sha256=" + crypto
      .createHmac("sha256", process.env.WEBHOOK_SECRET)
      .update(ts + "." + body)
      .digest("hex");
    if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
      return res.sendStatus(401);
    }
    if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
      return res.sendStatus(401); // reject replays older than 5 min
    }

    const event = JSON.parse(body);
    if (event.event !== "schedule.completed") return res.sendStatus(200);

    // Forward to Slack
    await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        text: `Daily capture for schedule ${event.schedule_id}`,
        blocks: [{
          type: "image",
          image_url: event.url,
          alt_text: "Screenshot",
        }],
      }),
    });

    res.sendStatus(200);
  }
);

That's the entire integration. Every morning at 8 AM Berlin time, the schedule fires, we capture the page, and Slack shows the image.

Automated diff detection

Getting the screenshot is step one. Detecting "did something change" is step two. The simple approach: pixel-hash comparison against yesterday's capture.

import { imageHash } from "image-hash";

// Load current and previous images
const [current, previous] = await Promise.all([
  fetchImage(event.url),
  loadFromS3(`competitor-tracking/${event.schedule_id}/yesterday.png`),
]);

const [hashA, hashB] = await Promise.all([
  hash(current), hash(previous),
]);

if (hashA !== hashB) {
  // Something visibly changed
  await notifyProductTeam({
    schedule: event.schedule_id,
    yesterday: previous.url,
    today: current.url,
  });
}

await saveToS3(`competitor-tracking/${event.schedule_id}/yesterday.png`, current);

You can also use getsnap.dev's extract_metadata option to get the page's HTTP status and Open Graph tags in the same call, which are often more reliable change-detection signals than pixel diffs.

The compliance archive angle

If you're in a regulated industry (financial services, health), being able to prove "this is what our competitor's disclosure page looked like on 2026-05-14" has real value. Store every completed capture in your own S3 bucket. Set the schedule with options.s3_upload:

"options": {
  "url": "https://competitor.com/disclosures",
  "format": "pdf",
  "full_page": true,
  "pdf_page_size": "A4",
  "pdf_title": "Competitor Disclosures Archive",
  "s3_upload": {
    "access_key_id":     "AKIA...",
    "secret_access_key": "...",
    "bucket":            "compliance-archives",
    "region":            "eu-central-1",
    "path":              "competitors/disclosures/{{daily-timestamp}}"
  }
}

The result is a dated PDF in your own bucket for every day the schedule fires. Immutable. Timestamped. Ready for the auditor.

Cost math

30 competitor pages captured daily is 900 captures/month. Getsnap.dev's Starter plan is $9/month for 5,000 captures — you're at 18% utilisation with room for another 4,000 captures for other use cases (OG images, PDF invoices, etc.).

Compared to running your own Playwright cron job on a VPS (~$5/month VPS plus your own maintenance time), getsnap.dev at $9/mo for 5,000 captures is a tight fit for this workload. Against the other hosted APIs it is a unit-price argument rather than a headline-price one: entry plans start lower elsewhere — ApiFlash at $7 and ScreenshotAPI.net at $9 — but for 1,000 captures rather than 5,000, so per thousand they run $7.00 and $9.00 against our $1.80. Scheduled capture is also not universal among them, which matters more than price if it is the feature you came for. Competitor pricing checked 5 October 2026; see the comparison pages for current figures.

Set up your first schedule

Free tier gives you 100 credits/month — enough to test the whole flow at 3-4 URLs/day before committing.

Get Free API Key

Related reading