Features Visual Diff Change Detection Scheduled Screenshots Watermark & Timestamp PDF Export API Change Alerts Full-Page Screenshots Pricing Blog How It Works Contact

Getting Started

The Snapshot Archive API lets you capture website screenshots, manage monitors, retrieve snapshots, and detect visual changes — all programmatically. API access is available on Starter plans and above.

Base URL https://api.snapshotarchive.com/v1

Quick Start

Four steps to capture your first screenshot via the API.

1. Get your API key

Go to Dashboard → API Keys and create a new key. Copy it — you won't see it again.

2. Create a monitor

The response includes the monitor's id — save it, you'll use it in the next steps.

bash
curl -X POST https://api.snapshotarchive.com/v1/monitors \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "url": "https://example.com",
    "frequency_minutes": 720,
    "device_type": "desktop",
    "viewport_width": 1920,
    "viewport_height": 1080
  }'
php
$response = Http::withToken('YOUR_API_KEY')
    ->post('https://api.snapshotarchive.com/v1/monitors', [
        'url' => 'https://example.com',
        'frequency_minutes' => 720,
        'device_type' => 'desktop',
        'viewport_width' => 1920,
        'viewport_height' => 1080,
    ]);

$monitor = $response->json('data');
python
import requests

response = requests.post(
    'https://api.snapshotarchive.com/v1/monitors',
    headers={
        'Authorization': 'Bearer YOUR_API_KEY',
        'Accept': 'application/json',
    },
    json={
        'url': 'https://example.com',
        'frequency_minutes': 720,
        'device_type': 'desktop',
        'viewport_width': 1920,
        'viewport_height': 1080,
    }
)

monitor = response.json()['data']
javascript
const response = await fetch('https://api.snapshotarchive.com/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: 720,
    device_type: 'desktop',
    viewport_width: 1920,
    viewport_height: 1080,
  }),
});

const { data: monitor } = await response.json();

3. Trigger a snapshot

Use the id from the create response (step 2) to trigger a capture. The response gives you a snapshot_id to track progress.

bash
# Use the monitor ID returned from step 2 (e.g. 42)
curl -X POST https://api.snapshotarchive.com/v1/monitors/42/trigger \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Response: {"data": {"message": "Snapshot queued successfully.", "snapshot_id": "9e8f7a6b-..."}}

4. Get the result

Use the snapshot_id from step 3 to poll until the capture is complete, then download the screenshot.

bash
# Poll until status is "completed" or "failed"
curl https://api.snapshotarchive.com/v1/snapshots/9e8f7a6b-... \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Once completed, download the screenshot
curl -L https://api.snapshotarchive.com/v1/snapshots/9e8f7a6b-.../screenshot \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o screenshot.png
Where do IDs come from? Every ID you need comes from a previous API response — you never need to look them up manually. If you already have monitors in the Dashboard, use GET /v1/monitors to get their IDs programmatically.

Authentication

All API requests require a Bearer token in the Authorization header. Generate API keys from your Dashboard.

HTTP Header
Authorization: Bearer YOUR_API_KEY
Accept: application/json
Content-Type: application/json
Keep your API key secret Never expose API keys in client-side code, public repositories, or URLs. If a key is compromised, revoke it immediately from your Dashboard and create a new one.

Every response includes JSON. Always send Accept: application/json to ensure proper error formatting. For POST/PATCH requests, also send Content-Type: application/json.

Plan Limits

API access is available on Starter plans and above. Each plan has different limits for monitors, capture frequency, and data retention.

Feature Free Starter Pro Growth Business
API Access — ✓ ✓ ✓ ✓
Monitors 3 20 50 100 200
Min Frequency Daily Every 12h Every 6h Hourly Every 30 min
Retention 30 days 90 days 1 year 2 years 3 years
Visual Diff — ✓ ✓ ✓ ✓
PDF / HTML Export — ✓ ✓ ✓ ✓
API Keys 0 5 5 5 5
Price Free $14/mo $39/mo $79/mo $129/mo
Exceeding limits If you downgrade or your subscription ends, monitors beyond your plan limit are set to plan_exceeded status. Upgrade your plan to reactivate them.

Projects

Projects let you organize monitors into groups. Every account has a default project. Monitors can optionally belong to a project.

GET /v1/projects

Returns a paginated list of your projects.

Query Parameters

ParameterTypeDescription
per_pageintegerItems per page (default: 20)
sortstringSort field: created_at (default), updated_at, name
orderstringSort direction: desc (default) or asc

Request Example

bash
curl "https://api.snapshotarchive.com/v1/projects" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
projects = requests.get(f'{BASE}/projects', headers=headers).json()

for project in projects['data']:
    print(f"ID: {project['id']} | {project['name']} | {project['monitors_count']} monitors")

# Use the project ID to filter monitors
project_id = projects['data'][0]['id']
javascript
const { data: projects } = await fetch(`${BASE}/projects`, { headers }).then(r => r.json());

projects.forEach(p => {
  console.log(`ID: ${p.id} | ${p.name} | ${p.monitors_count} monitors`);
});

// Use the project ID to filter monitors
const projectId = projects[0].id;

Response

Response 200
{
  "data": [
    {
      "id": 1,
      "name": "My Website",
      "description": "Production site monitoring",
      "is_default": true,
      "monitors_count": 5,
      "created_at": "2026-01-15T10:30:00Z",
      "updated_at": "2026-01-15T10:30:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 1,
    "last_page": 1
  }
}
POST /v1/projects

Create a new project.

Request Body

FieldTypeRequiredDescription
namestringYesProject name (max 255 characters)
descriptionstringNoProject description (max 1000 characters)
bash
curl -X POST https://api.snapshotarchive.com/v1/projects \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"name": "My Website", "description": "Production monitoring"}'
php
$response = Http::withToken('YOUR_API_KEY')
    ->post('https://api.snapshotarchive.com/v1/projects', [
        'name' => 'My Website',
        'description' => 'Production monitoring',
    ]);

$project = $response->json('data');
python
response = requests.post(
    'https://api.snapshotarchive.com/v1/projects',
    headers={'Authorization': 'Bearer YOUR_API_KEY', 'Accept': 'application/json'},
    json={'name': 'My Website', 'description': 'Production monitoring'}
)

project = response.json()['data']
javascript
const response = await fetch('https://api.snapshotarchive.com/v1/projects', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({
    name: 'My Website',
    description: 'Production monitoring',
  }),
});

