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 Start
Follow these four steps to capture your first screenshot via the API:
Step 1: Check your account & plan
curl https://snapshotarchive.com/api/v1/account \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
Verify your plan includes API access (api_access: true) and check your monitor limit.
Step 2: Create a project
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": "My Website"}'
Note the returned id — you'll use it as project_id when creating monitors.
Step 3: Create a monitor
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",
"project_id": 1,
"frequency_minutes": 60,
"viewport_width": 1920,
"viewport_height": 1080,
"device_type": "desktop",
"full_page": true
}'
The response includes the monitor id. Note: your plan determines the minimum allowed frequency_minutes.
Step 4: Trigger a snapshot
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"
}
}
The snapshot is captured asynchronously. Poll GET /api/v1/snapshots/{snapshot_id} and check the status field: pending → processing → completed. Once completed, the files object contains download URLs.
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']
Handling Errors
Always check the response status and handle errors gracefully. API errors return a structured error object:
const response = await fetch('https://snapshotarchive.com/api/v1/monitors', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ url: 'https://example.com', frequency_minutes: 60 })
});
if (!response.ok) {
const body = await response.json();
if (body.error) {
// API error (401, 403, 429, etc.)
console.error(`API error [${body.error.code}]: ${body.error.message}`);
} else if (body.errors) {
// Validation error (422)
Object.entries(body.errors).forEach(([field, messages]) => {
console.error(`${field}: ${messages.join(', ')}`);
});
}
return;
}
const { data } = await response.json();
console.log('Created monitor:', data.id);
$response = Http::withToken('YOUR_API_KEY')
->acceptJson()
->post('https://snapshotarchive.com/api/v1/monitors', [
'url' => 'https://example.com',
'frequency_minutes' => 60,
]);
if ($response->failed()) {
if ($response->json('error')) {
// API error (401, 403, 429, etc.)
$code = $response->json('error.code');
$message = $response->json('error.message');
throw new \Exception("API error [{$code}]: {$message}");
}
if ($response->status() === 422) {
// Validation errors
$errors = $response->json('errors');
// Handle field-level errors...
}
}
$monitor = $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.
curl https://snapshotarchive.com/api/v1/projects/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
{
"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}
Soft-delete a project. The project and its data are archived and can be restored by contacting support. Monitors within the project are not affected.
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 |
disable_animations | boolean | No | Disable CSS animations and transitions before capture. Default: false |
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) |
clip_selector | string | No | CSS selector to clip — only capture this element area (max 500 chars) |
cookies | array | No | Array of cookie objects to set before capture (max 20). Each object requires name, value, and domain. |
diff_threshold_percent | float | No | Minimum change % to flag as significant (0–100). Default: 0.5 |
watermark_enabled | boolean | No | Add a timestamp watermark overlay on screenshots. Default: false |
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"
}
}
}
Cookies Format
When setting cookies, provide an array of objects with name, value, and domain:
{
"cookies": [
{ "name": "session_id", "value": "abc123", "domain": "example.com" },
{ "name": "consent", "value": "accepted", "domain": ".example.com" }
]
}
Hide Selectors Format
Provide an array of CSS selectors. Matching elements will be hidden (set to visibility: hidden) before capture:
{
"hide_selectors": [".cookie-banner", "#popup-overlay", ".chat-widget"]
}
422 error with code monitor_limit_exceeded. If the requested frequency is too low for your plan, you receive frequency_not_allowed.
http_auth_user / http_auth_password) can only be configured through the dashboard for security reasons. These fields appear in monitor responses but cannot be set via API.
/api/v1/monitors/{id}
Retrieve a single monitor with its project and latest snapshot information.
curl https://snapshotarchive.com/api/v1/monitors/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
{
"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}
Soft-delete a monitor. Associated snapshots and diffs are preserved and archived.
curl -X DELETE https://snapshotarchive.com/api/v1/monitors/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
(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.
curl https://snapshotarchive.com/api/v1/snapshots/9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
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.
watermark_enabled: true, this endpoint returns the image directly as 200 OK with Content-Type: image/png (no redirect) with the URL and capture timestamp overlaid on the image.
/api/v1/snapshots/{id}/pdf
Redirects to the PDF capture of the page. Returns 404 if PDF is not available for this snapshot.
403 error with code feature_not_available.
curl -L https://snapshotarchive.com/api/v1/snapshots/{id}/pdf \
-H "Authorization: Bearer YOUR_API_KEY" \
-o page.pdf
/api/v1/snapshots/{id}/html
Redirects to the captured HTML source of the page (gzipped). Returns 404 if HTML is not available.
403 error with code feature_not_available.
curl -L https://snapshotarchive.com/api/v1/snapshots/{id}/html \
-H "Authorization: Bearer YOUR_API_KEY" \
-o source.html.gz
/api/v1/snapshots/{id}/package
Download a complete snapshot package as a ZIP archive containing the screenshot, PDF, and HTML source.
403 error with code feature_not_available.
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.
curl https://snapshotarchive.com/api/v1/diffs/78 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
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.
curl https://snapshotarchive.com/api/v1/account \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
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": 365,
"min_frequency_minutes": 30,
"api_access": true,
"diff_enabled": true,
"snapshot_package": true
}
}
}
snapshot_package field indicates whether your plan includes access to PDF, HTML, and snapshot package downloads. If false, requests to /snapshots/{id}/pdf, /html, and /package will return 403.
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.
curl https://snapshotarchive.com/api/v1/account/usage \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
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 |
frequency_not_allowed | 422 | Requested capture frequency is below plan minimum |
feature_not_available | 403 | Feature requires a higher plan (e.g., PDF/HTML capture requires Starter+) |
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."]
}
}
Code Examples
The API works with any language that can make HTTP requests. Below are complete working examples that create a monitor, trigger a snapshot, and retrieve the result.
Node.js
const API_KEY = 'YOUR_API_KEY';
const BASE = 'https://snapshotarchive.com/api/v1';
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
};
// 1. Create a monitor
const monitor = await fetch(`${BASE}/monitors`, {
method: 'POST',
headers,
body: JSON.stringify({
url: 'https://example.com',
frequency_minutes: 1440,
viewport_width: 1920,
viewport_height: 1080,
device_type: 'desktop',
full_page: true,
}),
}).then(r => r.json());
const monitorId = monitor.data.id;
console.log('Monitor created:', monitorId);
// 2. Trigger a snapshot
const trigger = await fetch(`${BASE}/monitors/${monitorId}/trigger`, {
method: 'POST',
headers,
}).then(r => r.json());
const snapshotId = trigger.data.snapshot_id;
console.log('Snapshot queued:', snapshotId);
// 3. Poll until completed
let snapshot;
do {
await new Promise(r => setTimeout(r, 5000));
snapshot = await fetch(`${BASE}/snapshots/${snapshotId}`, { headers })
.then(r => r.json());
} while (snapshot.data.status !== 'completed');
console.log('Screenshot URL:', snapshot.data.files.screenshot_url);
PHP
use Illuminate\Support\Facades\Http;
$api = Http::withToken('YOUR_API_KEY')
->acceptJson()
->baseUrl('https://snapshotarchive.com/api/v1');
// 1. Create a monitor
$monitor = $api->post('/monitors', [
'url' => 'https://example.com',
'frequency_minutes' => 1440,
'viewport_width' => 1920,
'viewport_height' => 1080,
'device_type' => 'desktop',
'full_page' => true,
])->json('data');
// 2. Trigger a snapshot
$trigger = $api->post("/monitors/{$monitor['id']}/trigger")->json('data');
$snapshotId = $trigger['snapshot_id'];
// 3. Poll until completed
do {
sleep(5);
$snapshot = $api->get("/snapshots/{$snapshotId}")->json('data');
} while ($snapshot['status'] !== 'completed');
echo $snapshot['files']['screenshot_url'];
$apiKey = 'YOUR_API_KEY';
$baseUrl = 'https://snapshotarchive.com/api/v1';
function apiRequest(string $method, string $url, ?array $data = null): array {
global $apiKey;
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$apiKey}",
'Content-Type: application/json',
'Accept: application/json',
],
]);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// 1. Create a monitor
$monitor = apiRequest('POST', "{$baseUrl}/monitors", [
'url' => 'https://example.com',
'frequency_minutes' => 1440,
'device_type' => 'desktop',
]);
// 2. Trigger a snapshot
$trigger = apiRequest('POST', "{$baseUrl}/monitors/{$monitor['data']['id']}/trigger");
$snapshotId = $trigger['data']['snapshot_id'];
// 3. Poll until completed
do {
sleep(5);
$snapshot = apiRequest('GET', "{$baseUrl}/snapshots/{$snapshotId}");
} while ($snapshot['data']['status'] !== 'completed');
echo $snapshot['data']['files']['screenshot_url'];
Python
import time
import requests
API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://snapshotarchive.com/api/v1'
headers = {
'Authorization': f'Bearer {API_KEY}',
'Accept': 'application/json',
}
# 1. Create a monitor
monitor = requests.post(f'{BASE_URL}/monitors', headers=headers, json={
'url': 'https://example.com',
'frequency_minutes': 1440,
'viewport_width': 1920,
'viewport_height': 1080,
'device_type': 'desktop',
'full_page': True,
}).json()['data']
# 2. Trigger a snapshot
trigger = requests.post(
f"{BASE_URL}/monitors/{monitor['id']}/trigger", headers=headers
).json()['data']
snapshot_id = trigger['snapshot_id']
# 3. Poll until completed
while True:
time.sleep(5)
snapshot = requests.get(
f'{BASE_URL}/snapshots/{snapshot_id}', headers=headers
).json()['data']
if snapshot['status'] == 'completed':
break
print(snapshot['files']['screenshot_url'])
Ruby
require 'net/http'
require 'json'
require 'uri'
API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://snapshotarchive.com/api/v1'
def api_request(method, path, body = nil)
uri = URI("#{BASE_URL}#{path}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = case method
when :get then Net::HTTP::Get.new(uri)
when :post then Net::HTTP::Post.new(uri)
when :put then Net::HTTP::Put.new(uri)
when :delete then Net::HTTP::Delete.new(uri)
end
request['Authorization'] = "Bearer #{API_KEY}"
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = body.to_json if body
JSON.parse(http.request(request).body)
end
# 1. Create a monitor
monitor = api_request(:post, '/monitors', {
url: 'https://example.com',
frequency_minutes: 1440,
device_type: 'desktop'
})
# 2. Trigger a snapshot
trigger = api_request(:post, "/monitors/#{monitor['data']['id']}/trigger")
snapshot_id = trigger['data']['snapshot_id']
# 3. Poll until completed
loop do
sleep 5
snapshot = api_request(:get, "/snapshots/#{snapshot_id}")
break if snapshot['data']['status'] == 'completed'
end
puts snapshot['data']['files']['screenshot_url']
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)
const (
apiKey = "YOUR_API_KEY"
baseURL = "https://snapshotarchive.com/api/v1"
)
func apiRequest(method, path string, body any) map[string]any {
var reqBody *bytes.Reader
if body != nil {
b, _ := json.Marshal(body)
reqBody = bytes.NewReader(b)
} else {
reqBody = bytes.NewReader(nil)
}
req, _ := http.NewRequest(method, baseURL+path, reqBody)
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
return result
}
func main() {
// 1. Create a monitor
monitor := apiRequest("POST", "/monitors", map[string]any{
"url": "https://example.com",
"frequency_minutes": 1440,
"device_type": "desktop",
})
monitorID := monitor["data"].(map[string]any)["id"]
// 2. Trigger a snapshot
trigger := apiRequest("POST", fmt.Sprintf("/monitors/%v/trigger", monitorID), nil)
snapshotID := trigger["data"].(map[string]any)["snapshot_id"]
// 3. Poll until completed
for {
time.Sleep(5 * time.Second)
snap := apiRequest("GET", fmt.Sprintf("/snapshots/%v", snapshotID), nil)
data := snap["data"].(map[string]any)
if data["status"] == "completed" {
files := data["files"].(map[string]any)
fmt.Println(files["screenshot_url"])
break
}
}
}