How to Schedule Automatic Website Screenshots with a Screenshot API
Schedule recurring website screenshots with a native API schedule or an external timer. Learn how to capture, store, retry, and monitor each run.
To schedule automatic website screenshots, either create a recurring schedule in a screenshot service that supports one, or use an external timer such as cron, CI, a cloud scheduler, or an automation platform to call a screenshot API. Set the target URL and capture options, choose a cadence, verify an initial capture, and decide how to store results and handle failures.
For a hosted screenshot API without a built-in recurring schedule, the scheduler owns the timing and calls the API on each run. ScreenshotNeo is one option for the capture step: it returns PNG, JPEG, WebP, or PDF from a single request, and can be called from cron or another scheduler. Its API and options are documented at ScreenshotNeo docs.
1. Choose where the schedule lives
There are three common designs. The right one depends on whether you want the screenshot provider or your existing infrastructure to own run timing and history.
| Approach | How it works | Good fit when | Check before choosing |
|---|---|---|---|
| Native recurring schedule | Create and manage a recurring capture in a screenshot service dashboard or API. | You want fewer components to operate, with schedule management near the captures. | Confirm schedule controls, run history, image retention, change detection, alerts, pause/delete behavior, quotas, and pricing. These vary by provider. |
| External scheduler | A cron job, CI schedule, cloud timer, or workflow calls a screenshot API at each run. | Your team already operates a scheduler or needs schedule logic in code. | You own retries, duplicate-run handling, result storage, and notifications unless another component provides them. |
| Cloud browser rendering plus a scheduler | A cloud browser endpoint renders a page; a separate timer triggers it on a recurring basis. | You need rendered browser output and want to compose it with cloud scheduling. | Check endpoint limits, authentication, output handling, and the scheduler configuration independently. |
Some providers document recurring captures, stored results, run history, or change alerts; those features are not universal. Cloudflare, for example, documents a Browser Run screenshot endpoint that processes a page’s HTML and JavaScript before capture. A separate scheduler can trigger it when recurring runs are needed. See the Cloudflare Browser Run documentation. For other providers, use their current documentation to verify the precise behavior before designing around it.
2. Estimate capture volume and choose a cadence
Pick a frequency that matches how quickly the page can change and how quickly you need to notice. Hourly, six-hour, daily, and weekly schedules are examples supported in some product documentation, not a shared guarantee across services. AWS EventBridge Scheduler, for example, supports recurring rate and cron expressions; verify its current limits and configuration in the AWS EventBridge Scheduler API reference.
Estimate the number of captures before setting a short interval:
captures per period = URL count × runs per URL × viewport variants
Include retries and any additional page widths or device variants in your estimate. Then compare that volume with your screenshot plan, scheduler limits, storage retention, and any charges for retries or additional renders. Billing differs by provider; check the current terms rather than assuming a universal per-image price.
- Use a longer interval for low-change reference pages and a shorter one for pages where timely changes matter.
- Start with one URL and one viewport, then add variants only when they answer a real monitoring question.
- Set a maximum runtime and a retry policy so a slow page does not create overlapping runs.
- Review the schedule after the monitored page or business need changes.
3. Configure the capture before automating it
Before wiring a recurring trigger, run a manual capture and confirm that the result is useful. Choose the exact URL, output format, viewport, and whether the capture should cover the full page. For pages that render asynchronously, determine whether the API needs a wait condition or a delay. Keep the same settings in the scheduled job so that results are comparable over time.
Screenshot APIs expose different controls. Check the chosen service’s documentation for supported options and defaults, including:
- Image format, viewport dimensions, device scale, and full-page capture.
- Wait conditions, such as a selector, a delay, or network idle, if supported.
- Authentication, cookies, headers, or user-agent settings for pages that are not public.
- Element-only capture, custom CSS, or hidden selectors if the screenshot should focus on a specific region.
- Output storage, cache behavior, result URLs, and retention.
Do not assume one provider’s parameter names or behavior apply to another. Keep secrets such as API keys in the scheduler’s secret store or environment, not in a checked-in script or a public job log.
4. DIY: call a screenshot API from a recurring job
This example uses cron to run a capture once per day. It calls the ScreenshotNeo API, writes the returned image to a dated path, and uses a temporary file so an interrupted response does not replace a previous successful image. Create a directory for the output first and store the key in an environment variable available to cron.
mkdir -p "$HOME/site-shots"
Save the following as capture-site.sh and make it executable with chmod 700 capture-site.sh. Replace the target URL as needed.
#!/usr/bin/env bash
set -euo pipefail
: "${SCREENSHOTNEO_API_KEY:?Set SCREENSHOTNEO_API_KEY before running}"
OUT_DIR="${OUT_DIR:-$HOME/site-shots}"
URL="https://stripe.com"
DATE="$(date -u +%Y-%m-%dT%H-%M-%SZ)"
mkdir -p "$OUT_DIR"
TMP="$OUT_DIR/.capture-$DATE.tmp"
DEST="$OUT_DIR/stripe-$DATE.webp"
trap 'rm -f "$TMP"' EXIT
curl --fail --silent --show-error --get \
"https://api.screenshotneo.com/v1/shot" \
--data-urlencode "access_key=$SCREENSHOTNEO_API_KEY" \
--data-urlencode "url=$URL" \
--output "$TMP"
mv "$TMP" "$DEST"
printf 'Saved %s\n' "$DEST"
Add a cron entry with crontab -e to run it daily at 08:00 UTC:
0 8 * * * SCREENSHOTNEO_API_KEY=YOUR_API_KEY /absolute/path/capture-site.sh >> /absolute/path/site-shots/capture.log 2>&1
Use an absolute path for the script and output directory. Cron runs with a limited environment, so a key exported in an interactive terminal may not be available to it. For production, use the scheduler’s secret mechanism where available rather than putting a long-lived key directly in a crontab.
Equivalent one-shot requests
These runnable examples capture one page. They are useful for a manual baseline or as the command inside a scheduled task. Replace YOUR_API_KEY and the target URL. See the ScreenshotNeo API documentation for the full option set.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python
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()
with open("shot.webp", "wb") as f:
f.write(r.content)
Node.js
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} ${res.statusText}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await (await import('node:fs/promises')).writeFile('shot.webp', bytes);
For Python, install the dependency with python -m pip install requests. The Node.js example uses the built-in fetch and filesystem modules. Add your scheduler’s retry, timeout, logging, and storage behavior around the one-shot request.
Keep scheduled jobs safe to retry
Schedulers may retry after a timeout or temporary failure, and a retry can overlap with a later run. Use a unique output name per scheduled occurrence, or make the job idempotent by recording a run identifier before storing results. Do not assume a timeout means the remote capture did not finish. Set a lock or concurrency limit if two simultaneous captures would overwrite the same path or exceed a quota.
For a small cron setup, a lock can prevent overlapping local runs:
flock -n /tmp/stripe-screenshot.lock /absolute/path/capture-site.sh
The lock only coordinates processes on the same host. A distributed scheduler needs its own concurrency control if jobs can run on multiple workers.
5. Store results and make failures visible
Decide what each successful run should leave behind: a local file, object storage, a database record, a provider-hosted image, or a visual-monitoring system. Store useful metadata alongside the image, such as target URL, UTC capture time, viewport, output format, job/run identifier, response status, and relevant billing or page-verdict headers when the API provides them.
Build operational handling around the API response:
- Check success. Treat a non-success HTTP response as a failed capture; do not save an error body with an image extension.
- Use bounded retries. Retry transient network errors and server-side failures with a delay and a maximum attempt count. Avoid retrying permanent configuration errors such as an invalid key unchanged.
- Prevent duplicate output. Give each scheduled run a stable identifier or unique timestamp, and define what to do if the scheduler delivers the same run twice.
- Notify on persistent failure. Send a job failure to the monitoring or alerting channel your team already uses.
- Review retention. Apply an explicit cleanup or archive policy. Provider retention and deletion behavior are provider-specific; check what happens to stored images when a schedule is paused or deleted.
When comparing schedule options, consider timing flexibility, dashboard and API controls, run history, archive retention, change detection, alerts, deploy-trigger support, quota, and the integration code the team must maintain. A native schedule can reduce components to operate. An external scheduler can fit an existing cron, CI, or cloud-timer workflow. Neither is best for every setup.
6. Troubleshooting recurring screenshot jobs
| Symptom | Likely cause | Fix |
|---|---|---|
| No scheduled run appears | The scheduler expression, timezone, enabled state, or deployment configuration is wrong. | Inspect the scheduler’s next-run time and timezone, confirm the job is enabled, and run the command manually with the same environment. |
| The job runs manually but fails under cron | Cron uses a limited environment or a different working directory and PATH. | Use absolute paths, set required environment variables explicitly or use a secrets facility, and log standard output and errors. |
| Authentication fails | The key is missing, malformed, expired, or unavailable to the scheduled process. | Check the secret configuration and API authentication instructions; do not print the key into logs. |
| The saved file is an error response | The script wrote a response body without checking HTTP status. | Use a success-status check such as cURL’s --fail or Python’s raise_for_status(); write to a temporary file and rename only after success. |
| The page is blank or incomplete | The page may need more render time, a selector wait, authentication, or a different viewport. A page can also fail to load. | Reproduce with one manual capture, then adjust supported wait and page settings. Check the API response’s page verdict where available. |
| Runs overlap or replace each other’s files | A capture takes longer than the schedule interval, or multiple deliveries share an output filename. | Set a concurrency limit or lock and include a unique run ID or timestamp in the output path. |
| Retries create unexpectedly high volume | Retries are unbounded, overlap, or are counted as additional renders by the provider. | Cap attempts, use backoff, log each attempt, and verify the provider’s current billing and quota rules. |
| Old captures disappear | The storage location has a lifecycle policy or the provider’s retention behavior differs from expectations. | Check storage lifecycle settings and provider documentation; export images you need to retain. |
| Images differ between runs | Dynamic content, rotating banners, personalization, viewport changes, or timing differences affect rendering. | Keep viewport and wait settings fixed; where supported, hide irrelevant selectors or use controlled cookies and headers. Record the settings with each capture. |
7. Performance, reliability, and cost
Capture time depends on page loading and rendering, API limits, and the selected wait conditions. A page that waits for network idle may take longer when it keeps background requests open. For a large URL list, use bounded concurrency rather than launching every capture at once, and respect the API and scheduler’s documented limits.
For reliability, separate scheduling from capture handling: log each run, apply finite retries for transient failures, store successful outputs atomically, and alert after the retry budget is exhausted. Keep a manual capture path for debugging. If the screenshot service offers a native schedule, compare its run history and failure behavior with what your external scheduler already provides.
Cost is driven by the chosen provider’s billing model and your workload. Calculate URL count × runs per URL × viewport variants, then account for retries and extra captures. Verify current quotas, overage behavior, cache treatment, and storage costs with each service. Do not assume a cache hit, failed page, or retry is billed the same way by every API.
Or skip the browser setup
Use ScreenshotNeo’s one-call API for the capture step and run that request from your scheduler. ScreenshotNeo accepts the URL and returns an image or PDF; the API parameters used by other screenshot APIs also work, which can make switching easier. See the API documentation for capture options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
- Cookie and consent banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses say which page verdict and billing status applied.
- An MCP server gives AI agents, including Claude, Cursor, and other MCP clients, the tools
take_screenshot,get_page_info, andcapture_pdf. - 1,000 screenshots a month are free with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
Frequently asked questions
Can I schedule a screenshot without a server I manage?
Yes. Use a hosted schedule feature from a screenshot provider, or a managed cloud timer or workflow that calls the screenshot API. Confirm that the chosen service supports the cadence and output handling you need.
Should I use cron or a provider’s built-in schedule?
Use the option that best fits the systems your team already operates. A provider schedule may keep management and run history together; cron or CI can keep timing in an existing workflow. Compare limits and failure handling before deciding.
Can I schedule multiple URLs?
Yes. Iterate over the URL list or use a bulk endpoint if your chosen API provides one. Estimate the resulting volume first and limit concurrency to the provider’s documented capacity.
How often should a website be captured?
Set the cadence according to the page’s change rate and how quickly you need to detect a change. Start less frequently, review the results and volume, then adjust.
Do scheduled screenshots automatically tell me when a page changes?
No. Recurring images create a history, but change detection and alerts are separate features. Confirm that the selected service provides them, or compare stored captures in your own workflow.


