Can Chrome Headless Take Screenshots of Websites on a Schedule?
Yes. Chrome Headless captures pages; cron or another scheduler runs the capture command or script on your chosen cadence.
Yes. Chrome Headless can capture a rendered webpage, and a separate scheduler such as cron can launch Chrome on a recurring schedule. Chrome performs the capture; the scheduler decides when it runs. For one URL and a fixed viewport, the command line is often enough. Use Puppeteer when you need browser interactions, page-specific readiness checks, or a multi-page workflow.
Chrome Headless mode is designed to run without a visible browser UI, including in unattended environments. The documented command-line screenshot option saves a PNG by default. Scheduling, file retention, logging, and alerting are responsibilities of the surrounding job setup.
1. Capture a page once with Chrome
First verify the Chrome executable name and that Headless mode launches in the environment that will run the job. The binary may be called chrome, google-chrome, or something else, depending on the operating system and installation.
chrome --headless --screenshot --window-size=1280,900 https://example.com/
By default, Chrome writes screenshot.png in the current working directory. The --window-size flag sets the viewport. To set a maximum wait before capture, add --timeout, in milliseconds:
chrome --headless --screenshot --window-size=1280,900 --timeout=10000 https://example.com/
A timeout is a bound on the wait, not confirmation that a dynamic page has reached the state you want. If the screenshot must include content loaded after a particular action or application event, use a script that waits for a meaningful condition.
2. Schedule recurring captures with cron
The following example runs at 08:00 UTC every day, creates a dated output file, and logs standard output and errors. Replace the executable, URL, and paths with values appropriate for the machine running the job.
#!/bin/sh
set -eu
CHROME_BIN="$(command -v google-chrome || command -v chromium || command -v chrome)"
OUT_DIR="/var/tmp/site-shots"
STAMP="$(date -u +%Y-%m-%dT%H-%M-%SZ)"
mkdir -p "$OUT_DIR"
"$CHROME_BIN" \
--headless \
--screenshot="$OUT_DIR/example-$STAMP.png" \
--window-size=1280,900 \
--timeout=15000 \
https://example.com/
Save this as /usr/local/bin/capture-example.sh and make it executable for the account that will run it. Add a cron entry with crontab -e:
0 8 * * * /usr/local/bin/capture-example.sh >> /var/log/site-shots.log 2>&1
That schedule uses the cron daemon’s timezone, which may differ from UTC. Check the machine’s cron and timezone configuration if the exact run time matters. Cron usually has a limited environment, so use absolute paths for scripts and output directories, and make sure the job’s account can launch Chrome and write the files.
Keep a useful history
Choose a naming and retention policy before enabling a recurring job. Reusing the default screenshot.png overwrites the previous capture when runs share a working directory. A timestamped name preserves each run, but storage grows over time. For example, a separate cleanup job can remove files older than 30 days:
find /var/tmp/site-shots -type f -name 'example-*.png' -mtime +30 -delete
Use a retention period that fits your audit or comparison needs. Store sensitive captures in a directory with appropriate access controls; screenshots can contain private page content.
3. Use Puppeteer when a page needs browser automation
The command line fits a basic URL-and-file capture. Puppeteer provides browser automation through JavaScript, which is useful when a capture depends on clicking through a workflow, waiting for a particular selector, or processing several pages with per-page logic.
Install Puppeteer in a project using the current installation instructions in its documentation. This script launches a browser, waits for a selected element, captures the page, and closes the browser even if capture fails:
import puppeteer from 'puppeteer';
const url = process.env.TARGET_URL ?? 'https://example.com/';
const output = process.env.OUTPUT_FILE ?? 'screenshot.png';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage({
viewport: { width: 1280, height: 900 },
});
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
await page.waitForSelector('main', { timeout: 15_000 });
await page.screenshot({ path: output, fullPage: true });
} finally {
await browser.close();
}
Run the script manually with TARGET_URL and OUTPUT_FILE set, then schedule the script the same way as the command-line capture. For example, a cron entry can invoke a Node script at a fixed time. Keep the command’s environment and file permissions explicit.
Choose a readiness condition that matches the page
domcontentloadedwaits for the initial document to be parsed; it does not mean client-rendered data or images are ready.waitForSelectorcan wait for a page element that signals the content you need has appeared.- A site-specific condition may be needed when content arrives after an API call, user action, or application state change.
- A fixed delay can be a simple fallback, but it may waste time on fast runs and still be too short on slow ones.
There is no universal wait that proves every website is visually complete. Define what “ready” means for the page you are capturing and handle the case where that condition never appears.
4. Pick the right capture method
| Need | Use | Trade-off |
|---|---|---|
| One URL and a fixed viewport | Chrome --headless --screenshot |
Small setup; limited workflow control. |
| A maximum wait before capture | CLI --timeout |
Bounds waiting; does not verify page-specific readiness. |
| Interactions or a specific page condition | Puppeteer | More control, with script and runtime maintenance. |
| Repeated runs | Cron, CI scheduler, or another job runner | The scheduler is separate from Chrome and must be configured and monitored. |
For a single fixed capture, begin with the command line. Add Puppeteer when the page state or workflow requires logic. For multiple pages, keep URLs and per-page settings in configuration rather than duplicating capture code.
5. Make scheduled jobs reliable
- Set paths explicitly. Scheduled environments may not use your interactive shell’s working directory or
PATH. - Use unique output names or deliberate overwrite behavior. Confirm that repeated runs won’t erase history you need.
- Check exit status and logs. Redirect errors to a log, and have the job runner alert you when a run fails if the captures matter operationally.
- Prevent overlapping runs. If one capture can take longer than the schedule interval, use a lock or job-runner concurrency limit so runs do not pile up.
- Test as the scheduled account. That account needs permission to launch the browser and write output files.
- Review privacy and access. A screenshot records whatever the browser can see, including account-specific or sensitive content.
Chrome’s documentation establishes the capture behavior, but the cron example in Chrome’s developer blog is illustrative rather than a universal production recipe. Monitoring, retries, retention, and alerting belong to your own job runner or scripts.
Or skip the browser setup
If you would rather make one API request than maintain Chrome and a scheduler, ScreenshotNeo is a website screenshot API and MCP server. Its API accepts a URL and returns an image or PDF. See the ScreenshotNeo API docs for request options.
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,
)
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())));
Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed, and response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month with no card.
Troubleshooting
| Symptom | Likely cause | What to check or change |
|---|---|---|
| No screenshot file appears | The job ran in a different working directory, Chrome failed to launch, or the account cannot write there. | Set an explicit output path, check the process exit status and log, and verify directory permissions as the scheduled user. |
| Every run replaces the same image | The command uses the default filename or a fixed output path. | Include a timestamp in the filename or deliberately configure a single latest-image file. |
| Screenshot is blank or incomplete | The page had not reached the needed state, or content depends on JavaScript or delayed requests. | Increase the CLI maximum wait or use Puppeteer to wait for a page-specific selector or state. A longer timeout alone does not guarantee readiness. |
| Command works in a terminal but fails in cron | Cron may have a different PATH, working directory, environment, or user permissions. |
Use absolute paths, set needed environment values, and run the script under the cron account. |
| Runs overlap or output is inconsistent | A slow capture is still running when the next scheduled run begins. | Increase the interval or add a lock/concurrency limit in the job runner. |
| Capture stops at the wrong time | The cron daemon’s timezone differs from the intended schedule. | Check the host timezone and cron behavior, or schedule in a job runner with an explicit timezone setting. |
Performance, reliability, and cost
Every recurring capture consumes browser runtime and storage. Full-page screenshots, slow pages, and multiple URLs can extend job duration. Measure the duration of your own pages, keep the schedule interval longer than typical run time, and set a timeout so a stuck navigation does not wait indefinitely. No general performance rate or completion guarantee applies to every site.
With self-hosted Chrome, you operate the browser installation, scheduler, logs, retries, and file retention. The software itself may be available without a per-capture service charge, but compute, storage, and maintenance still have costs. Budget capacity based on your URL count and cadence; for example, one URL captured daily produces about 30 files in a 30-day month.
ScreenshotNeo offers a hosted alternative with 1,000 free shots per month and paid plans from $5 for 3,000; higher plans are Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000). Yearly billing gives two months free, and every feature is available on every plan. Compare the cost and operational work against running your own browser and scheduler for your capture volume.
FAQ
Can Chrome take a screenshot every day?
Yes. Schedule the Chrome command or a capture script to run daily. Chrome does not provide the recurring schedule through the screenshot flag itself.
Does Headless Chrome need a desktop session?
No visible browser UI is required. Chrome documents Headless mode for unattended environments. The machine still needs a compatible Chrome installation and permission to run it.
Can I capture more than one website?
Yes. You can schedule multiple commands or use a Puppeteer script that iterates over URLs. Add per-page readiness conditions and unique output names where needed.
Will --timeout wait until all images and content are ready?
No. It sets a maximum wait before capture. Use a page-specific condition when the timing of important content matters.
Sources
- Chrome Headless command-line reference — screenshot output, viewport, and timeout options.
- Chrome Headless mode — unattended operation.
- Puppeteer documentation — browser automation and screenshot use cases.
- Chrome developer blog example — illustrative cron-triggered Puppeteer workflow.


