ScreenshotNeo

BlogHow-to

How to Schedule Weekly Website Screenshots for a Visual Change Report

Capture the same pages every week with Playwright and GitHub Actions, then compare screenshots to spot visual changes.

By the ScreenshotNeo team4 October 20269 min read

To schedule weekly website screenshots for a visual change report, use Playwright to capture the same pages in a consistent browser environment, then use GitHub Actions to run the capture script weekly. Save the images as workflow artifacts, or use Playwright Test’s screenshot assertions to compare them with reviewed baselines. The browser capture and the schedule are separate parts of the workflow.

This guide builds a small scheduled capture job, explains how to compare its results, and covers timing, stability, troubleshooting, and alternatives. The example runs every Monday at 14:17 UTC; change the schedule to suit your team.

1. Choose the pages and comparison method

Start with a fixed list of URLs that matter to your report. Give each page a stable name, such as home or pricing, so its weekly images can be matched reliably. Decide whether to capture the visible viewport or the full page. Full-page captures are useful for long pages but take more time and can include content far below the fold.

Choose how the report will show changes:

  • Save weekly screenshots as artifacts: a reviewer downloads or views each run’s images and compares them with earlier captures. This is simple, but you need a process for retaining and reviewing the files.
  • Use Playwright Test visual assertions: toHaveScreenshot() compares a screenshot with a stored baseline. This fits teams that want comparison assertions and baselines managed alongside test code. Review baseline changes deliberately.
  • Use Percy with Playwright: Percy’s Playwright integration sends snapshots to Percy for comparison and review. Consider it if you want a hosted review workflow; check its current setup and product details before adopting it.

These approaches differ in where comparison data lives and how reviewers approve changes. Choose based on your review process and the amount of CI and account setup your team wants to maintain.

2. Create a Playwright capture script

The following Node.js example opens each page in Chromium, waits for the page to load, and writes a full-page PNG. It uses Playwright’s browser automation API directly, so the job produces an archive of screenshots rather than a baseline assertion.

import { chromium } from 'playwright';
import { mkdir } from 'node:fs/promises';

const pages = [
  { name: 'home', url: 'https://example.com/' },
  { name: 'pricing', url: 'https://example.com/pricing' },
];

const outputDir = process.env.OUTPUT_DIR ?? 'screenshots';
await mkdir(outputDir, { recursive: true });

const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
  viewport: { width: 1440, height: 1000 },
  deviceScaleFactor: 1,
});

try {
  const page = await context.newPage();
  for (const item of pages) {
    const response = await page.goto(item.url, {
      waitUntil: 'networkidle',
      timeout: 60_000,
    });
    if (!response || !response.ok()) {
      throw new Error(`${item.url} returned ${response?.status() ?? 'no response'}`);
    }
    await page.screenshot({
      path: `${outputDir}/${item.name}.png`,
      fullPage: true,
      animations: 'disabled',
    });
  }
} finally {
  await context.close();
  await browser.close();
}

Replace the example URLs and page names with your own. The script uses one browser context and one page in sequence, which keeps this small example straightforward. If a page requires authentication, add a secure login or storage-state setup; never commit credentials or session files containing secrets.

networkidle waits for network activity to settle, but some sites keep connections open or load analytics continuously. If it times out, use waitUntil: 'domcontentloaded' or load, then wait for a meaningful page-specific selector with page.locator('main').waitFor(). A fixed delay can help with known late rendering, but it increases job time and may still capture too early.

For a baseline-driven Playwright Test workflow, write a test that captures a page and asserts its screenshot. For example, in a Playwright Test file:

import { test, expect } from '@playwright/test';

test('pricing page visual baseline', async ({ page }) => {
  await page.setViewportSize({ width: 1440, height: 1000 });
  await page.goto('https://example.com/pricing', { waitUntil: 'networkidle' });
  await expect(page).toHaveScreenshot('pricing.png', { fullPage: true });
});

Playwright creates or updates screenshot baselines through its test workflow. Treat a baseline update as a reviewed change: first decide whether the visual difference is expected, then update and commit the baseline using the documented Playwright process. See the Playwright visual comparisons documentation for assertion behavior and baseline setup.

3. Schedule the capture with GitHub Actions

Add a workflow file such as .github/workflows/weekly-screenshots.yml on the repository’s default branch. Scheduled workflows run against the latest commit on that branch. GitHub Actions uses POSIX cron syntax; by default, scheduled times are UTC. GitHub also documents optional IANA timezone support.

name: Weekly website screenshots

on:
  schedule:
    - cron: '17 14 * * 1'
  workflow_dispatch:

jobs:
  capture:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright Chromium
        run: npx playwright install --with-deps chromium

      - name: Capture pages
        run: node scripts/capture.mjs

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        with:
          name: weekly-website-screenshots
          path: screenshots/
          if-no-files-found: error
          retention-days: 30

The cron expression 17 14 * * 1 means every Monday at 14:17 UTC under the default timezone behavior. The minute is intentionally not zero: GitHub warns scheduled events can be delayed during high load, particularly around the beginning of an hour. A scheduled start is not an exact-time guarantee. Weekly runs are supported; GitHub documents a shortest schedule interval of five minutes.

