ScreenshotNeo

BlogHow-to

How to Schedule Recurring Website Screenshots with ApiFlash

ApiFlash handles each screenshot request; use cron or a scheduler to run it on a cadence, save the result, and handle caching, retries, and quota limits.

By the ScreenshotNeo team4 October 202610 min read

ApiFlash documents an API for taking screenshots, but its reviewed documentation does not describe a built-in recurring schedule. To capture a site repeatedly, schedule a script or workflow outside ApiFlash. Each run sends an authenticated request to https://api.apiflash.com/v1/urltoimage, then stores or routes the returned image.

This guide uses a Unix cron job and a Python script. It also includes equivalent cURL and Node.js requests, capture settings, caching behavior, delivery options, failure handling, and ways to choose a scheduler.

1. Choose a scheduler and capture cadence

A scheduler starts your capture command at a specified time. The right choice depends on where you want the job to run and how much control you need over time zones, missed runs, alerts, and retries.

Scheduler Good fit Considerations
Unix cron A script on a Linux server or VM Check the server time zone, keep the process supervised, and arrange log rotation and alerts.
Hosted cloud scheduler A job that should run without maintaining a server Configure its time zone, retry policy, timeout, secret storage, and destination storage.
Automation workflow A recurring workflow that also routes results to other services Verify that the selected HTTP or API action can make the ApiFlash request. A generic scheduler listing does not prove a native ApiFlash integration.

Decide whether you need a screenshot every hour, day, or week, and whether a missed run should be retried or skipped. For cron, the schedule is interpreted in the machine’s local time unless configured otherwise. Prefer UTC or explicitly document the server time zone to avoid daylight-saving surprises.

2. Create a protected ApiFlash key

  1. Create an ApiFlash access key in its dashboard.
  2. Store the key in a secret manager or environment variable on the machine that runs the job.
  3. Do not put the key in a public webpage, source repository, command history, or logs.

Set the key as APIFLASH_ACCESS_KEY. On a Unix shell, you can export it for the current session with export APIFLASH_ACCESS_KEY='your-key'. For recurring production jobs, configure it through the host’s protected environment or secret settings.

3. Pick the screenshot settings

Every scheduled run should request the same intended page state. ApiFlash accepts GET query parameters or POST form data. The required inputs are an access key and a fully qualified URL, including https:// or http://.

Setting Use it for Important behavior
width, height Viewport dimensions Choose a consistent viewport so changes in output represent page changes rather than configuration changes.
format Output format JPEG is the documented default; PNG and WebP are also available.
Full-page or element capture Capturing the whole document or a selected element Use full-page capture for long pages; use the documented selector option when only one component matters.
wait_until Choosing when navigation is considered ready The default is network_idle; alternatives include dom_loaded and page_loaded.
wait_for Waiting for a CSS selector Useful when a specific element signals that the page is ready.
delay Adding a fixed wait Documented maximum is 10 seconds. Use it only when a known page behavior needs extra time.
fresh, ttl Controlling cached results Use fresh=true when each run must capture a new page state. Set a TTL when reuse is acceptable.
response_type Choosing how the result is returned By default the response is image bytes. response_type=json returns a screenshot URL; extraction options can also return HTML or text links.
S3 output parameters Saving directly to an S3 bucket Use the documented parameters and keep bucket credentials private.

ApiFlash documents a default cache TTL of 86,400 seconds and a configurable range from 0 to 2,592,000 seconds (30 days). Identical requests can return a cached image and do not count against the monthly quota. For a recurring visual monitor, explicitly request freshness if an unchanged request would otherwise reuse an earlier capture.

Page state matters. Choose a readiness condition that matches the site: a page with long-lived network connections may not become idle, while a simple DOM-ready event may occur before images or client-rendered content appear. ApiFlash’s FAQ also notes that fonts can differ unless the site serves its own fonts; custom headers can interfere with remote font loading.

4. Build the capture script

This Python example requests the image bytes, checks for an HTTP error, and writes the result to a timestamped file. It uses fresh=true so recurring runs do not intentionally reuse a cached screenshot.

import os
from datetime import datetime, timezone
from pathlib import Path