const { data: project } = await response.json();
Dashboard showing the 'My Website' project created via API
The project created via API appears in your dashboard — with monitor count, description, and creation date.
GET /v1/projects/{id}

Retrieve a single project by ID.

PATCH /v1/projects/{id}

Update a project. Send only the fields you want to change.

DELETE /v1/projects/{id}

Delete a project. Returns 204 No Content on success.

Monitors

Monitors are the core resource. Each monitor tracks a URL and captures screenshots on a schedule. You can configure viewport size, device type, authentication, and many other capture options.

GET /v1/monitors

Returns a paginated list of your monitors.

Query Parameters

ParameterTypeDescription
per_pageintegerItems per page (default: 20)
project_idintegerFilter monitors by project
statusstringFilter by status: active, paused, error, plan_exceeded
urlstringSearch monitors by URL (partial match)
namestringSearch monitors by name (partial match)
tagstringFilter monitors by tag name (exact match)
sortstringSort field: created_at (default), updated_at, url, name, status, last_snapshot_at, next_snapshot_at
orderstringSort direction: desc (default) or asc

Request Example

bash
# List all active monitors
curl "https://api.snapshotarchive.com/v1/monitors?status=active" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Search by URL
curl "https://api.snapshotarchive.com/v1/monitors?url=example.com" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
import requests

API_KEY = 'YOUR_API_KEY'
BASE = 'https://api.snapshotarchive.com/v1'
headers = {'Authorization': f'Bearer {API_KEY}', 'Accept': 'application/json'}

# List monitors
response = requests.get(f'{BASE}/monitors', headers=headers, params={'status': 'active'})
monitors = response.json()

# Access the data
for monitor in monitors['data']:
    print(f"ID: {monitor['id']} | {monitor['url']} | Status: {monitor['status']}")

# Get a specific monitor's ID for use in other endpoints
monitor_id = monitors['data'][0]['id']  # e.g. 42
print(f"\nUse this ID for snapshots: GET /v1/monitors/{monitor_id}/snapshots")
javascript
const API_KEY = 'YOUR_API_KEY';
const BASE = 'https://api.snapshotarchive.com/v1';
const headers = { 'Authorization': `Bearer ${API_KEY}`, 'Accept': 'application/json' };

// List monitors
const res = await fetch(`${BASE}/monitors?status=active`, { headers });
const { data: monitors, meta } = await res.json();

// Access the data
monitors.forEach(m => {
  console.log(`ID: ${m.id} | ${m.url} | Status: ${m.status}`);
});

// Get a specific monitor's ID for use in other endpoints
const monitorId = monitors[0].id; // e.g. 42
console.log(`\nUse this ID for snapshots: GET /v1/monitors/${monitorId}/snapshots`);

Response

Response 200
{
  "data": [
    {
      "id": 42,
      "project_id": 1,
      "url": "https://example.com",
      "name": "Example Homepage",
      "status": "active",
      "frequency_minutes": 720,
      "viewport_width": 1920,
      "viewport_height": 1080,
      "full_page": false,
      "device_type": "desktop",
      "diff_enabled": true,
      "diff_threshold_percent": 50,
      "watermark_enabled": false,
      "alert_enabled": false,
      "last_snapshot_at": "2026-07-24T08:00:00Z",
      "next_snapshot_at": "2026-07-24T20:00:00Z",
      "created_at": "2026-01-15T10:30:00Z",
      "updated_at": "2026-07-24T08:00:05Z",
      "project": {
        "id": 1,
        "name": "My Website"
      }
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 5,
    "last_page": 1
  }
}
Using response data The data[].id field (e.g. 42) is the monitor ID. Use it in other endpoints like /v1/monitors/42/snapshots, /v1/monitors/42/trigger, and /v1/monitors/42/diffs.
POST /v1/monitors

Create a new monitor. The monitor starts capturing immediately on its schedule.

Request Body

FieldTypeRequiredDescription
urlstringYesURL to monitor (max 2048 chars)
namestringNoDisplay name (max 255 chars)
project_idintegerNoProject to assign monitor to
frequency_minutesintegerYesCapture interval in minutes (min: 30, subject to plan limits)
device_typestringYesdesktop or mobile
viewport_widthintegerYesBrowser width in pixels (320–3840)
viewport_heightintegerYesBrowser height in pixels (480–2160)
full_pagebooleanNoCapture the full scrollable page (default: false)
delay_secondsintegerNoWait before capture, 0–30 seconds
wait_for_selectorstringNoCSS selector to wait for before capture
click_selectorstringNoCSS selector to click before capture
clip_selectorstringNoCSS selector to clip screenshot to a specific element
hide_selectorsarrayNoCSS selectors to hide (e.g. cookie banners)
disable_animationsbooleanNoDisable CSS animations/transitions
http_auth_userstringNoHTTP Basic Auth username
http_auth_passwordstringNoHTTP Basic Auth password
cookiesarrayNoCustom cookies (max 20). Each: {name, value, domain}
diff_threshold_percentnumberNoVisual diff sensitivity, 0–100 (default: 50)
watermark_enabledbooleanNoAdd timestamp watermark to screenshots
alert_enabledbooleanNoEnable change alerts
alert_emailstringNoEmail for change notifications
alert_webhook_urlstringNoWebhook URL for change notifications
alert_slack_webhook_urlstringNoSlack webhook URL for notifications
bash
curl -X POST https://api.snapshotarchive.com/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",
    "project_id": 1,
    "frequency_minutes": 360,
    "device_type": "desktop",
    "viewport_width": 1920,
    "viewport_height": 1080,
    "full_page": true,
    "hide_selectors": [".cookie-banner", "#popup"],
    "diff_threshold_percent": 30,
    "alert_enabled": true,
    "alert_email": "[email protected]"
  }'