The workflow includes workflow_dispatch so you can start a capture manually from the Actions interface. Set artifact retention to the period your reviewers need, subject to your repository’s artifact settings. GitHub’s Playwright CI guide also describes running Playwright in CI and uploading reports as artifacts.

If your organization supports timezone configuration for scheduled workflows, use the documented timezone field with an IANA timezone such as America/New_York when the desired schedule follows local time through daylight-saving changes. Otherwise, calculate the UTC schedule you intend to use and revisit it when local offsets change. Consult GitHub’s current scheduling documentation for the accepted syntax and timezone behavior.

4. Keep comparisons useful

A screenshot comparison is only meaningful when the capture conditions are reasonably consistent. Keep the browser, viewport, device scale factor, page state, and capture timing stable. Run the job in a consistent CI environment; Playwright’s CI guidance discusses containers as one way to make the environment more consistent for screenshot work.

  • Dynamic text: timestamps, prices, rotating promotions, and personalized content can create changes unrelated to a code release. Identify the source and decide whether to freeze, mask, or ignore it in the comparison workflow.
  • Animations: animated elements may land on different frames. The script disables screenshot animations; you can also apply site-specific CSS where appropriate.
  • Fonts and images: wait for essential assets to render before capture. A page load event does not guarantee that every lazy image is loaded. Full-page screenshot behavior and site loading strategy can affect what appears below the fold.
  • Cookie banners and overlays: consent prompts and chat widgets can obscure content or vary by session. Use a consistent consent state, or capture a clean page with a tool that handles these overlays.
  • Page failures: check the navigation response and fail the job when a target page cannot be captured. Otherwise, an error page may be archived as if it were a valid weekly snapshot.

Do not automatically treat every pixel difference as a defect. Review the change in context, especially when pages contain third-party content or frequently changing data.

5. Understand performance, reliability, and cost

For a small URL list, a sequential browser script is easy to maintain. Runtime grows with the number of pages, navigation delays, and image size. Reusing a browser context avoids starting a new browser for every URL; parallel pages may shorten elapsed time, but they also use more memory and can increase load on target sites. Start with the smallest concurrency that meets your schedule.

GitHub Actions scheduling is suitable for a weekly cadence, but scheduled events can be delayed during high load and may not start at the exact requested minute. Keep the workflow on the default branch, choose a cron minute away from the hour boundary, and use a manual trigger for an immediate capture. Preserve artifacts for as long as the report needs to compare them; artifact retention and workflow execution are part of your CI usage and repository settings.

Playwright itself is an open-source browser automation framework. Your workflow’s costs and limits depend on the CI provider, run duration, storage, and any hosted visual-review service you choose. The dossier does not establish comparative pricing or performance for Percy or other providers, so check current terms directly before choosing one.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. A GET request returns a PNG, JPEG, WebP, or PDF; the same parameter names used by other screenshot APIs also work, which can make switching easier. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.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())));

For a weekly report, call the endpoint once for each URL in your scheduled job and store the returned files with a stable page name and capture date. ScreenshotNeo can accept consent banners like a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free 1,000 screenshots per month, with no card required.

Troubleshooting

Symptom Likely cause Fix
The scheduled workflow never runs The workflow file is not on the default branch, or the cron expression/timezone is not what you expect. Put the file on the default branch, check the five cron fields, confirm the timezone setting, and use workflow_dispatch to validate the job manually.
The run starts later than scheduled GitHub Actions schedule events can be delayed during high load, especially at the start of an hour. Choose a minute other than 0 and treat the schedule as a target time rather than an exact-time guarantee.
npm ci fails The repository has no lockfile or its lockfile does not match package metadata. Commit a current lockfile and ensure Playwright is listed in the project dependencies.
Playwright cannot launch Chromium The browser binary or required system packages are missing from the runner. Install the browser and dependencies with npx playwright install --with-deps chromium.
Navigation times out at networkidle Some pages continue network activity or keep connections open. Wait for domcontentloaded or load, then wait for a page-specific selector or a short, deliberate delay.
The screenshot is blank or incomplete The site may still be rendering, navigation may have failed, or content may load lazily. Check the response status, wait for the main content and essential assets, and verify full-page behavior on the target page.
Images differ every week without a code change Dynamic content, animations, third-party widgets, font rendering, or inconsistent viewport settings are changing. Stabilize the capture conditions, disable animations, and handle volatile page regions in the comparison strategy.
Artifact upload says no files were found The script failed, used a different output directory, or wrote no screenshots. Check the capture step’s logs and ensure OUTPUT_DIR and the artifact path match.

FAQ

Can I schedule more than one capture a week?

Yes. GitHub Actions accepts recurring cron schedules and documents a shortest interval of five minutes. Pick a cadence and time that fit your review needs.

Will weekly screenshots detect every website change?

No. They show the page state at capture time. A short-lived change that appears and disappears between runs may not be captured.

Should I use a full-page screenshot or a viewport screenshot?

Use a viewport capture to monitor the visible first screen and a full-page capture when changes throughout the page matter. Keep the choice consistent across runs.

Can I use this for authenticated pages?

Yes, if the CI job can authenticate securely. Store credentials in repository secrets and avoid committing credentials or reusable session state to source control.

Where should the weekly report live?

For a starter workflow, upload screenshots as Actions artifacts. If reviewers need an approval interface or long-term comparison history, evaluate a visual review service such as Percy and confirm its current retention and account terms.