Scheduled screenshots
Automatically capture screenshots on a recurring schedule
Scheduled screenshots
Capture a URL on a recurring schedule without running your own cron, browser, or storage. A schedule captures the page, keeps the result for as long as you specify, and can email you or POST to a webhook — optionally only when the page actually changed.
Scheduled captures are available on every plan, including Free — they're metered by your monthly screenshot quota. Webhook delivery is free on every plan too. Email delivery is a paid feature (Starter and above).
Create schedule
POST /v1/schedulesRequest body
{
"name": "Homepage Monitor",
"url": "https://example.com",
"schedule": "0 0 9 * * *",
"timezone": "America/New_York",
"options": {
"viewport": { "width": 1920, "height": 1080 },
"format": "png",
"fullPage": false,
"blockAds": true
},
"destinations": [
{ "type": "email", "to": ["[email protected]"], "subject": "Homepage snapshot" }
],
"onlyOnChange": true,
"diffThreshold": 0.01,
"alertOnFailure": true,
"autoPauseAfterFailures": 5,
"retentionDays": 30,
"startsAt": "2025-02-01T00:00:00Z",
"endsAt": null
}Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Schedule name (max 255 chars) |
url | string | Yes | URL to capture |
schedule | string | Yes | Cron expression or interval alias |
timezone | string | No | Timezone for cron (default: UTC) |
options | object | No | Screenshot options |
destinations | array | No | Where to deliver each capture — email or webhook (max 10) |
onlyOnChange | boolean | No | Skip delivery when the capture matches the previous run (default: false) |
diffThreshold | number | No | Fraction of changed pixels (0–1) that counts as a change (default: 0.01) |
alertOnFailure | boolean | No | Send a failure notice to your destinations when a run fails (default: false) |
autoPauseAfterFailures | integer | No | Pause the schedule after this many consecutive failures (1–100) |
retentionDays | integer | No | Days to keep captures (1–365, default: 30) |
startsAt | string | No | ISO 8601 — don't run before this time |
endsAt | string | No | ISO 8601 — don't run after this time |
webhookUrl | string | No | Deprecated. Use a webhook destination instead |
webhookSecret | string | No | Deprecated. Use a webhook destination's secret instead |
Schedule formats
Cron expressions
Standard 6-field cron format:
┌───────────── second (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌───────────── hour (0-23)
│ │ │ ┌───────────── day of month (1-31)
│ │ │ │ ┌───────────── month (1-12)
│ │ │ │ │ ┌───────────── day of week (0-7, Sunday=0 or 7)
│ │ │ │ │ │
* * * * * *Examples:
| Expression | Description |
|---|---|
0 0 9 * * * | Every day at 9:00 AM |
0 0 */4 * * * | Every 4 hours |
0 0 9 * * 1-5 | Weekdays at 9:00 AM |
0 0 0 1 * * | First day of each month at midnight |
0 0 9,12,18 * * * | At 9 AM, 12 PM, and 6 PM daily |
Interval format
Simple intervals for common schedules:
| Format | Description |
|---|---|
hourly | Every hour at minute 0 |
daily | Every day at midnight |
weekly | Every Sunday at midnight |
monthly | First day of month at midnight |
Screenshot options
The options object takes the same fields as the screenshot API, minus url (which comes from the schedule):
{
"options": {
"viewport": { "width": 1920, "height": 1080 },
"device": null,
"format": "png",
"fullPage": false,
"quality": 80,
"delay": 0,
"waitFor": null,
"waitUntil": "domcontentloaded",
"timeout": 30000,
"darkMode": false,
"customCss": null,
"hideSelectors": [],
"selector": null,
"blockAds": true,
"blockCookieBanners": true,
"blockLevel": "none",
"stealthMode": false,
"outputs": null
}
}Use outputs to capture more than an image per run — a PDF, HTML, Markdown, or extracted JSON alongside the screenshot. See multiple outputs. Content outputs (HTML/Markdown/JSON) require the content extraction capability, included on Pro and above.
Delivery destinations
By default a capture is stored and reachable from the schedule history. Add destinations to also push each run to email or a webhook. A schedule can have up to 10 destinations.
Each destination can set onlyOnChange to override the schedule-level setting — for example, archive every run to a webhook but email a person only when the page changes.
{
"type": "email",
"to": ["[email protected]", "[email protected]"],
"subject": "Pricing page snapshot",
"onlyOnChange": true
}| Field | Type | Required | Description |
|---|---|---|---|
to | string[] | Yes | Recipient addresses |
subject | string | No | Custom subject line |
onlyOnChange | boolean | No | Override the schedule's change setting for this destination |
Webhook
{
"type": "webhook",
"url": "https://your-server.com/webhooks/scheduled",
"secret": "whsec_...",
"onlyOnChange": false
}| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Endpoint that receives the POST |
secret | string | No | Signs the payload with HMAC-SHA256 (write-only; never returned) |
onlyOnChange | boolean | No | Override the schedule's change setting for this destination |
Webhook delivery is available on every plan, including Free. Email delivery requires a paid plan (Starter and above).
Webhook destination payload
Each run posts JSON to your endpoint with the X-Webhook-Event: schedule.delivery header. When you set a secret, an X-Webhook-Signature HMAC-SHA256 header is included (same scheme as webhooks).
Single-output capture:
{
"event": "schedule.delivery",
"scheduleName": "Daily homepage",
"url": "https://example.com",
"executionId": "exec001abc",
"resultUrl": "https://api.allscreenshots.com/v1/screenshots/jobs/abc123xyz0/result",
"storageUrl": "https://storage.allscreenshots.com/abc123xyz0.png",
"changed": true
}Multi-output capture — same shape plus an outputs map keyed by output id:
{
"event": "schedule.delivery",
"scheduleName": "Daily homepage",
"url": "https://example.com",
"executionId": "exec001abc",
"resultUrl": "https://api.allscreenshots.com/v1/screenshots/jobs/abc123xyz0/result/screenshot",
"storageUrl": "https://storage.allscreenshots.com/abc123xyz0/screenshot.png",
"changed": true,
"outputs": {
"screenshot": {
"type": "screenshot",
"contentType": "image/png",
"size": 245678,
"resultUrl": "https://api.allscreenshots.com/v1/screenshots/jobs/abc123xyz0/result/screenshot",
"storageUrl": "https://storage.allscreenshots.com/abc123xyz0/screenshot.png"
},
"markdown": {
"type": "markdown",
"contentType": "text/markdown",
"size": 1200,
"resultUrl": "https://api.allscreenshots.com/v1/screenshots/jobs/abc123xyz0/result/markdown",
"storageUrl": "https://storage.allscreenshots.com/abc123xyz0/markdown.md"
}
}
}Top-level resultUrl/storageUrl point at the first output (the screenshot for typical captures). Read outputs to iterate every artifact.
If the run fails (or auto-pause kicks in) and alertOnFailure is set, the same endpoint receives a schedule.failed event instead:
{
"event": "schedule.failed",
"scheduleName": "Daily homepage",
"url": "https://example.com",
"error": "Page load timed out",
"autoPaused": false
}Change detection
Set onlyOnChange: true and a schedule compares each capture against the previous successful run. If the page hasn't changed, the capture is still recorded in history but delivery is skipped — so you only get emailed when something is actually different.
diffThreshold is the fraction of pixels that must differ to count as a change, from 0 to 1 (default 0.01, i.e. 1%). The first run has no baseline, so it always counts as a change and delivers.
| diffThreshold | Behavior |
|---|---|
0.1 | Only large changes (~10% of pixels) trigger delivery |
0.03 | Moderate changes |
0.005 | Small changes — catches minor edits |
The dashboard exposes these three values as Low / Medium / High sensitivity.
Response
{
"id": "schedabc123",
"name": "Homepage Monitor",
"url": "https://example.com",
"schedule": "0 0 9 * * *",
"scheduleDescription": "at 9:00 AM America/New_York",
"timezone": "America/New_York",
"status": "active",
"options": {
"viewport": { "width": 1920, "height": 1080 },
"format": "png",
"fullPage": false,
"blockAds": true
},
"destinations": [
{ "type": "email", "id": "dst_a1b2c3", "to": ["[email protected]"], "subject": "Homepage snapshot" }
],
"onlyOnChange": true,
"diffThreshold": 0.01,
"alertOnFailure": true,
"autoPauseAfterFailures": 5,
"consecutiveFailures": 0,
"retentionDays": 30,
"startsAt": "2025-02-01T00:00:00Z",
"endsAt": null,
"lastExecutedAt": null,
"nextExecutionAt": "2025-01-16T14:00:00Z",
"executionCount": 0,
"successCount": 0,
"failureCount": 0,
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-15T10:30:00Z"
}status is one of active, paused, completed, or failed. Webhook secrets are never returned; destinations come back with a generated id and masked secrets.
List schedules
GET /v1/schedulesResponse
{
"schedules": [
{
"id": "schedabc123",
"name": "Homepage Monitor",
"url": "https://example.com",
"schedule": "0 0 9 * * *",
"scheduleDescription": "at 9:00 AM America/New_York",
"timezone": "America/New_York",
"status": "active",
"retentionDays": 30,
"lastExecutedAt": "2025-01-15T14:00:00Z",
"nextExecutionAt": "2025-01-16T14:00:00Z",
"executionCount": 1,
"successCount": 1,
"failureCount": 0,
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-15T10:30:00Z"
}
],
"total": 1
}Get schedule
GET /v1/schedules/{scheduleId}Response
Full schedule details including recent captures.
Update schedule
PUT /v1/schedules/{scheduleId}Request body
Only include fields you want to update. Accepts the same fields as create, including destinations, onlyOnChange, diffThreshold, and the failure-handling options. Passing destinations replaces the existing list.
{
"name": "Updated Name",
"schedule": "0 0 */2 * * *",
"onlyOnChange": true,
"diffThreshold": 0.02,
"options": {
"fullPage": true
}
}Pause/resume schedule
POST /v1/schedules/{scheduleId}/pause
POST /v1/schedules/{scheduleId}/resumePausing stops the schedule from running until you resume it. Resuming also resets the consecutive-failure counter, so a schedule that auto-paused after repeated failures starts clean.
Run now
POST /v1/schedules/{scheduleId}/triggerRuns the capture immediately, regardless of the next scheduled time. Useful for testing a new schedule or grabbing an on-demand snapshot. The run shows up in the schedule history like any other.
Delete schedule
DELETE /v1/schedules/{scheduleId}Deleting a schedule also deletes all associated captures. This cannot be undone.
Get schedule history
GET /v1/schedules/{scheduleId}/historyQuery parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Maximum executions to return |
Response
limit defaults to 50 and is capped at 100. Execution status is success or failed. When change detection is on, diffScore is the measured fraction of changed pixels and changed says whether it crossed diffThreshold. deliveries records the outcome per destination.
{
"scheduleId": "schedabc123",
"totalExecutions": 30,
"executions": [
{
"id": "exec001abc",
"executedAt": "2025-01-15T14:00:00Z",
"runId": "run_7f3a9c",
"status": "success",
"resultUrl": "https://api.allscreenshots.com/v1/screenshots/jobs/job001abc/result",
"storageUrl": "https://storage.allscreenshots.com/job001abc.png",
"fileSize": 245678,
"renderTimeMs": 1234,
"diffScore": 0.042,
"changed": true,
"deliveries": [
{ "type": "email", "id": "dst_a1b2c3", "status": "sent" }
],
"expiresAt": "2025-04-15T14:00:00Z"
},
{
"id": "exec002abc",
"executedAt": "2025-01-14T14:00:00Z",
"runId": "run_2b8e1d",
"status": "failed",
"errorCode": "TIMEOUT",
"errorMessage": "Navigation timed out after 30000ms",
"expiresAt": "2025-04-14T14:00:00Z"
}
]
}Examples
Daily homepage monitoring
curl -X POST 'https://api.allscreenshots.com/v1/schedules' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"name": "Daily Homepage",
"url": "https://example.com",
"schedule": "0 0 9 * * *",
"timezone": "America/New_York",
"options": {
"fullPage": true,
"blockAds": true,
"blockCookieBanners": true
},
"destinations": [
{ "type": "webhook", "url": "https://your-server.com/webhooks/daily-capture", "secret": "whsec_..." }
],
"retentionDays": 90
}'Hourly competitor tracking, emailed on change
curl -X POST 'https://api.allscreenshots.com/v1/schedules' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"name": "Competitor Pricing",
"url": "https://competitor.com/pricing",
"schedule": "hourly",
"options": {
"waitFor": ".pricing-table",
"waitUntil": "networkidle"
},
"onlyOnChange": true,
"diffThreshold": 0.01,
"destinations": [
{ "type": "email", "to": ["[email protected]"], "subject": "Competitor pricing changed" }
],
"retentionDays": 7
}'Business hours only
curl -X POST 'https://api.allscreenshots.com/v1/schedules' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"name": "Dashboard Status",
"url": "https://internal-dashboard.example.com",
"schedule": "0 0 9-17 * * 1-5",
"timezone": "Europe/London",
"options": {
"viewport": { "width": 1920, "height": 1080 }
}
}'Schedule limits
Scheduled captures are available on every plan. Each plan caps how many schedules an API key can have. The cap counts every schedule on the key, including paused ones — delete schedules you no longer need to free up room.
| Plan | Schedules |
|---|---|
| Free | 50 |
| Starter | 50 |
| Pro | 50 |
| Business | 100 |
| Scale | 500 |
| Enterprise | 1000 |
Every run counts against your monthly screenshot quota. A daily schedule uses ~30 screenshots per month; an hourly one uses ~720. Each extra outputs entry on a run counts too.