php
$response = Http::withToken('YOUR_API_KEY')
    ->post('https://api.snapshotarchive.com/v1/monitors', [
        'url' => 'https://example.com',
        'name' => 'Example Homepage',
        'project_id' => 1,
        'frequency_minutes' => 360,
        'device_type' => 'desktop',
        'viewport_width' => 1920,
        'viewport_height' => 1080,
        'full_page' => true,
        'hide_selectors' => ['.cookie-banner', '#popup'],
        'diff_threshold_percent' => 30,
        'alert_enabled' => true,
        'alert_email' => '[email protected]',
    ]);

$monitor = $response->json('data');
python
response = requests.post(
    'https://api.snapshotarchive.com/v1/monitors',
    headers={'Authorization': 'Bearer YOUR_API_KEY', 'Accept': 'application/json'},
    json={
        'url': 'https://example.com',
        'name': 'Example Homepage',
        'project_id': 1,
        'frequency_minutes': 360,
        'device_type': 'desktop',
        'viewport_width': 1920,
        'viewport_height': 1080,
        'full_page': True,
        'hide_selectors': ['.cookie-banner', '#popup'],
        'diff_threshold_percent': 30,
        'alert_enabled': True,
        'alert_email': '[email protected]',
    }
)

monitor = response.json()['data']
javascript
const response = await fetch('https://api.snapshotarchive.com/v1/monitors', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com',
    name: 'Example Homepage',
    project_id: 1,
    frequency_minutes: 360,
    device_type: 'desktop',
    viewport_width: 1920,
    viewport_height: 1080,
    full_page: true,
    hide_selectors: ['.cookie-banner', '#popup'],
    diff_threshold_percent: 30,
    alert_enabled: true,
    alert_email: '[email protected]',
  }),
});

const { data: monitor } = await response.json();
Dashboard showing the 'Hacker News' monitor created via API
Monitors created via API are fully visible in your dashboard — with status, project, change detection, alerts, and frequency.
GET /v1/monitors/{id}

Retrieve a single monitor with its latest snapshot.

PATCH /v1/monitors/{id}

Update a monitor. Send only the fields you want to change.

bash
# Pause a monitor
curl -X PATCH https://api.snapshotarchive.com/v1/monitors/42 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"status": "paused"}'
DELETE /v1/monitors/{id}

Delete a monitor and all its snapshots. Returns 204 No Content.

POST /v1/monitors/{id}/trigger

Trigger an immediate snapshot capture. Returns 202 Accepted — the snapshot is processed asynchronously.

Response 202
{
  "data": {
    "message": "Snapshot queued successfully.",
    "snapshot_id": "9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b"
  }
}
Async capture After triggering, poll GET /v1/snapshots/{snapshot_id} until status changes from pending to completed or failed. Typical capture time is 10–30 seconds.
Dashboard showing a completed snapshot triggered via API
Once the snapshot completes, it appears in your Archive with the captured screenshot, timestamp, response time, and HTTP status.

Snapshots

Snapshots are the captured screenshots. Each snapshot includes the screenshot image, and optionally a PDF export and HTML source.

GET /v1/monitors/{monitorId}/snapshots

Returns a paginated list of snapshots for a specific monitor, newest first.

Query Parameters

ParameterTypeDescription
per_pageintegerItems per page (default: 20)
captured_afterstringOnly snapshots captured after this date/time (ISO 8601, e.g. 2026-09-01 or 2026-09-01T00:00:00Z)
captured_beforestringOnly snapshots captured before this date/time
statusstringFilter by status: completed, failed, pending, processing
sortstringSort field: captured_at (default), created_at, http_status, response_time_ms, page_weight_bytes
orderstringSort direction: desc (default) or asc

Request Example

bash
# List snapshots for monitor 42 (get monitor ID from GET /v1/monitors)
curl "https://api.snapshotarchive.com/v1/monitors/42/snapshots?status=completed&captured_after=2026-09-01" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
# Get monitor ID first, then list its snapshots
monitors = requests.get(f'{BASE}/monitors', headers=headers, params={'url': 'example.com'}).json()
monitor_id = monitors['data'][0]['id']  # e.g. 42

snapshots = requests.get(
    f'{BASE}/monitors/{monitor_id}/snapshots',
    headers=headers,
    params={'status': 'completed', 'captured_after': '2026-09-01'}
).json()

for snap in snapshots['data']:
    print(f"{snap['id'][:8]}... | {snap['captured_at']} | HTTP {snap['http_status']}")
javascript
// Get monitor ID first, then list its snapshots
const monitors = await fetch(`${BASE}/monitors?url=example.com`, { headers }).then(r => r.json());
const monitorId = monitors.data[0].id; // e.g. 42

const params = new URLSearchParams({ status: 'completed', captured_after: '2026-09-01' });
const { data: snapshots } = await fetch(
  `${BASE}/monitors/${monitorId}/snapshots?${params}`, { headers }
).then(r => r.json());

snapshots.forEach(s => {
  console.log(`${s.id.slice(0, 8)}... | ${s.captured_at} | HTTP ${s.http_status}`);
});

Response

