ScreenshotNeo

BlogHow-to

How to Schedule Website Screenshots in India and Send Alerts on WhatsApp

Build a recurring website screenshot workflow with India-local timing, change detection, and WhatsApp alerts using Playwright and Meta’s business messaging API.

By the ScreenshotNeo team4 October 202611 min read

To schedule website screenshots in India and send WhatsApp alerts, connect four separate steps: a scheduler triggers a capture, a browser renders the page, a comparison detects meaningful visual changes, and a WhatsApp Business integration sends an alert. Playwright can handle capture and image comparison; a cron-capable scheduler can run the job in the Asia/Kolkata time zone. WhatsApp delivery requires a configured business messaging account and API access. The research sources do not verify one service that provides all four steps as a single India-local product.

This guide builds the capture and comparison steps with Playwright, then shows where to connect an approved WhatsApp Business Platform sender. Before deploying, verify the scheduler’s actual time-zone setting and current WhatsApp setup, template, opt-in, and pricing rules in the official documentation.

1. Choose the workflow and confirm its boundaries

Keep the responsibilities separate so failures are diagnosable:

  1. Scheduler: starts the job at the required India-local time.
  2. Capture: opens the target URL in a consistent browser environment and saves a screenshot.
  3. Change detection: compares the new capture with a known-good baseline.
  4. Delivery: sends a WhatsApp message when the comparison indicates a change or an operational failure needs attention.
Approach What it covers What to verify
Self-hosted Playwright Programmable browser capture and screenshot assertions. You must supply a recurring trigger, storage, baseline management, and WhatsApp integration.
Hosted scheduled screenshot service May manage recurring jobs and capture history. Check time zone, retention, change detection, webhook behavior, and whether WhatsApp is actually supported.
Screenshot plus webhook plus WhatsApp sender Composes services with a webhook or your own small integration. Email or webhook delivery does not mean direct WhatsApp delivery; the message-sending step is separate.

ScreenshotAPI.net documents recurring captures with cron-based schedules and retained results; its schedule example alone does not establish India-local time-zone behavior. Allscreenshots documents email or webhook delivery and optional change-only notifications, but that is not evidence of direct WhatsApp delivery. Confirm current product documentation before relying on either service for a production schedule. [ScreenshotAPI.net scheduled screenshot features] [Allscreenshots scheduling and delivery]

2. Set up a repeatable Playwright capture

The example below is a small Node.js project. It saves each capture with a timestamp, compares it against a reference image, and exits with a distinct status when the page differs. It uses Playwright’s screenshot assertion support, which requires the @playwright/test package. Run the job in the same operating system, browser version, viewport, and dependency environment each time; browser rendering can vary across environments. [Playwright visual comparisons]

Install

mkdir website-watch
cd website-watch
npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium
mkdir -p captures baselines

Create capture.mjs:

import { chromium, expect } from '@playwright/test';
import { mkdir, copyFile } from 'node:fs/promises';
import path from 'node:path';

const targetUrl = process.env.TARGET_URL;
const baselinePath = process.env.BASELINE_PATH ?? 'baselines/home.png';
const outputDir = process.env.OUTPUT_DIR ?? 'captures';
const viewport = {
  width: Number(process.env.VIEWPORT_WIDTH ?? 1440),
  height: Number(process.env.VIEWPORT_HEIGHT ?? 1000),
};

if (!targetUrl) {
  console.error('Set TARGET_URL to an absolute http:// or https:// URL.');
  process.exit(2);
}

await mkdir(outputDir, { recursive: true });
const stamp = new Date().toISOString().replaceAll(':', '-');
const currentPath = path.join(outputDir, `page-${stamp}.png`);
const browser = await chromium.launch({ headless: true });

try {
  const page = await browser.newPage({ viewport, deviceScaleFactor: 1 });
  page.setDefaultNavigationTimeout(45_000);
  const response = await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });

  if (!response || !response.ok()) {
    throw new Error(`Navigation returned ${response?.status() ?? 'no response'}`);
  }

  // Wait for a stable page-specific landmark when available.
  const readySelector = process.env.READY_SELECTOR;
  if (readySelector) {
    await page.locator(readySelector).waitFor({ state: 'visible', timeout: 15_000 });
  } else {
    await page.waitForTimeout(1500);
  }

  await page.screenshot({ path: currentPath, fullPage: true, animations: 'disabled' });

  if (process.env.CREATE_BASELINE === '1') {
    await copyFile(currentPath, baselinePath);
    console.log(`Baseline saved: ${baselinePath}`);
    process.exitCode = 0;
  } else {
    try {
      await expect(page).toHaveScreenshot(path.basename(baselinePath), {
        fullPage: true,
        animations: 'disabled',
        maxDiffPixelRatio: Number(process.env.MAX_DIFF_PIXEL_RATIO ?? 0.01),
        timeout: 10_000,
      });
      console.log(`No visual change. Capture: ${currentPath}`);
      process.exitCode = 0;
    } catch (error) {
      console.log(`Visual change detected. Capture: ${currentPath}`);
      console.log(error.message);
      // A distinct code lets the scheduler trigger an alert for changes.
      process.exitCode = 10;
    }
  }
} finally {
  await browser.close();
}