import requests

api_key = os.environ["APIFLASH_ACCESS_KEY"]
params = {
    "access_key": api_key,
    "url": "https://example.com",
    "width": 1440,
    "height": 900,
    "format": "png",
    "fresh": "true",
    "wait_until": "network_idle",
}

response = requests.get(
    "https://api.apiflash.com/v1/urltoimage",
    params=params,
    timeout=120,
)
response.raise_for_status()

output_dir = Path("screenshots")
output_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
output_path = output_dir / f"example-{timestamp}.png"
output_path.write_bytes(response.content)
print(f"Saved {output_path}")

Install the dependency with python -m pip install requests. Replace the example URL and adjust viewport and format for your monitoring needs. Protect the output directory and decide how long to retain old captures.

Equivalent cURL request

This GET request writes the returned image to a file. --data-urlencode safely encodes the target URL as a query parameter.

curl --fail --show-error --silent --get \
  "https://api.apiflash.com/v1/urltoimage" \
  --data-urlencode "access_key=${APIFLASH_ACCESS_KEY}" \
  --data-urlencode "url=https://example.com" \
  --data-urlencode "width=1440" \
  --data-urlencode "height=900" \
  --data-urlencode "format=png" \
  --data-urlencode "fresh=true" \
  --output "example.png"

Equivalent Node.js request

This example uses built-in fetch and writes the response bytes to a file.

import { writeFile } from 'node:fs/promises';

const key = process.env.APIFLASH_ACCESS_KEY;
if (!key) throw new Error('Set APIFLASH_ACCESS_KEY');

const query = new URLSearchParams({
  access_key: key,
  url: 'https://example.com',
  width: '1440',
  height: '900',
  format: 'png',
  fresh: 'true',
  wait_until: 'network_idle',
});

const response = await fetch(
  `https://api.apiflash.com/v1/urltoimage?${query}`,
  { signal: AbortSignal.timeout(120_000) },
);
if (!response.ok) {
  throw new Error(`ApiFlash returned HTTP ${response.status}: ${await response.text()}`);
}
await writeFile('example.png', Buffer.from(await response.arrayBuffer()));

5. Schedule the script with cron

Save the Python script as /opt/site-screenshots/capture.py, ensure the job’s Python environment has requests, and make the output location writable by the job user. Then edit that user’s crontab with crontab -e.

# Run every day at 06:30 UTC (set the host or cron environment to UTC).
30 6 * * * APIFLASH_ACCESS_KEY='your-key' /usr/bin/python3 /opt/site-screenshots/capture.py >> /var/log/site-screenshots.log 2>&1

For long-lived deployments, inject the key through a protected environment file or service configuration rather than writing it into a crontab. Cron provides a minimal environment, so use absolute paths and do not assume your interactive shell configuration is loaded.

Before relying on recurrence, run the script once manually and confirm that the saved image has the expected viewport, page state, and freshness. This is a deployment checklist, not a claim that the example has been run against your site.

6. Choose how to store or route each result

  • Save returned bytes: use the image response as in the code above. Choose a timestamped name or a stable name depending on whether you want a history or only the latest image.
  • Use JSON response mode: request response_type=json when a screenshot URL is more convenient than receiving the bytes directly. Download or route that URL according to your retention needs.
  • Use extraction outputs: documented extraction parameters can return links to HTML or text as well as screenshot information.
  • Upload to S3: ApiFlash documents S3 output parameters. Keep bucket credentials in a secret manager, restrict permissions to the required bucket and operation, and check the upload result.

For every destination, decide how to handle overwrites, retention, access controls, and failed writes. Avoid logging image data, access keys, or signed storage URLs.

7. Add retries, logs, and quota checks

Scheduled jobs need observable outcomes. Log the run time, target identifier, HTTP status, elapsed time, output destination, and relevant quota headers. Avoid logging the secret key or sensitive query values.

  • Retry transient network failures and HTTP 429 responses with exponential backoff and jitter.
  • Do not retry authentication, plan, or quota errors indefinitely. Alert and fix the underlying key, plan, or usage issue.
  • Use a finite timeout and a maximum attempt count so a job cannot run forever.
  • Prevent overlapping runs if one capture can take longer than the schedule interval; use a lock or scheduler concurrency setting.
  • Check successful-response quota headers and use the documented quota endpoint to monitor remaining monthly capacity.

