How to Run Recurring Website Screenshots with a Raspberry Pi
Use a Raspberry Pi, Playwright, and a scheduler to capture a website on a recurring basis. This guide covers setup, code, storage, reliability, and troubleshooting.
Direct answer: Set up a Raspberry Pi with Raspberry Pi OS, install a browser automation tool that supports the Pi’s operating system and hardware, then schedule a script to open a URL and save a screenshot. The example below uses Node.js and Playwright. A cron job runs it every hour; you can change the schedule to suit your needs.
This is a local browser workflow: the Pi loads the page and writes image files to its storage. Browser and package compatibility depend on the chosen board and OS, so confirm that the current Playwright release supports your environment and that its browser binary launches before relying on unattended captures. Raspberry Pi’s setup guides cover headless installation and browser choices, while Playwright documents the screenshot API. Raspberry Pi getting started, Raspberry Pi OS documentation, and Playwright Page API.
1. Prepare the Raspberry Pi
- Choose a Raspberry Pi model based on the current OS and browser automation package compatibility, the page’s complexity, and the amount of storage you need. The sources here do not establish a best model or memory requirement for this workload.
- Use Raspberry Pi Imager to install Raspberry Pi OS. For a Pi without a monitor or desktop, Raspberry Pi recommends Raspberry Pi OS Lite. Imager can configure networking and remote access before first boot.
- Provide boot media, power, and a network connection. Configure SSH or Raspberry Pi Connect if you need to administer the device remotely.
- Update the OS and record its version. Install Node.js using the method recommended for your Raspberry Pi OS release, then check
node --versionandnpm --version. - Install Playwright and its browser according to its current Linux and architecture instructions. Raspberry Pi OS offers Chromium and Firefox, but that does not guarantee that any particular Playwright browser build works on every board and OS release. Verify the exact combination you choose.
node --version
npm --version
Keep the Pi on reliable power and network access. A browser capture requires the device to be running and able to reach the target site at its scheduled time.
2. Create a Playwright screenshot script
In a project directory, initialize npm and install Playwright. The install command below follows Playwright’s typical package workflow; check the official installation guide for current system dependencies and supported browser installation on your OS.
mkdir -p "$HOME/site-capture"
cd "$HOME/site-capture"
npm init -y
npm install playwright
npx playwright install chromium
Create capture.mjs with the following complete script. It takes the URL from an environment variable, writes a timestamped PNG under an absolute output directory, uses a fixed viewport, and exits with a failure status if navigation or capture fails.
import { chromium } from 'playwright';
import { mkdir } from 'node:fs/promises';
import path from 'node:path';
const target = process.env.TARGET_URL;
if (!target) {
console.error('Set TARGET_URL to the page to capture.');
process.exit(2);
}
const outputDir = process.env.OUTPUT_DIR || path.join(process.cwd(), 'screenshots');
const fullPage = process.env.FULL_PAGE === '1';
const timeoutMs = Number(process.env.NAVIGATION_TIMEOUT_MS || 60000);
let parsed;
try {
parsed = new URL(target);
if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error('Use an http or https URL.');
} catch (error) {
console.error(`Invalid TARGET_URL: ${error.message}`);
process.exit(2);
}
await mkdir(outputDir, { recursive: true });
const stamp = new Date().toISOString().replaceAll(':', '-');
const outputPath = path.join(outputDir, `capture-${stamp}.png`);
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
page.setDefaultNavigationTimeout(timeoutMs);
await page.goto(target, { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: outputPath, fullPage });
console.log(`Saved ${outputPath}`);
await context.close();
} finally {
await browser.close();
}
Run it manually before scheduling:
cd "$HOME/site-capture"
TARGET_URL='https://example.com' OUTPUT_DIR="$HOME/site-capture/screenshots" node capture.mjs
The example waits for the initial DOM to be parsed, then captures. That is a practical starting point, not a guarantee that every site’s dynamic content has finished rendering. Choose a site-specific wait condition when the page fills in content after navigation. Do not use a broad network-idle wait without considering long-lived requests and the site’s behavior.
3. Choose the screenshot output
Playwright’s screenshot API supports PNG, JPEG, and WebP. The image type can be inferred from the filename extension. By default, a screenshot covers the visible viewport; set fullPage: true to capture the full scrollable document. The full-page option can produce very tall files, and pages with lazy-loaded content may need extra handling if lower sections must be present.
| Choice | Use it when | Trade-off |
|---|---|---|
| Viewport | You need a consistent dashboard view or above-the-fold snapshot. | Content below the viewport is omitted. |
| Full page | You need a record of the whole scrollable page. | Large or very long pages can consume more time, memory, and storage. |
| PNG | You want lossless image output and broad support. | Files can be larger than lossy formats. |
| JPEG or WebP | Your downstream workflow accepts these formats and smaller files matter. | Encoding and quality trade-offs may affect image detail. |
Set the path extension to .jpg or .webp to select a different format. The API also supports clipping to a region, scale selection, animation handling, and a timeout. Consult the Page screenshot options for the exact option names and current behavior.
For comparisons over time, keep the viewport, device scale factor, browser version, OS, and capture settings stable. Playwright notes that rendering can vary with the host OS, browser version, settings, hardware, power source, and headless mode. A changed image does not necessarily mean the site alone changed.
4. Schedule recurring captures with cron
On a typical Raspberry Pi OS installation, cron can run the script at a chosen interval. First find the absolute paths for Node and the project:
command -v node
printf '%s\n' "$HOME/site-capture/capture.mjs"
Edit the current user’s crontab with crontab -e and add a line like this, replacing /home/pi and /usr/bin/node with the actual paths for the account that will run the job:
0 * * * * TARGET_URL='https://example.com' OUTPUT_DIR='/home/pi/site-capture/screenshots' /usr/bin/node /home/pi/site-capture/capture.mjs >> /home/pi/site-capture/capture.log 2>&1
This runs at the start of every hour according to the Pi’s local time. Examples of other common schedules:
| Schedule | Cron expression |
|---|---|
| Every 15 minutes | */15 * * * * |
| Every day at 06:30 | 30 6 * * * |
| Every Monday at 09:00 | 0 9 * * 1 |
Cron uses the machine’s local time. Check the Pi’s timezone if the capture must align with a specific local schedule. Use absolute paths because cron may run with a different working directory and a smaller environment than your interactive shell. Keep the job under the same user that owns the project and can write to the output directory.
Prevent overlap and manage old files
If a capture sometimes takes longer than its interval, two browser processes may overlap. On systems with flock, use a lock around the job:
0 * * * * /usr/bin/flock -n /tmp/site-capture.lock /usr/bin/env TARGET_URL='https://example.com' OUTPUT_DIR='/home/pi/site-capture/screenshots' /usr/bin/node /home/pi/site-capture/capture.mjs >> /home/pi/site-capture/capture.log 2>&1
Confirm that flock is installed and that the lock path is writable on your OS. If your environment does not provide it, use a scheduler or wrapper with an equivalent single-run lock. Decide how long to retain files and logs. For example, a separate cleanup job can delete captures older than your retention period; test its target directory carefully before enabling deletion.
5. Run it unattended and verify the result
- Run the command manually as the scheduled account and confirm that the image opens and has the expected dimensions.
- Run the script with the exact absolute paths, environment variables, and working assumptions used by cron.
- Wait for a scheduled invocation and inspect the log and output directory.
- Reboot the Pi and confirm a later scheduled capture still runs. This catches assumptions about a logged-in desktop session or an interactive shell.
- Record the OS, Node, Playwright, browser versions, viewport, and capture settings alongside the process if you need to explain visual changes later.
Consider a storage budget before capturing frequently. Each run creates another file, and full-page output can be much larger than a viewport capture. Choose a retention period, output format, and cadence that fit the available storage; no universal file size or capture speed can be assumed for arbitrary sites and Pi hardware.
6. Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| Browser executable missing | The Playwright package is installed but its browser was not installed, or it is incompatible with the OS or architecture. | Follow current Playwright installation instructions for the chosen Pi OS and confirm the browser launches locally. |
| Works in a terminal, fails in cron | Cron uses a different account, PATH, working directory, environment, or permissions. | Use absolute paths, set environment variables in the crontab line, check file ownership, and read the redirected log. |
| Navigation timeout | The network or site is slow, the URL is unreachable, or navigation never reaches the chosen state. | Check connectivity and the URL from the Pi. Increase the timeout only if the page legitimately needs longer; choose an appropriate wait condition. |
| Screenshot is blank or incomplete | The page may still be rendering, content may require interaction, or assets may fail to load. | Inspect the page at the same URL and add a wait for a meaningful selector or a bounded delay if appropriate. |
| Bottom of full-page capture is missing | Content may load lazily only after scrolling. | Use a page-specific scroll routine before capture, then verify the full-page output. Do not assume every site loads lazy content the same way. |
| Images differ between runs | Dynamic content, fonts, animations, browser updates, OS changes, hardware, or rendering settings changed. | Stabilize and record the environment; wait for page-specific content and consider the documented animation screenshot option. |
| No file appears | Output directory does not exist or is not writable, or the script exited before saving. | Use an absolute output path, create the directory, verify permissions, and inspect the job log and exit status. |
| Disk fills over time | Every successful run adds another image or log entry. | Lower the cadence, use an appropriate format, and add a tested retention and log rotation policy. |
| Multiple captures run at once | A run exceeded the schedule interval. | Add a lock such as flock, increase the interval, or reduce capture work. |
Sites may require authentication, block automation, or restrict automated access. Handle credentials securely and follow the site’s terms and access rules; do not attempt to bypass access controls.
7. Performance, reliability, and cost
- Performance: Browser startup, page complexity, network conditions, and full-page length all affect duration. The research does not establish a benchmark or model-specific capacity. Measure your own target pages and leave schedule headroom.
- Reliability: The Pi must have power, network access, storage, and a functioning browser installation when the job runs. Log failures, verify after reboot, and prevent overlapping jobs where needed.
- Consistency: Keep the capture environment stable if you compare images. Browser and OS updates can change rendering.
- Cost: The local workflow has no per-screenshot service charge, but uses the Pi, storage, power, and maintenance. The reviewed sources provide no energy or storage-cost figures, so estimate them for your own setup.
- Access: Respect site terms and authentication boundaries. A locally automated browser does not grant permission to capture restricted content.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A single request can return a PNG, JPEG, WebP, or PDF. For a recurring job, call the API from your existing scheduler instead of maintaining a browser on the Pi. See the ScreenshotNeo API documentation.
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,
)
r.raise_for_status()
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 and consent banners are accepted before capture, and 60+ known consent platforms, newsletter popups, and chat widgets are removed; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, with the result reported in response headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Can I use a Raspberry Pi without a monitor?
Yes. Install Raspberry Pi OS Lite and configure networking and remote access during imaging, then administer the Pi remotely.
Should I capture the viewport or the whole page?
Use viewport capture for a stable above-the-fold view and full-page capture when the complete scrollable document matters. Validate lazy-loaded content separately.
Why do screenshots change when I did not edit the page?
The browser, operating system, hardware, power source, headless mode, site content, or capture settings can affect rendering. Keep the environment consistent when image comparison matters.
Does this guide guarantee Playwright works on every Pi?
No. Confirm current Playwright browser support for the specific OS and hardware, install the required browser, and verify it launches before scheduling captures.