Set the baseline once after checking that the page is in the expected state. For Playwright’s snapshot assertion to resolve a named baseline predictably, put the reference image where Playwright expects snapshots for your project, or adapt the example to compare explicit image files with an image-diff library. A simple setup is to use Playwright Test’s toHaveScreenshot inside a test project and keep its generated snapshot alongside the test. The script above shows the workflow and status handling; do not assume a standalone expect(page).toHaveScreenshot() call discovers an arbitrary file path in baselines/.

For a test-project implementation, create playwright.config.mjs and watch.spec.mjs:

// playwright.config.mjs
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: '.',
  testMatch: 'watch.spec.mjs',
  snapshotPathTemplate: 'baselines/{arg}{ext}',
  use: {
    browserName: 'chromium',
    headless: true,
    viewport: { width: 1440, height: 1000 },
    deviceScaleFactor: 1,
  },
});
// watch.spec.mjs
import { test, expect } from '@playwright/test';

const url = process.env.TARGET_URL;
test('monitored page matches its visual baseline', async ({ page }) => {
  if (!url) throw new Error('Set TARGET_URL');
  const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45_000 });
  expect(response?.ok(), `HTTP status: ${response?.status()}`).toBeTruthy();
  if (process.env.READY_SELECTOR) {
    await page.locator(process.env.READY_SELECTOR).waitFor({ state: 'visible', timeout: 15_000 });
  } else {
    await page.waitForTimeout(1500);
  }
  await expect(page).toHaveScreenshot('home.png', {
    fullPage: true,
    animations: 'disabled',
    maxDiffPixelRatio: Number(process.env.MAX_DIFF_PIXEL_RATIO ?? 0.01),
  });
});

Generate the reference intentionally, inspect it, then run comparisons:

TARGET_URL='https://example.com' npx playwright test --update-snapshots
TARGET_URL='https://example.com' npx playwright test

Do not automatically replace a baseline whenever a difference occurs. That can turn an unwanted change into the new accepted state. Review the image and promote it to baseline only when the change is expected.

Capture settings to tune

  • Full page or viewport: full-page screenshots catch below-the-fold changes but take longer and can be affected by lazy-loaded content. Viewport captures are faster and more stable for a fixed area.
  • Readiness: prefer waiting for a page-specific selector. A fixed delay is simple but may be too short under load or unnecessarily long on fast pages.
  • Viewport and scale: keep viewport dimensions and device scale factor constant. Use one monitor job per meaningful breakpoint if responsive layout matters.
  • Animations: disable them in snapshots to avoid capturing arbitrary animation frames.
  • Dynamic regions: timestamps, rotating banners, user-specific content, and ads can create noise. Hide or mask known volatile elements in the capture or comparison logic, and document those exclusions.
  • Authentication: use a dedicated test account and secure storage-state or cookie handling if the page is private. Do not commit credentials or session files.
  • Consent dialogs: consent state can alter page appearance. Use an explicitly chosen consent state consistently, and follow the site’s consent requirements.

3. Schedule the job in India Standard Time

India uses the IANA time-zone identifier Asia/Kolkata. Configure the scheduler explicitly to that zone where supported; do not assume that a cron expression is interpreted in Indian time. If a scheduler only accepts UTC, convert the desired India time to UTC and account for any daylight-saving behavior if the workflow is later moved to another region.

Linux cron example

If the machine itself is configured for India time, a cron entry can run every day at 09:00 local time. Set environment variables and paths explicitly:

CRON_TZ=Asia/Kolkata
0 9 * * * cd /opt/website-watch && TARGET_URL='https://example.com' /usr/bin/node capture.mjs >> /var/log/website-watch.log 2>&1

CRON_TZ support depends on the cron implementation. Check the host’s cron documentation and confirm the next scheduled run from its logs. If unsupported, set the server time zone deliberately or use a scheduler with a per-job time-zone field. Hosted scheduling products also differ in how they interpret cron schedules, so verify the configured zone in the service dashboard.

Run from a service scheduler

For a container platform or CI scheduler, configure the schedule in the platform’s own time-zone controls, store TARGET_URL and any secret values as protected variables, and retain screenshots and logs in durable storage. A repository scheduler commonly uses UTC unless its documentation says otherwise; calculate the UTC equivalent and check it whenever schedule requirements change.

4. Connect change detection to WhatsApp

WhatsApp messaging is a separate delivery step. Meta’s WhatsApp Business Platform Cloud API setup requires a Meta business portfolio, a WhatsApp Business Account, and a business phone number. A personal WhatsApp account is not established by these sources as a programmable sender. Review Meta’s current Developer Hub documentation for setup, webhooks, opt-in, rate limits, pricing, and available partner routes before sending messages. [Meta WhatsApp Cloud API setup] [Meta WhatsApp Business Platform Developer Hub]

