Capture Webpage Automation in 2026

Automate webpage capture with Node-cron, GitHub Actions cron, AWS Lambda scheduled events, and no-code automation (n8n, Make). Build change detection, visual monitoring, and CI screenshot pipelines.

Node.jsNode-cronGitHub Actions Lambdan8nApril 2026

Common Automation Patterns

Scheduled Capture with node-cron

npm install node-cron node-fetch
import cron from 'node-cron';
import { writeFile } from 'fs/promises';
import { join } from 'path';

const URLS = [
  { name: 'competitor-pricing', url: 'https://competitor.com/pricing' },
  { name: 'our-homepage',       url: 'https://mysite.com' },
  { name: 'status-page',        url: 'https://status.myservice.com' },
];

async function captureURL(name, url) {
  const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
    method: 'POST',
    headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ url, fullPage: true, format: 'png', blockAds: true }),
  });

  const date = new Date().toISOString().split('T')[0];
  const filename = `${name}-${date}.png`;
  await writeFile(join('./captures', filename), Buffer.from(await res.arrayBuffer()));
  console.log(`✓ Captured ${name} → ${filename}`);
}

// Run every day at 9 AM
cron.schedule('0 9 * * *', async () => {
  console.log('Starting daily capture run...');
  for (const { name, url } of URLS) {
    await captureURL(name, url);
  }
}, {
  timezone: 'America/New_York',
});

Pixel-Diff Change Detection

Detect when a monitored page changes by comparing screenshots with pixelmatch:

npm install pixelmatch pngjs
import pixelmatch from 'pixelmatch';
import { PNG } from 'pngjs';
import { readFile, writeFile, access } from 'fs/promises';

async function detectChange(name, newBuffer) {
  const baselinePath = `./baselines/${name}.png`;

  // If no baseline, save current as baseline
  try { await access(baselinePath); } catch {
    await writeFile(baselinePath, newBuffer);
    console.log(`✓ Baseline saved for ${name}`);
    return { changed: false, isFirstRun: true };
  }

  const baseline = PNG.sync.read(await readFile(baselinePath));
  const current = PNG.sync.read(newBuffer);

  const { width, height } = baseline;
  const diff = new PNG({ width, height });
  const mismatch = pixelmatch(baseline.data, current.data, diff.data, width, height, { threshold: 0.1 });

  const changePercent = (mismatch / (width * height)) * 100;

  if (changePercent > 0.5) {   // >0.5% pixels changed
    await writeFile(`./diffs/${name}-diff.png`, PNG.sync.write(diff));
    await writeFile(baselinePath, newBuffer);  // update baseline
    return { changed: true, changePercent: changePercent.toFixed(2) };
  }

  return { changed: false, changePercent: changePercent.toFixed(2) };
}

// Alert via Slack webhook
async function alertSlack(name, changePercent) {
  await fetch(process.env.SLACK_WEBHOOK, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `🔔 *Page change detected: ${name}*\n${changePercent}% of pixels changed`,
    }),
  });
}

GitHub Actions Scheduled Capture

GitHub Actions cron jobs are perfect for visual monitoring — free for public repos, and artifacts give you a timestamped archive:

# .github/workflows/capture.yml
name: Daily Page Capture
on:
  schedule:
    - cron: '0 9 * * *'    # 9 AM UTC daily
  workflow_dispatch:         # manual trigger

jobs:
  capture:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }

      - name: Capture screenshots
        env:
          SNAPAPI_KEY: ${{ secrets.SNAPAPI_KEY }}
        run: |
          mkdir -p captures
          node scripts/capture.js

      - name: Upload captures as artifact
        uses: actions/upload-artifact@v4
        with:
          name: captures-${{ github.run_number }}
          path: captures/
          retention-days: 30   # keep 30 days of history

      - name: Check for changes and notify
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        run: node scripts/diff-and-notify.js
GitHub Actions cron has a minimum interval of 5 minutes. For tighter monitoring, use a self-hosted runner or AWS EventBridge + Lambda.

AWS Lambda + EventBridge Scheduled Capture

// lambda/capture-handler.js
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({ region: 'us-east-1' });

export const handler = async (event) => {
  const urls = [
    { name: 'pricing',  url: 'https://competitor.com/pricing' },
    { name: 'homepage', url: 'https://mysite.com' },
  ];

  const date = new Date().toISOString().split('T')[0];
  const results = [];

  for (const { name, url } of urls) {
    const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
      method: 'POST',
      headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ url, fullPage: true, format: 'png' }),
    });

    const buffer = Buffer.from(await res.arrayBuffer());

    await s3.send(new PutObjectCommand({
      Bucket: process.env.BUCKET,
      Key: `captures/${date}/${name}.png`,
      Body: buffer,
      ContentType: 'image/png',
    }));

    results.push({ name, status: 'captured' });
  }

  return { statusCode: 200, body: JSON.stringify(results) };
};
# serverless.yml (Serverless Framework)
functions:
  capture:
    handler: lambda/capture-handler.handler
    timeout: 60
    events:
      - schedule: rate(1 day)   # or: cron(0 9 * * ? *)
    environment:
      SNAPAPI_KEY: ${env:SNAPAPI_KEY}
      BUCKET: my-captures-bucket

No-Code Automation with n8n

n8n's HTTP Request node can call SnapAPI directly — no code required:

  1. Add a Schedule trigger (e.g. "Every day at 9 AM")
  2. Add an HTTP Request node:
    • Method: POST
    • URL: https://api.snapapi.pics/v1/screenshot
    • Headers: X-Api-Key: your_key, Content-Type: application/json
    • Body: {"url": "https://example.com", "fullPage": true, "format": "png"}
  3. Add a Move Binary Data node to handle the PNG response
  4. Add an S3 or Google Drive node to store the capture
  5. Add a Slack node to notify on completion

Make (Integromat) Scenario

Same pattern in Make: Scheduler → HTTP → Google Drive/Dropbox → Slack/Email. SnapAPI's REST API connects natively to any Make HTTP module.

Queue-Based Bulk Capture

For high-volume capture (hundreds of URLs), use a queue to control concurrency and retries:

import PQueue from 'p-queue';

const queue = new PQueue({ concurrency: 5 });  // 5 parallel captures

const urls = [/* hundreds of URLs */];

const results = await Promise.all(
  urls.map((url, i) =>
    queue.add(async () => {
      try {
        const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
          method: 'POST',
          headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
          body: JSON.stringify({ url, format: 'png', fullPage: true }),
        });
        const buffer = Buffer.from(await res.arrayBuffer());
        await writeFile(`captures/${i}.png`, buffer);
        return { url, status: 'ok' };
      } catch (err) {
        return { url, status: 'error', error: err.message };
      }
    })
  )
);

console.log(`Done: ${results.filter(r => r.status === 'ok').length} captures`);
console.log(`Failed: ${results.filter(r => r.status === 'error').length}`);