Response 200
{
  "data": [
    {
      "id": "9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b",
      "monitor_id": 42,
      "status": "completed",
      "http_status": 200,
      "response_time_ms": 1250,
      "page_weight_bytes": 2456789,
      "error_message": null,
      "captured_at": "2026-07-24T08:00:15Z",
      "created_at": "2026-07-24T08:00:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 150,
    "last_page": 8
  }
}
GET /v1/monitors/{monitorId}/latest-snapshot

Get the most recent completed snapshot for a monitor, with signed download URLs. This is a shortcut — instead of listing snapshots and picking the first one, you get the latest in a single call.

bash
# Get the latest screenshot for monitor 42
curl https://api.snapshotarchive.com/v1/monitors/42/latest-snapshot \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Returns 404 if the monitor has no completed snapshots yet.

GET /v1/snapshots/{uuid}

Retrieve a snapshot with signed download URLs for all available files.

Request Example

bash
# Use the snapshot UUID from trigger response or snapshots list
curl "https://api.snapshotarchive.com/v1/snapshots/9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
# Get snapshot details with download URLs
snapshot_uuid = '9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b'  # from trigger or list
snapshot = requests.get(f'{BASE}/snapshots/{snapshot_uuid}', headers=headers).json()['data']

# Download the screenshot
if snapshot['files']['screenshot_url']:
    img = requests.get(snapshot['files']['screenshot_url'])
    with open('screenshot.png', 'wb') as f:
        f.write(img.content)
javascript
// Get snapshot details with download URLs
const snapshotId = '9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b'; // from trigger or list
const { data: snapshot } = await fetch(`${BASE}/snapshots/${snapshotId}`, { headers })
  .then(r => r.json());

console.log(`Status: ${snapshot.status}`);
console.log(`Screenshot: ${snapshot.files.screenshot_url}`);

Response

Response 200
{
  "data": {
    "id": "9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b",
    "monitor_id": 42,
    "status": "completed",
    "http_status": 200,
    "response_time_ms": 1250,
    "page_weight_bytes": 2456789,
    "meta_data": {
      "title": "Example Domain",
      "description": "This domain is for use in examples..."
    },
    "error_message": null,
    "captured_at": "2026-07-24T08:00:15Z",
    "created_at": "2026-07-24T08:00:00Z",
    "files": {
      "screenshot_url": "https://snapshots.snapshotarchive.com/...",
      "pdf_url": "https://snapshots.snapshotarchive.com/...",
      "html_url": "https://snapshots.snapshotarchive.com/..."
    }
  }
}

Download Files

GET /v1/snapshots/{uuid}/screenshot

Download the screenshot image. If the monitor has watermarking enabled, the watermark is applied dynamically. Otherwise, redirects to a signed storage URL.

GET /v1/snapshots/{uuid}/pdf

Download the PDF export. Requires Starter plan or above.

GET /v1/snapshots/{uuid}/html

Download the HTML source. Requires Starter plan or above.

GET /v1/snapshots/{uuid}/package

Download all files (screenshot, PDF, HTML) as a ZIP archive. Requires Starter plan or above.

Polling Example

After triggering a snapshot, poll until it's ready:

bash
#!/bin/bash
# Trigger snapshot
RESPONSE=$(curl -s -X POST https://api.snapshotarchive.com/v1/monitors/42/trigger \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json")

SNAPSHOT_ID=$(echo $RESPONSE | jq -r '.data.snapshot_id')

# Poll until completed
while true; do
  STATUS=$(curl -s https://api.snapshotarchive.com/v1/snapshots/$SNAPSHOT_ID \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Accept: application/json" | jq -r '.data.status')

  echo "Status: $STATUS"

  if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then
    break
  fi

  sleep 5
done
php
// Trigger snapshot
$trigger = Http::withToken('YOUR_API_KEY')
    ->post('https://api.snapshotarchive.com/v1/monitors/42/trigger');

$snapshotId = $trigger->json('data.snapshot_id');

// Poll until completed
do {
    sleep(5);
    $snapshot = Http::withToken('YOUR_API_KEY')
        ->get("https://api.snapshotarchive.com/v1/snapshots/{$snapshotId}")
        ->json('data');
} while (in_array($snapshot['status'], ['pending', 'processing']));

// Download screenshot
if ($snapshot['status'] === 'completed') {
    $screenshotUrl = $snapshot['files']['screenshot_url'];
}
python
import time
import requests

API_KEY = 'YOUR_API_KEY'
BASE = 'https://api.snapshotarchive.com/v1'
headers = {'Authorization': f'Bearer {API_KEY}', 'Accept': 'application/json'}

# Trigger snapshot
trigger = requests.post(f'{BASE}/monitors/42/trigger', headers=headers)
snapshot_id = trigger.json()['data']['snapshot_id']

# Poll until completed
while True:
    time.sleep(5)
    snap = requests.get(f'{BASE}/snapshots/{snapshot_id}', headers=headers).json()['data']
    if snap['status'] in ('completed', 'failed'):
        break

# Download screenshot
if snap['status'] == 'completed':
    screenshot_url = snap['files']['screenshot_url']
    img = requests.get(screenshot_url)
    with open('screenshot.png', 'wb') as f:
        f.write(img.content)
javascript
const API_KEY = 'YOUR_API_KEY';
const BASE = 'https://api.snapshotarchive.com/v1';
const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Accept': 'application/json',
};

// Trigger snapshot
const trigger = await fetch(`${BASE}/monitors/42/trigger`, {
  method: 'POST', headers,
});
const { data: { snapshot_id } } = await trigger.json();

// Poll until completed
const sleep = ms => new Promise(r => setTimeout(r, ms));
let snapshot;
do {
  await sleep(5000);
  const res = await fetch(`${BASE}/snapshots/${snapshot_id}`, { headers });
  snapshot = (await res.json()).data;
} while (['pending', 'processing'].includes(snapshot.status));

// Download screenshot
if (snapshot.status === 'completed') {
  const img = await fetch(snapshot.files.screenshot_url);
  // save to file or process the image
}

Diffs

Visual diffs compare consecutive snapshots to detect changes. Each diff includes a change percentage and a highlighted diff image showing what changed.

GET /v1/monitors/{monitorId}/diffs

Returns a paginated list of diffs for a specific monitor.

Query Parameters

ParameterTypeDescription
per_pageintegerItems per page (default: 20)
created_afterstringOnly diffs created after this date/time (ISO 8601)
created_beforestringOnly diffs created before this date/time
min_change_percentnumberOnly diffs with change ≥ this percentage (e.g. 5 for 5%+)
max_change_percentnumberOnly diffs with change ≤ this percentage
is_significantbooleanFilter by significance: true or false
sortstringSort field: created_at (default), change_percent
orderstringSort direction: desc (default) or asc

Request Example

bash
# List diffs for monitor 42 (get monitor ID from GET /v1/monitors)
curl "https://api.snapshotarchive.com/v1/monitors/42/diffs?is_significant=true" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
# Get monitor ID first
monitors = requests.get(f'{BASE}/monitors', headers=headers, params={'url': 'example.com'}).json()
monitor_id = monitors['data'][0]['id']

# List significant diffs
diffs = requests.get(
    f'{BASE}/monitors/{monitor_id}/diffs',
    headers=headers,
    params={'is_significant': 'true', 'sort': 'change_percent', 'order': 'desc'}
).json()

for diff in diffs['data']:
    print(f"Diff #{diff['id']}: {diff['change_percent']}% change on {diff['created_at']}")
javascript
// Get monitor ID first
const monitors = await fetch(`${BASE}/monitors?url=example.com`, { headers }).then(r => r.json());
const monitorId = monitors.data[0].id;

// List significant diffs
const params = new URLSearchParams({ is_significant: 'true', sort: 'change_percent', order: 'desc' });
const { data: diffs } = await fetch(`${BASE}/monitors/${monitorId}/diffs?${params}`, { headers })
  .then(r => r.json());

diffs.forEach(d => {
  console.log(`Diff #${d.id}: ${d.change_percent}% change on ${d.created_at}`);
});

Response

Response 200
{
  "data": [
    {
      "id": 789,
      "monitor_id": 42,
      "change_percent": 12.5,
      "pixel_count_changed": 28500,
      "pixel_count_total": 228000,
      "is_significant": true,
      "created_at": "2026-07-24T08:00:20Z",
      "snapshot_before": {
        "id": "abc12345-...",
        "captured_at": "2026-07-23T08:00:15Z"
      },
      "snapshot_after": {
        "id": "def67890-...",
        "captured_at": "2026-07-24T08:00:15Z"
      }
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 30,
    "last_page": 2
  }
}
GET /v1/diffs/{id}

Retrieve a single diff with a signed URL for the diff image.

Request Example

bash
# Use the diff ID from the diffs list response
curl "https://api.snapshotarchive.com/v1/diffs/789" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
# Get diff details with the diff image URL
diff_id = 789  # from the diffs list response
diff = requests.get(f'{BASE}/diffs/{diff_id}', headers=headers).json()['data']

print(f"Change: {diff['change_percent']}%")
print(f"Diff image: {diff['diff_image_url']}")
javascript
// Get diff details with the diff image URL
const diffId = 789; // from the diffs list response
const { data: diff } = await fetch(`${BASE}/diffs/${diffId}`, { headers }).then(r => r.json());

console.log(`Change: ${diff.change_percent}%`);
console.log(`Diff image: ${diff.diff_image_url}`);

Response

Response 200
{
  "data": {
    "id": 789,
    "monitor_id": 42,
    "change_percent": 12.5,
    "pixel_count_changed": 28500,
    "pixel_count_total": 228000,
    "is_significant": true,
    "diff_image_url": "https://snapshots.snapshotarchive.com/...",
    "created_at": "2026-07-24T08:00:20Z",
    "snapshot_before": {
      "id": "abc12345-...",
      "captured_at": "2026-07-23T08:00:15Z"
    },
    "snapshot_after": {
      "id": "def67890-...",
      "captured_at": "2026-07-24T08:00:15Z"
    }
  }
}

Account Info

Retrieve your account details and current plan information.

GET /v1/account

Returns your account profile and active plan.

Request Example

bash
curl "https://api.snapshotarchive.com/v1/account" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
account = requests.get(f'{BASE}/account', headers=headers).json()['data']

print(f"Plan: {account['plan']['name']}")
print(f"Monitors limit: {account['plan']['monitors_limit']}")
print(f"API access: {account['plan']['api_access']}")
javascript
const { data: account } = await fetch(`${BASE}/account`, { headers }).then(r => r.json());

console.log(`Plan: ${account.plan.name}`);
console.log(`Monitors limit: ${account.plan.monitors_limit}`);

Response

Response 200
{
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]",
    "timezone": "America/New_York",
    "created_at": "2026-01-15T10:30:00Z",
    "plan": {
      "name": "Pro",
      "slug": "pro",
      "monitors_limit": 50,
      "retention_days": 365,
      "min_frequency_minutes": 360,
      "api_access": true,
      "diff_enabled": true,
      "snapshot_package": true
    }
  }
}

Usage Stats

Check your current resource usage against plan limits.

GET /v1/account/usage

Returns current usage for monitors, API keys, and today's snapshot count.

Request Example

bash
curl "https://api.snapshotarchive.com/v1/account/usage" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
usage = requests.get(f'{BASE}/account/usage', headers=headers).json()['data']

monitors = usage['monitors']
print(f"Monitors: {monitors['used']}/{monitors['limit']} ({monitors['remaining']} remaining)")
print(f"Snapshots today: {usage['snapshots_today']}")
javascript
const { data: usage } = await fetch(`${BASE}/account/usage`, { headers }).then(r => r.json());

console.log(`Monitors: ${usage.monitors.used}/${usage.monitors.limit}`);
console.log(`Snapshots today: ${usage.snapshots_today}`);

Response

Response 200
{
  "data": {
    "monitors": {
      "used": 12,
      "limit": 50,
      "remaining": 38
    },
    "api_keys": {
      "used": 2,
      "limit": 5,
      "remaining": 3
    },
    "snapshots_today": 24
  }
}

Pagination

All list endpoints return paginated results. Use the meta object to navigate through pages.

Query Parameters

ParameterTypeDescription
pageintegerPage number (default: 1)
per_pageintegerItems per page (default: 20)
Pagination meta
{
  "meta": {
    "current_page": 2,
    "per_page": 20,
    "total": 85,
    "last_page": 5
  }
}
bash
# Get page 3 with 50 items per page
curl "https://api.snapshotarchive.com/v1/monitors?page=3&per_page=50" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Rate Limits

API requests are rate-limited to ensure fair usage. Rate limit information is included in response headers.

HeaderDescription
X-RateLimit-LimitMax requests per minute
X-RateLimit-RemainingRemaining requests in current window
Retry-AfterSeconds to wait (only on 429 responses)

If you exceed the rate limit, you'll receive a 429 Too Many Requests response. Wait for the duration indicated in the Retry-After header before making another request.

Error Codes

All errors return a consistent JSON structure with an error code, human-readable message, and HTTP status.

Error Response
{
  "error": {
    "code": "monitor_limit_exceeded",
    "message": "You have reached the maximum number of monitors for your plan.",
    "status": 422
  }
}

HTTP Status Codes

CodeMeaning
200Success
201Resource created
202Request accepted (async processing)
204Deleted successfully (no content)
401Invalid or missing API key
403Feature not available on your plan
404Resource not found
422Validation error or plan limit exceeded
429Rate limit exceeded
500Internal server error

Error Codes Reference

Error CodeHTTPDescription
monitor_limit_exceeded422Monitor count exceeds your plan limit. Upgrade or delete existing monitors.
frequency_not_allowed422Requested frequency is lower than your plan allows.
feature_not_available403Your plan does not include this feature (PDF, HTML export, etc.).
not_found404The requested resource or file does not exist.
Validation errors For 422 responses from validation, the response includes a message field with details and an errors object mapping field names to error messages.
Validation Error 422
{
  "message": "The url field is required.",
  "errors": {
    "url": ["The url field is required."],
    "frequency_minutes": ["The frequency minutes field is required."]
  }
}

Tags

Tags help you categorize monitors. You can create tags in the Dashboard and filter monitors by tag via the API.

GET /v1/tags

Returns all your tags with the number of monitors attached to each.

Request Example

bash
curl "https://api.snapshotarchive.com/v1/tags" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
response = requests.get(f'{BASE}/tags', headers=headers)
tags = response.json()['data']

for tag in tags:
    print(f"{tag['name']}: {tag['monitors_count']} monitors")
javascript
const { data: tags } = await fetch(`${BASE}/tags`, { headers }).then(r => r.json());

tags.forEach(tag => {
  console.log(`${tag.name}: ${tag.monitors_count} monitors`);
});

Response

Response 200
{
  "data": [
    {
      "id": 1,
      "name": "production",
      "color": "#22c55e",
      "monitors_count": 12,
      "created_at": "2026-03-10T14:20:00Z",
      "updated_at": "2026-03-10T14:20:00Z"
    },
    {
      "id": 2,
      "name": "competitors",
      "color": "#f97316",
      "monitors_count": 5,
      "created_at": "2026-03-12T09:15:00Z",
      "updated_at": "2026-03-12T09:15:00Z"
    }
  ]
}

To filter monitors by tag, use the tag parameter on the monitors list endpoint:

bash
# Get all monitors tagged "production"
curl "https://api.snapshotarchive.com/v1/monitors?tag=production" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Filtering & Sorting

All list endpoints support filtering and sorting via query parameters. Combine multiple filters in a single request to narrow down results.

How Filters Work

Filters are passed as query parameters. Only records matching all specified filters are returned (AND logic). Omitted filters have no effect.

Date Filters

Date parameters accept ISO 8601 format. You can use a date (2026-09-01) or a full timestamp (2026-09-01T14:30:00Z).

bash
# Snapshots from September 2026
curl "https://api.snapshotarchive.com/v1/monitors/42/snapshots?captured_after=2026-09-01&captured_before=2026-09-30" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Only completed snapshots, oldest first
curl "https://api.snapshotarchive.com/v1/monitors/42/snapshots?status=completed&sort=captured_at&order=asc" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Diffs with more than 5% visual change
curl "https://api.snapshotarchive.com/v1/monitors/42/diffs?min_change_percent=5&sort=change_percent&order=desc" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Search monitors by URL
curl "https://api.snapshotarchive.com/v1/monitors?url=example.com&status=active" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Filter Reference

EndpointAvailable Filters
/v1/monitorsproject_id, status, url, name, tag
/v1/monitors/{id}/snapshotscaptured_after, captured_before, status
/v1/monitors/{id}/diffscreated_after, created_before, min_change_percent, max_change_percent, is_significant

Sorting

All list endpoints accept sort and order parameters. If not specified, results are sorted by creation date, newest first.

EndpointSortable Fields
/v1/projectscreated_at, updated_at, name
/v1/monitorscreated_at, updated_at, url, name, status, last_snapshot_at, next_snapshot_at
/v1/monitors/{id}/snapshotscaptured_at, created_at, http_status, response_time_ms, page_weight_bytes
/v1/monitors/{id}/diffscreated_at, change_percent

Guide: Get a Snapshot by Date

A common task is retrieving the screenshot of a specific page as it appeared on a particular date. Use the captured_after and captured_before filters with per_page=1 to get exactly the snapshot you need.

Step 1: Find your monitor

First, list your monitors to get the ID of the one you need. You can search by URL:

bash
# Find monitor by URL
curl "https://api.snapshotarchive.com/v1/monitors?url=example.com" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Response includes: "data": [{"id": 42, "url": "https://example.com", ...}]
# Use the "id" value (42) in the next step

Step 2: Find the closest snapshot to a specific date

bash
# Get the screenshot from September 5, 2026
# Use captured_before to get the latest snapshot on or before that date
curl "https://api.snapshotarchive.com/v1/monitors/42/snapshots?captured_before=2026-09-05T23:59:59Z&status=completed&per_page=1" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# The first item in "data" is the closest snapshot before that date
# Use the snapshot UUID to download the image:
curl -L "https://api.snapshotarchive.com/v1/snapshots/SNAPSHOT_UUID/screenshot" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o screenshot-sept5.png
python
import requests

API_KEY = 'YOUR_API_KEY'
BASE = 'https://api.snapshotarchive.com/v1'
headers = {'Authorization': f'Bearer {API_KEY}', 'Accept': 'application/json'}

# Step 1: Find the monitor by URL
monitors = requests.get(f'{BASE}/monitors', headers=headers, params={'url': 'example.com'}).json()
monitor_id = monitors['data'][0]['id']  # e.g. 42
print(f"Monitor ID: {monitor_id}")

# Step 2: Get the closest snapshot on or before September 5
response = requests.get(
    f'{BASE}/monitors/{monitor_id}/snapshots',
    headers=headers,
    params={
        'captured_before': '2026-09-05T23:59:59Z',
        'status': 'completed',
        'per_page': 1,
    }
)
snapshots = response.json()['data']

if snapshots:
    snapshot = snapshots[0]
    print(f"Found: {snapshot['captured_at']}")

    # Step 3: Download the image
    detail = requests.get(f"{BASE}/snapshots/{snapshot['id']}", headers=headers).json()['data']
    img = requests.get(detail['files']['screenshot_url'])
    with open('screenshot-sept5.png', 'wb') as f:
        f.write(img.content)
else:
    print("No snapshot found for that date")
javascript
const API_KEY = 'YOUR_API_KEY';
const BASE = 'https://api.snapshotarchive.com/v1';
const headers = { 'Authorization': `Bearer ${API_KEY}`, 'Accept': 'application/json' };

// Step 1: Find the monitor by URL
const monitors = await fetch(`${BASE}/monitors?url=example.com`, { headers }).then(r => r.json());
const monitorId = monitors.data[0].id; // e.g. 42
console.log(`Monitor ID: ${monitorId}`);

// Step 2: Get the closest snapshot on or before September 5
const params = new URLSearchParams({
  captured_before: '2026-09-05T23:59:59Z',
  status: 'completed',
  per_page: '1',
});
const snapshotsRes = await fetch(`${BASE}/monitors/${monitorId}/snapshots?${params}`, { headers });
const { data: snapshots } = await snapshotsRes.json();

if (snapshots.length > 0) {
  const snapshot = snapshots[0];
  console.log(`Found: ${snapshot.captured_at}`);

  // Step 3: Get download URLs
  const detail = await fetch(`${BASE}/snapshots/${snapshot.id}`, { headers }).then(r => r.json());
  console.log('Screenshot URL:', detail.data.files.screenshot_url);
} else {
  console.log('No snapshot found for that date');
}

Guide: Detect Visual Changes

Snapshot Archive automatically compares consecutive snapshots and generates visual diffs. Use the diffs API to find pages that have changed, filter by change magnitude, and alert on significant updates.

You need a monitor ID to query diffs. Get it from GET /v1/monitors (see Monitors) or by searching: GET /v1/monitors?url=example.com.

Find all significant changes this week

bash
# Get significant diffs from the past 7 days for monitor 42
curl "https://api.snapshotarchive.com/v1/monitors/42/diffs?created_after=2026-09-07&is_significant=true&sort=change_percent&order=desc" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

# Get only large changes (>10% pixel difference)
curl "https://api.snapshotarchive.com/v1/monitors/42/diffs?min_change_percent=10" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
python
from datetime import datetime, timedelta

# Check all monitors for significant changes this week
monitors = requests.get(f'{BASE}/monitors?status=active', headers=headers).json()['data']
week_ago = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')

for monitor in monitors:
    diffs = requests.get(
        f"{BASE}/monitors/{monitor['id']}/diffs",
        headers=headers,
        params={
            'created_after': week_ago,
            'is_significant': 'true',
            'sort': 'change_percent',
            'order': 'desc',
        }
    ).json()

    if diffs['meta']['total'] > 0:
        top_diff = diffs['data'][0]
        print(f"{monitor['url']}: {top_diff['change_percent']}% change on {top_diff['created_at']}")
javascript
// Check all monitors for significant changes this week
const weekAgo = new Date(Date.now() - 7 * 86400000).toISOString().split('T')[0];
const monitors = await fetch(`${BASE}/monitors?status=active`, { headers }).then(r => r.json());

for (const monitor of monitors.data) {
  const params = new URLSearchParams({
    created_after: weekAgo,
    is_significant: 'true',
    sort: 'change_percent',
    order: 'desc',
  });

  const diffs = await fetch(`${BASE}/monitors/${monitor.id}/diffs?${params}`, { headers })
    .then(r => r.json());

  if (diffs.meta.total > 0) {
    const top = diffs.data[0];
    console.log(`${monitor.url}: ${top.change_percent}% change on ${top.created_at}`);
  }
}
Tip: Use diff thresholds wisely Minor changes like timestamp updates or ad rotations often register as 0.1–2% change. For meaningful content changes, filter with min_change_percent=3 or use is_significant=true which respects your monitor's configured diff threshold.

Guide: Bulk Download Screenshots

Need to download all screenshots for a monitor within a date range? Paginate through the results and download each file. Here's how to automate it.

First, get your monitor ID from GET /v1/monitors or GET /v1/monitors?url=example.com. Then use it in the scripts below.

bash
#!/bin/bash
API_KEY="YOUR_API_KEY"
MONITOR_ID=42
BASE="https://api.snapshotarchive.com/v1"
OUTPUT_DIR="./screenshots"
mkdir -p "$OUTPUT_DIR"

PAGE=1
while true; do
  RESPONSE=$(curl -s "$BASE/monitors/$MONITOR_ID/snapshots?captured_after=2026-09-01&captured_before=2026-09-30&status=completed&per_page=20&page=$PAGE" \
    -H "Authorization: Bearer $API_KEY" \
    -H "Accept: application/json")

  # Extract snapshot IDs
  IDS=$(echo "$RESPONSE" | jq -r '.data[].id')
  [ -z "$IDS" ] && break

  for ID in $IDS; do
    DATE=$(echo "$RESPONSE" | jq -r ".data[] | select(.id==\"$ID\") | .captured_at[:10]")
    curl -sL "$BASE/snapshots/$ID/screenshot" \
      -H "Authorization: Bearer $API_KEY" \
      -o "$OUTPUT_DIR/$DATE-$ID.png"
    echo "Downloaded: $DATE-$ID.png"
  done

  LAST_PAGE=$(echo "$RESPONSE" | jq '.meta.last_page')
  [ "$PAGE" -ge "$LAST_PAGE" ] && break
  PAGE=$((PAGE + 1))
done

echo "Done! Downloaded to $OUTPUT_DIR"
python
import os, requests

API_KEY = 'YOUR_API_KEY'
BASE = 'https://api.snapshotarchive.com/v1'
headers = {'Authorization': f'Bearer {API_KEY}', 'Accept': 'application/json'}

def download_all_snapshots(monitor_id, date_from, date_to, output_dir='./screenshots'):
    os.makedirs(output_dir, exist_ok=True)
    page = 1

    while True:
        response = requests.get(
            f'{BASE}/monitors/{monitor_id}/snapshots',
            headers=headers,
            params={
                'captured_after': date_from,
                'captured_before': date_to,
                'status': 'completed',
                'per_page': 20,
                'page': page,
            }
        ).json()

        for snap in response['data']:
            # Get signed URL
            detail = requests.get(f"{BASE}/snapshots/{snap['id']}", headers=headers).json()['data']
            url = detail['files']['screenshot_url']
            if url:
                date = snap['captured_at'][:10]
                filename = f"{date}-{snap['id'][:8]}.png"
                img = requests.get(url)
                with open(os.path.join(output_dir, filename), 'wb') as f:
                    f.write(img.content)
                print(f"Downloaded: {filename}")

        if page >= response['meta']['last_page']:
            break
        page += 1

# Download all September 2026 screenshots for monitor 42
download_all_snapshots(42, '2026-09-01', '2026-09-30')
javascript
import { writeFile, mkdir } from 'fs/promises';

const API_KEY = 'YOUR_API_KEY';
const BASE = 'https://api.snapshotarchive.com/v1';
const headers = { 'Authorization': `Bearer ${API_KEY}`, 'Accept': 'application/json' };

async function downloadAllSnapshots(monitorId, dateFrom, dateTo, outputDir = './screenshots') {
  await mkdir(outputDir, { recursive: true });
  let page = 1;

  while (true) {
    const params = new URLSearchParams({
      captured_after: dateFrom, captured_before: dateTo,
      status: 'completed', per_page: '20', page: String(page),
    });

    const res = await fetch(`${BASE}/monitors/${monitorId}/snapshots?${params}`, { headers });
    const { data, meta } = await res.json();

    for (const snap of data) {
      const detail = await fetch(`${BASE}/snapshots/${snap.id}`, { headers }).then(r => r.json());
      const url = detail.data.files.screenshot_url;
      if (url) {
        const img = await fetch(url).then(r => r.arrayBuffer());
        const date = snap.captured_at.slice(0, 10);
        const filename = `${date}-${snap.id.slice(0, 8)}.png`;
        await writeFile(`${outputDir}/${filename}`, Buffer.from(img));
        console.log(`Downloaded: ${filename}`);
      }
    }

    if (page >= meta.last_page) break;
    page++;
  }
}

await downloadAllSnapshots(42, '2026-09-01', '2026-09-30');
Respect rate limits Each snapshot download counts as an API request. For large bulk downloads, add a short delay between requests or use the /v1/snapshots/{uuid}/package endpoint to download all files as a single ZIP.

Guide: Webhook Alerts

Snapshot Archive can send webhook notifications when visual changes are detected. Instead of polling the API, set up webhooks on your monitors to get instant alerts.

Configure webhooks

Set the alert_webhook_url field on a monitor to receive HTTP POST notifications when changes are detected.

bash
# Enable webhook alerts on a monitor
curl -X PATCH "https://api.snapshotarchive.com/v1/monitors/42" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "alert_enabled": true,
    "alert_mode": "instant",
    "alert_webhook_url": "https://your-server.com/webhooks/screenshot-change",
    "diff_enabled": true,
    "diff_threshold_percent": 5
  }'