ApiFlash documents request processing at 20 requests per second with a burst size of 400. Excessive bursts can receive HTTP 429. Stagger large capture batches and keep retries from creating a synchronized retry storm.

Common errors and fixes

Symptom Likely cause Fix
HTTP 401 Invalid or revoked access key Check the secret configured in the scheduler and rotate it if needed. Do not expose it in logs.
HTTP 402 Monthly quota exceeded Review usage and the account plan, then reduce capture frequency or adjust capacity.
HTTP 403 The request uses a feature unsupported by the current plan Check the plan requirements for the selected feature or remove that option.
HTTP 429 Request rate or burst limit reached Reduce concurrency, stagger jobs, and retry with backoff.
Invalid URL or unexpected target Missing URL scheme or incorrectly encoded query value Use a fully qualified URL and a URL encoder such as URLSearchParams, Requests parameters, or cURL’s --data-urlencode.
Image shows a loading state Capture ran before the needed content appeared Choose an appropriate wait_until, wait for a selector with wait_for, or add a small documented delay (up to 10 seconds).
Old screenshot repeats An identical request returned a cached result Set fresh=true when a new capture is required, or change the TTL if cache reuse is intentional.
Fonts look different The page’s fonts may not be available to the capture, or custom headers may disrupt remote font loading Serve fonts from the site when possible and review custom headers; consult ApiFlash’s FAQ for font behavior.
Cron works manually but not on schedule Different environment, working directory, permissions, or time zone Use absolute paths, configure secrets for the scheduled process, check file permissions and cron logs, and confirm the machine’s time zone.
Output file is empty or not an image An error response was saved as if it were a successful image Check the HTTP status before writing the body; use cURL’s --fail or code that raises on non-success responses.

Performance, reliability, and cost

Performance

Capture latency depends on the target page and selected readiness condition. A strict network-idle wait can take longer on pages with ongoing requests; a fixed delay adds time to every run. Capture only the viewport or element you need when a full-page result is unnecessary, and avoid scheduling more simultaneous requests than the workload requires.

Reliability

A screenshot job depends on the scheduler, network, target site, ApiFlash, and the output destination. Give each run a timeout, retry transient failures with backoff, and alert on repeated failures. Keep a record of the last successful capture so a missed schedule is visible. A scheduled trigger alone does not guarantee delivery or that a page rendered correctly.

Cost and cache behavior

ApiFlash’s published product page lists a free plan with 100 screenshots monthly, Lite at $7/month for 1,000, Medium at $35/month for 10,000, and Large at $180/month for 100,000. These are the page figures reviewed for this guide; check ApiFlash’s current pricing before budgeting because plans can change.

Estimate usage as targets × runs per target, then account for retries and whether requests are fresh or served from cache. ApiFlash documents that cached identical requests do not count against the monthly quota. Its documented default TTL is one day, configurable from 0 to 30 days. A freshness requirement can increase counted captures, so choose cache behavior deliberately.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF, so a scheduled task can call it directly without managing browser automation.

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

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, with no card.

FAQ

Can ApiFlash run a recurring schedule by itself?

The reviewed ApiFlash documentation describes a capture endpoint, not a built-in recurring scheduler. Run the endpoint from cron, a hosted scheduler, or a verified automation workflow.

Can I schedule captures for several URLs?

Yes. Keep the target URLs in a protected configuration file or scheduler input, then make one request per URL. Control concurrency to stay within rate limits and make failures traceable to a specific target.

Should each run use a fresh screenshot?

Use fresh=true when the purpose is to observe the page at that run’s time. Allow cache reuse when repeated identical output is acceptable and quota conservation matters.

Does Zapier have a verified ApiFlash action?

The surfaced Zapier listing documents recurring Schedule triggers and a generic Screenshot API action, but does not establish that the action is a native ApiFlash integration. Verify the current HTTP/API action and configure the ApiFlash endpoint explicitly.

Sources