The integration pattern is:

  1. The scheduled job captures and compares the page.
  2. On a change, it calls your alert handler or sends a webhook event.
  3. The handler formats a concise message and submits it through the configured WhatsApp Business API sender.
  4. The handler records the provider response and delivery status, without logging access tokens or sensitive page content.

Keep the sender-specific request implementation behind an adapter: the exact endpoint, authorization, recipient format, message template rules, and payload depend on the WhatsApp account and API configuration. Do not copy an unverified payload into production. For scheduled proactive alerts, check whether the current platform rules require an approved message template and recipient opt-in for the situation.

A useful alert should include the monitored URL or name, capture time in India time, whether a visual difference or capture failure occurred, and a link or reference to the stored image. Avoid sending full-page images or sensitive page data unless recipients are authorized and the channel is appropriate.

5. Store screenshots and manage baselines

  • Use a durable, access-controlled location for images if the job runs on ephemeral workers.
  • Name captures with the monitored site and an unambiguous UTC timestamp; display alerts in Asia/Kolkata.
  • Set a retention period based on investigation needs and storage budget. The cited sources do not establish a universal retention period or price.
  • Keep baselines separate from ordinary captures and record who approved a baseline update.
  • Use a per-site baseline. A shared baseline can cause one page’s image to be compared against another page.
  • Limit concurrent browser jobs to the memory and CPU available on the runner. Queue work if several large pages are captured at once.

6. Troubleshooting

Symptom Likely cause Fix
The job runs at the wrong India time The scheduler interpreted cron in UTC or another host time zone. Set the job zone to Asia/Kolkata, or convert the schedule to UTC. Verify the scheduler’s next-run display and logs.
Screenshot comparison fails every run Browser version, operating system, fonts, viewport, content, or device scale changed. Pin the runtime and browser; use fixed fonts and viewport; stabilize page state; mask known dynamic regions.
The capture is blank or incomplete Navigation ended before client rendering, the page blocked automation, or the selected readiness signal was wrong. Check the HTTP response and browser logs; wait for a meaningful selector; use a longer navigation timeout where justified; inspect whether the site permits automated access.
Lazy-loaded sections are missing The browser never scrolled those sections into view before capture. Scroll through the page before taking a full-page capture, wait for images to load, or capture the relevant sections individually.
Alerts arrive on every run Small dynamic changes exceed the comparison threshold, or the comparison uses unstable rendering. Stabilize environment and content, exclude volatile areas, and tune the pixel-difference threshold against reviewed examples.
No WhatsApp message arrives The capture step did not invoke the sender, the API configuration is incomplete, or recipient, opt-in, template, or delivery requirements were not met. Inspect the handler and provider response; confirm business account and phone setup, recipient details, current messaging rules, and delivery status in Meta’s documentation and tools.
Duplicate alerts arrive Retries or overlapping runs submitted the same change more than once. Use an idempotency key based on site, baseline version, and detected change; serialize runs per site; distinguish retryable transport errors from final failures.
Job times out or exhausts memory Large pages, many concurrent browsers, or an excessive full-page capture. Reduce concurrency, use a fixed viewport where sufficient, raise resource limits, and set explicit navigation and job timeouts.

7. Reliability, performance, and cost

Reliability: record a status for each run: started, captured, compared, changed or unchanged, alert submitted, and delivery outcome if available. Retry transient network errors with a bounded backoff, but do not retry indefinitely or allow overlapping runs to overwrite state. Send an operational alert for repeated capture failures separately from visual-change alerts.

Performance: full-page captures and browser startup dominate small monitoring jobs. Reuse a browser process when a worker handles multiple URLs, while giving each page an isolated context. Use selector-based readiness instead of long fixed sleeps. Schedule large batches with controlled concurrency and avoid unnecessary image dimensions.

Cost: self-hosting consumes compute, storage, and engineering time; WhatsApp costs and eligibility depend on the current account, message type, and platform rules. Hosted screenshot services may have their own quotas, retention limits, and pricing, which should be checked directly. The research sources do not establish India-specific total cost for this combined workflow.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It can handle the screenshot request; you still connect the result or a change event to your own WhatsApp Business messaging integration. The API supports a single GET request for a screenshot, while its documentation covers the available capture options: ScreenshotNeo API docs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Cookie banners, popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents take screenshots. One thousand screenshots a month are free with no card; paid plans start at $5 for 3,000. Schedule the API call with your chosen scheduler and send WhatsApp alerts through your separately configured business messaging integration. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Can I send screenshot alerts to a personal WhatsApp account?

The documented programmable route here is the WhatsApp Business Platform. The sources do not establish that a personal account can serve as the API sender for this workflow.

Does a cron expression guarantee India Standard Time?

No. Cron syntax does not by itself specify a time zone. Configure or verify the scheduler’s zone explicitly.

Should every visual difference send a message?

Usually not. Use a stable baseline, filter known dynamic areas, and alert on reviewed comparison rules or repeated changes where appropriate.

Can a webhook send the WhatsApp message by itself?

A webhook can trigger your integration. A separate, configured WhatsApp Business sender must submit the message and report its outcome.