Alert channels

In addition to custom webhooks, you can send alerts to:

FieldDescription
alert_emailEmail address for notifications
alert_webhook_urlCustom HTTP endpoint (POST)
alert_slack_webhook_urlSlack incoming webhook URL
alert_discord_webhook_urlDiscord webhook URL
alert_telegram_bot_tokenTelegram bot token (use with alert_telegram_chat_id)
Diff threshold Set diff_threshold_percent to control sensitivity. A value of 5 means only changes affecting 5% or more of the page will trigger an alert. Lower values detect more changes but may generate noise from ad rotations or timestamps.

Changelog

Recent API updates and improvements.

September 14, 2026

  • Filtering & Sorting — All list endpoints now support sort and order parameters.
  • Snapshot filters — Filter snapshots by date range (captured_after, captured_before) and status.
  • Monitor filters — Search monitors by url, name, filter by status and tag.
  • Diff filters — Filter diffs by date range, min_change_percent, max_change_percent, and is_significant.
  • Latest snapshot — New GET /v1/monitors/{id}/latest-snapshot endpoint for quick access to the most recent screenshot.
  • Tags API — New GET /v1/tags endpoint to list all tags with monitor counts.
  • Documentation — Added Filtering & Sorting reference, practical use-case guides, and this changelog.

July 2026

  • Initial API release with Projects, Monitors, Snapshots, Diffs, Account endpoints.
  • Authentication via Bearer API keys.
  • Rate limiting: 60 requests/minute per API key.