Getting Started
The Snapshot Archive API lets you programmatically capture website screenshots, manage monitors, retrieve snapshots, and detect visual changes. All API access requires authentication via an API key.
https://snapshotarchive.com/api/v1
Quick Example
Trigger a snapshot capture for one of your monitors:
curl -X POST https://snapshotarchive.com/api/v1/monitors/1/trigger \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
{
"data": {
"message": "Snapshot queued successfully.",
"snapshot_id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
}
}
Response Format
All responses are JSON. Successful responses wrap data in a data key. Collections include a meta key with pagination info.
{
"data": {
"id": 1,
"name": "My Project",
"created_at": "2025-01-15T10:30:00.000000Z"
}
}
{
"data": [ ... ],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 42,
"last_page": 3
}
}
{
"error": {
"code": "invalid_api_key",
"message": "The provided API key is invalid or has been revoked.",
"status": 401
}
}
Authentication
All API requests must include your API key in the Authorization header using the Bearer scheme.
Authorization: Bearer YOUR_API_KEY
Create and manage API keys from your Dashboard → API Keys.
Example Requests
curl https://snapshotarchive.com/api/v1/account \
-H "Authorization: Bearer sk_live_abc123..." \
-H "Accept: application/json"
$response = Http::withToken('sk_live_abc123...')
->acceptJson()
->get('https://snapshotarchive.com/api/v1/account');
$account = $response->json('data');
const response = await fetch('https://snapshotarchive.com/api/v1/account', {
headers: {
'Authorization': 'Bearer sk_live_abc123...',
'Accept': 'application/json'
}
});
const { data } = await response.json();
import requests
response = requests.get('https://snapshotarchive.com/api/v1/account', headers={
'Authorization': 'Bearer sk_live_abc123...',
'Accept': 'application/json'
})
data = response.json()['data']
Projects
Projects are containers for organizing your monitors. Each project can hold multiple monitors and is automatically created from the domain name when adding a website through the dashboard.
/api/v1/projects
Returns a paginated list of all your projects with monitor counts.
Query Parameters
| Name | Type | Default | Description |
|---|---|---|---|
per_page | integer | 20 | Items per page (1-100) |
page | integer | 1 | Page number |
Response
{
"data": [
{
"id": 1,
"user_id": 1,
"name": "example.com",
"description": null,
"monitors_count": 3,
"created_at": "2025-01-15T10:30:00.000000Z",
"updated_at": "2025-01-15T10:30:00.000000Z"
}
],
"meta": { "current_page": 1, "per_page": 20, "total": 1, "last_page": 1 }
}
/api/v1/projects
Create a new project.
Body Parameters
| Name | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Project name (max 255 characters) |
description | string | No | Project description (max 1000 characters) |
curl -X POST https://snapshotarchive.com/api/v1/projects \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"name": "Marketing Sites", "description": "All marketing pages"}'
{
"data": {
"id": 2,
"user_id": 1,
"name": "Marketing Sites",
"description": "All marketing pages",
"created_at": "2025-03-15T14:00:00.000000Z",
"updated_at": "2025-03-15T14:00:00.000000Z"
}
}
/api/v1/projects/{id}
Retrieve a single project by ID, including monitor count.
{
"data": {
"id": 1,
"user_id": 1,
"name": "example.com",
"description": null,
"monitors_count": 3,
"created_at": "2025-01-15T10:30:00.000000Z",
"updated_at": "2025-01-15T10:30:00.000000Z"
}
}
/api/v1/projects/{id}
Update a project's name or description.
Body Parameters
| Name | Type | Required | Description |
|---|---|---|---|
name | string | No | New project name (max 255 characters) |
description | string | No | New description (max 1000 characters) |
curl -X PUT https://snapshotarchive.com/api/v1/projects/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"name": "Updated Name", "description": "New description"}'
/api/v1/projects/{id}
Delete a project. This will also delete all monitors and their snapshots within the project.
curl -X DELETE https://snapshotarchive.com/api/v1/projects/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
(empty response body)
Monitors
Monitors track specific URLs and capture screenshots on a schedule. Each monitor belongs to a project and stores configuration for viewport, capture settings, and alert preferences.
/api/v1/monitors
Returns a paginated list of all your monitors across all projects. Includes the associated project data.
Query Parameters
| Name | Type | Default | Description |
|---|---|---|---|
project_id | integer | — | Filter monitors by project ID |
per_page | integer | 20 | Items per page (1-100) |
page | integer | 1 | Page number |
Response
{
"data": [
{
"id": 1,
"user_id": 1,
"project_id": 1,
"url": "https://example.com",
"name": "Example Homepage",
"status": "active",
"frequency_minutes": 1440,
"viewport_width": 1280,
"viewport_height": 800,
"device_type": "desktop",
"full_page": true,
"wait_for_selector": null,
"delay_seconds": 0,
"disable_animations": false,
"hide_selectors": null,
"click_selector": null,
"http_auth_user": null,
"diff_threshold_percent": "0.5000",
"alert_enabled": true,
"alert_email": "[email protected]",
"alert_webhook_url": null,
"alert_slack_webhook_url": null,
"next_snapshot_at": "2025-03-16T14:00:00.000000Z",
"last_snapshot_at": "2025-03-15T14:00:00.000000Z",
"consecutive_errors": 0,
"last_error_message": null,
"created_at": "2025-01-15T10:30:00.000000Z",
"updated_at": "2025-03-15T14:00:00.000000Z",
"project": {
"id": 1,
"name": "example.com"
}
}
],
"meta": { "current_page": 1, "per_page": 20, "total": 1, "last_page": 1 }
}
/api/v1/monitors
Create a new monitor to start tracking a URL. The first snapshot is captured automatically after creation.
Body Parameters
| Name | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Full URL to monitor (max 2048 chars) |
name | string | No | Display name (max 255 chars) |
project_id | integer | No | Project ID to assign the monitor to |
frequency_minutes | integer | No | Capture interval in minutes. Min: 5. Default depends on plan. |
viewport_width | integer | No | Browser viewport width in px (320–3840). Default: 1280 |
viewport_height | integer | No | Browser viewport height in px (480–2160). Default: 800 |
device_type | string | No | desktop or mobile. Default: desktop |
full_page | boolean | No | Capture full-page screenshot. Default: true |
wait_for_selector | string | No | CSS selector to wait for before capture (max 500 chars) |
delay_seconds | integer | No | Extra delay before capture in seconds (0–30). Default: 0 |
hide_selectors | array | No | Array of CSS selectors to hide before capture (e.g. cookie banners) |
click_selector | string | No | CSS selector to click before capture (max 500 chars) |
diff_threshold_percent | float | No | Minimum change % to flag as significant (0–100). Default: 0.5 |
alert_enabled | boolean | No | Enable alerts on significant changes. Default: true |
alert_email | string | No | Email address for change alerts |
alert_webhook_url | string | No | Webhook URL to POST on changes |
alert_slack_webhook_url | string | No | Slack incoming webhook URL for notifications |
curl -X POST https://snapshotarchive.com/api/v1/monitors \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"url": "https://example.com",
"name": "Example Homepage",
"frequency_minutes": 60,
"full_page": true,
"viewport_width": 1280,
"viewport_height": 800,
"diff_threshold_percent": 1.0,
"alert_enabled": true,
"alert_email": "[email protected]"
}'
{
"data": {
"id": 5,
"user_id": 1,
"project_id": 3,
"url": "https://example.com",
"name": "Example Homepage",
"status": "active",
"frequency_minutes": 60,
"viewport_width": 1280,
"viewport_height": 800,
"device_type": "desktop",
"full_page": true,
"diff_threshold_percent": "1.0000",
"alert_enabled": true,
"alert_email": "[email protected]",
"created_at": "2025-03-15T14:00:00.000000Z",
"project": {
"id": 3,
"name": "example.com"
}
}
}
422 error with code monitor_limit_exceeded.
/api/v1/monitors/{id}
Retrieve a single monitor with its project and latest snapshot information.
{
"data": {
"id": 1,
"url": "https://example.com",
"name": "Example Homepage",
"status": "active",
"frequency_minutes": 60,
...
"project": { "id": 1, "name": "example.com" },
"latest_snapshot": {
"id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"monitor_id": 1,
"status": "completed",
"http_status": 200,
"response_time_ms": 1250,
"captured_at": "2025-03-15T14:00:00.000000Z"
}
}
}
/api/v1/monitors/{id}
Update monitor settings. Accepts all the same parameters as create. Only provided fields are updated.
curl -X PUT https://snapshotarchive.com/api/v1/monitors/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"frequency_minutes": 120, "diff_threshold_percent": 2.0}'
/api/v1/monitors/{id}
Delete a monitor and all its associated snapshots and diffs.
(empty response body)
/api/v1/monitors/{id}/trigger
Manually trigger an immediate snapshot capture, regardless of the monitor's schedule. The snapshot is queued and processed asynchronously.
curl -X POST https://snapshotarchive.com/api/v1/monitors/1/trigger \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
Response
{
"data": {
"message": "Snapshot queued successfully.",
"snapshot_id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
}
}
202 status means the capture has been queued. Use the returned snapshot_id to poll GET /api/v1/snapshots/{id} and check the status field (pending → processing → completed or failed).
Snapshots
Snapshots are individual captures of a monitored URL. Each snapshot includes a screenshot image, and optionally a full-page screenshot, HTML source, and PDF. Snapshots use UUID identifiers.
/api/v1/monitors/{monitor_id}/snapshots
Returns a paginated list of snapshots for a specific monitor, ordered by most recent first.
Query Parameters
| Name | Type | Default | Description |
|---|---|---|---|
per_page | integer | 20 | Items per page (1-100) |
page | integer | 1 | Page number |
Response
{
"data": [
{
"id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"monitor_id": 1,
"user_id": 1,
"status": "completed",
"http_status": 200,
"response_time_ms": 1250,
"page_weight_bytes": 524288,
"links_internal": 45,
"links_external": 12,
"links_broken": 0,
"console_errors": [],
"screenshot_path": "monitors/1/screenshots/abc123.png",
"thumbnail_path": "monitors/1/thumbnails/abc123.jpg",
"pdf_path": "monitors/1/pdfs/abc123.pdf",
"html_path": "monitors/1/html/abc123.html.gz",
"captured_at": "2025-03-15T14:00:00.000000Z",
"created_at": "2025-03-15T14:00:00.000000Z",
"updated_at": "2025-03-15T14:00:05.000000Z"
}
],
"meta": { "current_page": 1, "per_page": 20, "total": 124, "last_page": 7 }
}
/api/v1/snapshots/{id}
Retrieve detailed information about a specific snapshot, including signed file URLs for direct download.
Response
{
"data": {
"id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"monitor_id": 1,
"user_id": 1,
"status": "completed",
"http_status": 200,
"http_headers": { "content-type": "text/html; charset=utf-8" },
"response_time_ms": 1250,
"page_weight_bytes": 524288,
"links_internal": 45,
"links_external": 12,
"links_broken": 0,
"console_errors": [],
"captured_at": "2025-03-15T14:00:00.000000Z",
"monitor": {
"id": 1,
"url": "https://example.com",
"name": "Example Homepage"
},
"files": {
"screenshot_url": "https://storage.example.com/monitors/1/screenshots/abc123.png",
"pdf_url": "https://storage.example.com/monitors/1/pdfs/abc123.pdf",
"html_url": "https://storage.example.com/monitors/1/html/abc123.html.gz"
}
}
}
files object contains direct URLs to the stored files. Any file that is not available will have a null value.
/api/v1/snapshots/{id}/screenshot
Redirects to the screenshot image file (PNG format). Follow the redirect to download.
curl -L https://snapshotarchive.com/api/v1/snapshots/{id}/screenshot \
-H "Authorization: Bearer YOUR_API_KEY" \
-o screenshot.png
302 redirect to the storage URL. Use -L (follow redirects) in cURL. Returns 404 if the screenshot is not available.
/api/v1/snapshots/{id}/pdf
Redirects to the PDF capture of the page. Returns 404 if PDF is not available for this snapshot.
/api/v1/snapshots/{id}/html
Redirects to the captured HTML source of the page (gzipped). Returns 404 if HTML is not available.
/api/v1/snapshots/{id}/package
Download a complete snapshot package as a ZIP archive containing the screenshot, PDF, and HTML source.
curl https://snapshotarchive.com/api/v1/snapshots/{id}/package \
-H "Authorization: Bearer YOUR_API_KEY" \
-o snapshot-package.zip
Content-Type: application/zip. Returns 404 if no files are available.
Diffs
Diffs represent visual comparisons between consecutive snapshots. When a change exceeds the monitor's diff_threshold_percent, it is flagged as significant and triggers alerts if configured.
/api/v1/monitors/{monitor_id}/diffs
Returns a paginated list of diffs for a specific monitor, including before/after snapshot timestamps.
Query Parameters
| Name | Type | Default | Description |
|---|---|---|---|
per_page | integer | 20 | Items per page (1-100) |
page | integer | 1 | Page number |
Response
{
"data": [
{
"id": 78,
"monitor_id": 1,
"snapshot_before_id": "aaa-bbb-ccc",
"snapshot_after_id": "ddd-eee-fff",
"change_percent": "12.5000",
"pixel_count_changed": 45230,
"pixel_count_total": 1048576,
"is_significant": true,
"changed_regions": [
{ "x": 100, "y": 200, "width": 300, "height": 150 }
],
"diff_image_path": "monitors/1/diffs/diff_78.png",
"created_at": "2025-03-15T14:00:00.000000Z",
"snapshot_before": {
"id": "aaa-bbb-ccc",
"captured_at": "2025-03-14T14:00:00.000000Z"
},
"snapshot_after": {
"id": "ddd-eee-fff",
"captured_at": "2025-03-15T14:00:00.000000Z"
}
}
],
"meta": { "current_page": 1, "per_page": 20, "total": 15, "last_page": 1 }
}
/api/v1/diffs/{id}
Retrieve detailed diff information including the monitor, before/after snapshots, changed regions, and a direct URL to the diff overlay image.
Response
{
"data": {
"id": 78,
"monitor_id": 1,
"snapshot_before_id": "aaa-bbb-ccc",
"snapshot_after_id": "ddd-eee-fff",
"change_percent": "12.5000",
"pixel_count_changed": 45230,
"pixel_count_total": 1048576,
"is_significant": true,
"changed_regions": [
{ "x": 100, "y": 200, "width": 300, "height": 150 }
],
"created_at": "2025-03-15T14:00:00.000000Z",
"diff_image_url": "https://storage.example.com/monitors/1/diffs/diff_78.png",
"monitor": {
"id": 1,
"url": "https://example.com",
"name": "Example Homepage",
...
},
"snapshot_before": {
"id": "aaa-bbb-ccc",
"status": "completed",
"captured_at": "2025-03-14T14:00:00.000000Z",
...
},
"snapshot_after": {
"id": "ddd-eee-fff",
"status": "completed",
"captured_at": "2025-03-15T14:00:00.000000Z",
...
}
}
}
Account Info
Retrieve your account details and current plan configuration.
/api/v1/account
Get your account details, timezone, and full plan information.
Response
{
"data": {
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"timezone": "Europe/Berlin",
"created_at": "2025-01-01T00:00:00.000000Z",
"plan": {
"name": "Pro",
"slug": "pro",
"monitors_limit": 50,
"retention_days": 90,
"min_frequency_minutes": 60,
"api_access": true,
"diff_enabled": true
}
}
}
Usage Stats
Check your current resource usage against plan limits.
/api/v1/account/usage
Get current usage statistics for monitors, API keys, and today's snapshot count.
Response
{
"data": {
"monitors": {
"used": 12,
"limit": 50,
"remaining": 38
},
"api_keys": {
"used": 2,
"limit": 5,
"remaining": 3
},
"snapshots_today": 156
}
}
Pagination
All list endpoints return paginated results. Use the meta object to navigate through pages.
Query Parameters
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number to retrieve |
per_page | integer | 20 | Items per page (max 100) |
Meta Object
| Field | Type | Description |
|---|---|---|
current_page | integer | Current page number |
per_page | integer | Items returned per page |
total | integer | Total number of items across all pages |
last_page | integer | The last available page number |
Example: Iterating Through Pages
async function getAllMonitors(apiKey) {
let page = 1;
let allMonitors = [];
while (true) {
const response = await fetch(
`https://snapshotarchive.com/api/v1/monitors?page=${page}&per_page=100`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const { data, meta } = await response.json();
allMonitors.push(...data);
if (page >= meta.last_page) break;
page++;
}
return allMonitors;
}
Rate Limits
API requests are rate-limited to 60 requests per minute per API key.
Rate limit information is included in every response via headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
When you exceed the rate limit, you receive a 429 Too Many Requests response:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry after 42 seconds.",
"status": 429
}
}
Retry-After header for the number of seconds to wait.
Error Codes
The API uses standard HTTP status codes and returns structured error responses.
HTTP Status Codes
| Code | Meaning | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
202 | Accepted | Request accepted for async processing (e.g., snapshot trigger) |
204 | No Content | Resource deleted successfully (no response body) |
401 | Unauthorized | Missing, invalid, or expired API key |
403 | Forbidden | Authenticated but not authorized for this resource |
404 | Not Found | Resource does not exist or file not available |
422 | Validation Error | Request data failed validation or plan limit exceeded |
429 | Too Many Requests | Rate limit exceeded |
500 | Server Error | Unexpected server error |
Error Response Format
API errors return a structured error object with code, message, and status fields:
{
"error": {
"code": "invalid_api_key",
"message": "The provided API key is invalid or has been revoked.",
"status": 401
}
}
Error Codes Reference
| Code | HTTP Status | Description |
|---|---|---|
missing_api_key | 401 | No API key provided in the Authorization header |
invalid_api_key | 401 | API key does not exist or has been revoked |
expired_api_key | 401 | API key has expired |
monitor_limit_exceeded | 422 | Plan monitor limit reached, upgrade required |
rate_limit_exceeded | 429 | Too many requests in the time window |
not_found | 404 | Resource or file not found |
Validation Errors
Validation failures (422) from Laravel return field-level error details:
{
"message": "The url field is required. (and 1 more error)",
"errors": {
"url": ["The url field is required."],
"frequency_minutes": ["The frequency minutes field must be at least 5."]
}
}