MENU navbar-image

Introduction

This documentation aims to provide you with all the information you need to integrate WebChange Detector into your workflow.

Our Quick Start Guide gives you a quick overview to help you with the integration.

If you need help with the integration or miss something here, don't hesitate to contact us at: support@webchangedetector.com. We are happy to help.

Quick Start Guide

WebChange Detector captures screenshots of your URLs and compares them across time to detect visual changes. This guide walks you through the basic setup, manual pre/post-update checks, and continuous monitoring — all using default settings. For more detailed customisations, refer to the per-endpoint docs below.

Core Concepts

Initial Setup

Get API Token

Create a free trial account at webchangedetector.com. After account activation, get the API token from the dashboard at API & WP Websites. Send it as a Bearer token (see Authentication above).

Create Group

POST /api/v2/groups

Create the container that will hold your URLs and their shared settings (schedule, threshold, CSS injection, basic auth, etc.). The response contains the Group id you'll need for the next steps.

Create URLs

POST /api/v2/urls

Register the URLs you want to monitor. URLs exist independently of Groups — the same URL can be referenced from multiple Groups.

Add URLs to Group

POST /api/v2/groups/{uuid}/add-urls

Assign your URLs to the Group. By default, checks for both desktop and mobile are enabled per URL.

POST /api/v2/webhooks

Webhooks notify your application when checks finish or changes are detected. Available events for the v2 API:

Event Fires when
batch_finished All Screenshots in a Batch are complete.
comparison_status_new A new Comparison has been created (one per detected change).
queue_status_done A single Screenshot finishes successfully.
queue_status_failed A single Screenshot fails terminally.

If you can't host a webhook listener (e.g. local development), polling is a valid alternative — see "Wait for screenshots to finish" below.

Manual Checks

Manual checks compare a "before" state of your site to an "after" state. Run them around any change you want to verify (plugin updates, deployments, content edits).

  1. Take pre-update screenshots

    POST /api/v2/screenshots/take with sc_type = "pre" and the group_ids you want to capture. The response contains a batch_id — keep it for the next steps.

  2. Wait for screenshots to finish

    Either wait for the batch_finished webhook, or poll GET /api/v2/queues?batch_id={batch_id}&status=open,processing until the response is empty.

  3. Install updates

    Once the pre-update screenshots are done, perform your updates / changes on the site.

  4. Take post-update screenshots

    Same as Step 1, but with sc_type = "post" and the same group_ids. The API matches each post-Screenshot with its pre-counterpart and creates Comparisons automatically.

  5. Wait for screenshots to finish

    Wait for batch_finished (or poll as in Step 2). For each Comparison whose pixel diff exceeds the threshold, you'll additionally receive a comparison_status_new webhook.

  6. Check the differences

    GET /api/v2/comparisons?batch_id={batch_id} — filter by the post Batch to scope the result to this run. Each Comparison includes a public link for visual review.

(7. Optional: Re-check after fixes)

If you've made corrections on your site, run Step 4 again with a new sc_type = "post" Batch. The new post-Screenshots are compared against the original pre-Screenshots, so you can verify your fixes without redoing pre-capture.

That's all. You can now review all differences and make corrections on your site if something is broken, or roll back the updates.

Monitoring

For continuous monitoring (e.g. detecting surprises during the day), create the Group with monitoring = true:

POST /api/v2/groups
{
    "name": "example.com — Monitoring",
    "monitoring": true,
    "interval_in_h": 24,
    "alert_emails": ["ops@example.com"]
}

All URLs added to this Group are then captured and compared on the configured interval. Use the same Webhooks as for Manual Checks — comparison_status_new is typically the most useful event here.

Webhooks Verification

All webhook requests are signed with a SHA256 HMAC. The signature is sent in the Signature HTTP header.

Retrieve your webhook secret from GET /api/v2/account.

// PHP example
$signature = hash_hmac('sha256', $payloadJson, $secret);
if (! hash_equals($signature, $_SERVER['HTTP_SIGNATURE'])) {
    http_response_code(401);
    exit;
}

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_TOKEN}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

You can retrieve your token on webchangedetector.com or via the plugin.

AI Feedback Rules

Manage rules for ignoring recurring false positives in AI verification results.

List AI Feedback Rules

requires authentication

Returns a paginated list of all AiFeedbackRule for the current user.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'scope' => 'url',
            'is_active' => '1',
            'website_id' => '21cf8800-f906-4fa3-b1db-ed3d2977354d',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/ai-feedback-rules?scope=url&is_active=1&website_id=21cf8800-f906-4fa3-b1db-ed3d2977354d" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules"
);

const params = {
    "scope": "url",
    "is_active": "1",
    "website_id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules'
params = {
  'scope': 'url',
  'is_active': '1',
  'website_id': '21cf8800-f906-4fa3-b1db-ed3d2977354d',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
            "scope": "url",
            "description": "Slider showing different slide",
            "region_context": {
                "bbox": {
                    "x": 0,
                    "y": 100,
                    "w": 1920,
                    "h": 300
                }
            },
            "is_active": true,
            "last_matched_at": null,
            "match_count": 0,
            "url_id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "group_id": null,
            "website_id": null,
            "created_at": "2026-02-21 13:37:00",
            "updated_at": "2026-02-21 13:37:00"
        }
    ]
}
 

Request   

GET api/v2/ai-feedback-rules

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

scope   string  optional    

Filter by scope. Example: url

Must be one of:
  • url
  • group
  • website
is_active   boolean  optional    

Filter by active status. Example: true

website_id   string  optional    

Filter by website UUID. Example: 21cf8800-f906-4fa3-b1db-ed3d2977354d

Create AI Feedback Rule

requires authentication

Creates a new AiFeedbackRule from a comparison region. The backend auto-resolves whether the group belongs to a website and sets the scope accordingly.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'visual',
            'comparison_id' => '21cf8800-f906-4fa3-b1db-ed3d2977354d',
            'region_id' => 0,
            'console_entry' => '[Vue warn]: Component is missing template or render function.',
            'scope' => 'url',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"visual\",
    \"comparison_id\": \"21cf8800-f906-4fa3-b1db-ed3d2977354d\",
    \"region_id\": 0,
    \"console_entry\": \"[Vue warn]: Component is missing template or render function.\",
    \"scope\": \"url\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "visual",
    "comparison_id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
    "region_id": 0,
    "console_entry": "[Vue warn]: Component is missing template or render function.",
    "scope": "url"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules'
payload = {
    "type": "visual",
    "comparison_id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
    "region_id": 0,
    "console_entry": "[Vue warn]: Component is missing template or render function.",
    "scope": "url"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
        "scope": "url",
        "description": "Slider showing different slide",
        "region_context": {
            "bbox": {
                "x": 0,
                "y": 100,
                "w": 1920,
                "h": 300
            }
        },
        "is_active": true,
        "last_matched_at": null,
        "match_count": 0,
        "url_id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "group_id": null,
        "website_id": null,
        "created_at": "2026-02-21 13:37:00",
        "updated_at": "2026-02-21 13:37:00"
    }
}
 

Request   

POST api/v2/ai-feedback-rules

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

type   string  optional    

Feedback target. visual (default) trains the AI on a specific image region of a Comparison. console trains it on a browser-console log entry to ignore. Example: visual

Must be one of:
  • visual
  • console
comparison_id   string     

UUID of the comparison containing the region. Example: 21cf8800-f906-4fa3-b1db-ed3d2977354d

region_id   integer     

The region ID within the AI verification result. Example: 0

console_entry   string  optional    

Verbatim console-log line to teach the AI to ignore. Required when type=console. Max 500 chars. This field is required when type is console. Must not be greater than 500 characters. Example: [Vue warn]: Component is missing template or render function.

scope   string     

Scope for the rule: "url" for this URL only, "group_or_website" for all URLs. Example: url

Must be one of:
  • url
  • group_or_website

Update AI Feedback Rule

requires authentication

Update the active status and/or scope of an AiFeedbackRule.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules/non';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'is_active' => false,
            'scope' => 'url',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules/non" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"is_active\": false,
    \"scope\": \"url\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules/non"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "is_active": false,
    "scope": "url"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules/non'
payload = {
    "is_active": false,
    "scope": "url"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
        "scope": "url",
        "description": "Slider showing different slide",
        "region_context": null,
        "is_active": false,
        "last_matched_at": null,
        "match_count": 0,
        "url_id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "group_id": null,
        "website_id": null,
        "created_at": "2026-02-21 13:37:00",
        "updated_at": "2026-02-21 13:37:00"
    }
}
 

Request   

PUT api/v2/ai-feedback-rules/{id}

PATCH api/v2/ai-feedback-rules/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ai feedback rule. Example: non

ai_feedback_rule   string     

The UUID of the AI Feedback Rule. Example: 21cf8800-f906-4fa3-b1db-ed3d2977354d

Body Parameters

is_active   boolean  optional    

Set the active status of the rule. Example: false

scope   string  optional    

Change the scope: "url" for this URL only, "group_or_website" for all URLs. Example: url

Must be one of:
  • url
  • group_or_website

Delete AI Feedback Rule

requires authentication

Permanently deletes an AiFeedbackRule.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules/molestias';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request DELETE \
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules/molestias" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/ai-feedback-rules/molestias"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/ai-feedback-rules/molestias'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Request   

DELETE api/v2/ai-feedback-rules/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the ai feedback rule. Example: molestias

ai_feedback_rule   string     

The UUID of the AI Feedback Rule. Example: 21cf8800-f906-4fa3-b1db-ed3d2977354d

Account

This object represents your account with WCD

Get Account

requires authentication

Retrieves your Account. The fields plan, plan_name, company and magic_login_secret only come for full accounts (not sub accounts).

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/account';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/account" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/account"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/account'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "041c6528-783f-4e7e-b831-1a424ffeb8f3",
        "name_first": "Hans",
        "name_last": "Hacker",
        "email": "mail@example.com",
        "webhook_secret": "zq3csqdst0txgo5qe9mkqsourrxo56wd",
        "is_subaccount": false,
        "account_level": 0,
        "can_manage_subaccounts": false,
        "can_create_subaccounts": false,
        "plan_label": null,
        "invitation_accepted": true,
        "plan_features": {
            "ai_verification": true,
            "browser_console": true
        },
        "checks_done": 6982,
        "checks_left": 3018,
        "checks_limit": 10000,
        "timezone": "UTC",
        "status": "active",
        "renewal_at": "2025-01-01 13:37:42",
        "plan": "agency",
        "plan_name": "Agency",
        "company": "ACME Ltd",
        "magic_login_secret": "WdKnLLcsDxpJdfmv"
    }
}
 

Request   

GET api/v2/account

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Update Account

requires authentication

Updates your Account by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/account';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name_first' => 'Jane',
            'name_last' => 'Doe',
            'company' => 'ACME LLC',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/account" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name_first\": \"Jane\",
    \"name_last\": \"Doe\",
    \"company\": \"ACME LLC\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/account"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name_first": "Jane",
    "name_last": "Doe",
    "company": "ACME LLC"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/account'
payload = {
    "name_first": "Jane",
    "name_last": "Doe",
    "company": "ACME LLC"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "041c6528-783f-4e7e-b831-1a424ffeb8f3",
        "name_first": "Jane",
        "name_last": "Doe",
        "email": "mail@example.com",
        "webhook_secret": "zq3csqdst0txgo5qe9mkqsourrxo56wd",
        "is_subaccount": false,
        "account_level": 0,
        "can_manage_subaccounts": false,
        "can_create_subaccounts": false,
        "plan_label": null,
        "invitation_accepted": true,
        "plan_features": {
            "ai_verification": true,
            "browser_console": true
        },
        "checks_done": 42,
        "checks_left": 1337,
        "checks_limit": 1379,
        "timezone": "UTC",
        "status": "active",
        "renewal_at": "2025-01-01 13:37:42",
        "plan": "agency",
        "plan_name": "Agency",
        "company": "ACME LLC",
        "magic_login_secret": "WdKnLLcsDxpJdfmv"
    }
}
 

Request   

PUT api/v2/account

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name_first   string  optional    

First Name Example: Jane

name_last   string  optional    

Last Name Example: Doe

company   string  optional    

Company Name Example: ACME LLC

Get Account Stats

requires authentication

Returns aggregated statistics for the authenticated account, computed across ALL groups and websites (not paginated).

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/account/stats';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/account/stats" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/account/stats"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/account/stats'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "estimated_monthly_checks": 4520,
        "monitoring": {
            "websites": 12,
            "urls": 84,
            "estimated_monthly_checks": 4520,
            "next_run_at": 1716900000,
            "next_runs": [
                {
                    "name": "example.com",
                    "domain": "example.com",
                    "next_run_at": 1716900000
                },
                {
                    "name": "shop.example",
                    "domain": "shop.example",
                    "next_run_at": 1716903600
                }
            ],
            "trigger_websites": 1
        },
        "manual": {
            "websites": 8,
            "urls": 56,
            "checks_per_run": 112
        },
        "auto_update": {
            "websites_enabled": 9,
            "missing_email": 1,
            "active_weekdays": [
                "Mon",
                "Wed",
                "Fri"
            ],
            "estimated_monthly_checks": 1947,
            "next_runs": [
                {
                    "name": "example.com",
                    "domain": "example.com",
                    "next_run_at": 1716900000
                },
                {
                    "name": "shop.example",
                    "domain": "shop.example",
                    "next_run_at": 1716986400
                }
            ]
        }
    }
}
 

Request   

GET api/v2/account/stats

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Reactivate Account

requires authentication

Re-activates an account that was paused due to inactivity, including all its subaccounts. Only the account owner can re-activate; cancelled accounts cannot be re-activated this way.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/account/reactivate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/account/reactivate" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/account/reactivate"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/account/reactivate'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "041c6528-783f-4e7e-b831-1a424ffeb8f3",
        "name_first": "Hans",
        "name_last": "Hacker",
        "email": "mail@example.com",
        "status": "active",
        "renewal_at": "2025-01-01 13:37:42"
    }
}
 

Request   

POST api/v2/account/reactivate

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Batch

This object represents Batches of Queues.

List Batches

requires authentication

Returns a list of all Batch. The Batches are sorted by creation date, with the most recent Batches appearing first.

source is one of manual, monitoring, auto_update. ai_summary is null for accounts without the ai-verification feature; ai_summary.overall_status is one of everything_ok, needs_attention. website_domains lists the unique domains of the websites linked to the batch's queue groups (empty when no website is linked).

trigger_context is set for trigger-based monitoring runs (source = monitoring, started e.g. by saving a post in WordPress) and null otherwise. It holds the trigger type, first_triggered_at and the checked pages; each page has its url and triggered_at, plus title, post_id and editor when the trigger reported them. type is post_save or manual_trigger ("run monitoring now"). A manual_trigger run also has scope (url or all) and editor when reported; with scope = all (the whole Group) pages is empty.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/batches';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'above_threshold' => '0',
            'from' => '2024-07-01',
            'to' => '2024-07-04',
            'status' => 'to_fix,false_positive',
            'queue_type' => 'post,comparison',
            'group_ids' => '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
            'urls' => '023c282c-6513-420a-a36b-a654312ab229',
            'websites' => '023c282c-6513-420a-a36b-a654312ab229',
            'source' => 'manual,auto_update',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/batches?above_threshold=&from=2024-07-01&to=2024-07-04&status=to_fix%2Cfalse_positive&queue_type=post%2Ccomparison&group_ids=023c282c-6513-420a-a36b-a654312ab229%2C023c282c-6513-420a-a36b-a654312ab230&urls=023c282c-6513-420a-a36b-a654312ab229&websites=023c282c-6513-420a-a36b-a654312ab229&source=manual%2Cauto_update" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/batches"
);

const params = {
    "above_threshold": "0",
    "from": "2024-07-01",
    "to": "2024-07-04",
    "status": "to_fix,false_positive",
    "queue_type": "post,comparison",
    "group_ids": "023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230",
    "urls": "023c282c-6513-420a-a36b-a654312ab229",
    "websites": "023c282c-6513-420a-a36b-a654312ab229",
    "source": "manual,auto_update",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/batches'
params = {
  'above_threshold': '0',
  'from': '2024-07-01',
  'to': '2024-07-04',
  'status': 'to_fix,false_positive',
  'queue_type': 'post,comparison',
  'group_ids': '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
  'urls': '023c282c-6513-420a-a36b-a654312ab229',
  'websites': '023c282c-6513-420a-a36b-a654312ab229',
  'source': 'manual,auto_update',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "0d5f8108-51f1-4961-939a-2d33c7145918",
            "name": "Monitoring",
            "source": "monitoring",
            "trigger_context": {
                "type": "post_save",
                "first_triggered_at": "2024-07-29T13:30:12+00:00",
                "pages": [
                    {
                        "url": "example.com/about",
                        "title": "About us",
                        "post_id": 42,
                        "editor": "Anna",
                        "triggered_at": "2024-07-29T13:31:05+00:00"
                    }
                ]
            },
            "group_names": [
                "example.com - Monitoring Checks"
            ],
            "website_domains": [
                "example.com"
            ],
            "sc_version": "2.0",
            "finished_at": "2024-07-29 13:37:42",
            "ai_summary": {
                "summary": "Pricing text updated and navigation menu restructured. New JS error on checkout may affect functionality.",
                "overall_status": "needs_attention",
                "processing_time_ms": 1234
            },
            "update_results": null,
            "comparisons_count": {
                "ok": 0,
                "new": 1,
                "false_positive": 0,
                "to_fix": 0,
                "above_threshold": 10
            },
            "queues_count": {
                "failed": 0
            },
            "browser_console_count": {
                "added": 1,
                "removed": 0,
                "mixed": 0,
                "unchanged": 0
            }
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/batches?page=1",
        "last": "http://api.webchangedetector.test/api/v2/batches?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/batches?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/batches",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/batches

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

above_threshold   boolean  optional    

Only show Batches where there is a change detected between 2 screenshots. Example: false

from   string  optional    

Start date for filter. Must be a valid date. Example: 2024-07-01

to   string  optional    

End date for filter, defaults to today. Must be a valid date. Example: 2024-07-04

status   string  optional    

Comma separated list of Comparison status to filter for. Example: to_fix,false_positive

queue_type   string  optional    

Comma separated list of Queue types to filter for. Example: post,comparison

group_ids   string  optional    

Comma separated list of Group IDs. Example: 023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230

urls   string  optional    

Comma separated list of URL IDs to filter for. Example: 023c282c-6513-420a-a36b-a654312ab229

websites   string  optional    

Comma separated list of Website IDs to filter for. Example: 023c282c-6513-420a-a36b-a654312ab229

source   string  optional    

Comma separated list of Batch sources to filter for (manual, monitoring, auto_update). Example: manual,auto_update

Get Batch

requires authentication

Retrieves a Batch object identfied by their ID.

source is one of manual, monitoring, auto_update. ai_summary is null for accounts without the ai-verification feature; ai_summary.overall_status is one of everything_ok, needs_attention. website_domains lists the unique domains of the websites linked to the batch's queue groups (empty when no website is linked). trigger_context is set for trigger-based monitoring runs and null otherwise (shape: see List Batches).

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "0d5f8108-51f1-4961-939a-2d33c7145918",
        "name": "Foobar",
        "source": "monitoring",
        "trigger_context": null,
        "group_names": [
            "example.com - Monitoring Checks"
        ],
        "website_domains": [
            "example.com"
        ],
        "sc_version": "2.0",
        "finished_at": "2024-07-29 13:37:42",
        "ai_summary": {
            "summary": "Pricing text updated and navigation menu restructured. New JS error on checkout may affect functionality.",
            "overall_status": "needs_attention",
            "processing_time_ms": 1234
        },
        "update_results": null,
        "comparisons_count": {
            "ok": 0,
            "new": 1,
            "false_positive": 0,
            "to_fix": 0,
            "above_threshold": 10
        },
        "queues_count": {
            "failed": 0
        },
        "browser_console_count": {
            "added": 1,
            "removed": 0,
            "mixed": 0,
            "unchanged": 0
        }
    }
}
 

Request   

GET api/v2/batches/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Batch. Example: 0d5f8108-51f1-4961-939a-2d33c7145918

Update Batch

requires authentication

Updates the specified Batch by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Foobar',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Foobar\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Foobar"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918'
payload = {
    "name": "Foobar"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "id": "0d5f8108-51f1-4961-939a-2d33c7145918",
    "name": "Foobar",
    "source": "monitoring",
    "trigger_context": null,
    "group_names": [
        "example.com - Monitoring Checks"
    ],
    "website_domains": [
        "example.com"
    ],
    "sc_version": "2.0",
    "finished_at": "2024-07-29 13:37:42",
    "ai_summary": {
        "summary": "Pricing text updated and navigation menu restructured. New JS error on checkout may affect functionality.",
        "overall_status": "needs_attention",
        "processing_time_ms": 1234
    },
    "update_results": null,
    "comparisons_count": {
        "ok": 0,
        "new": 1,
        "false_positive": 0,
        "to_fix": 0,
        "above_threshold": 10
    },
    "queues_count": {
        "failed": 0
    },
    "browser_console_count": {
        "added": 1,
        "removed": 0,
        "mixed": 0,
        "unchanged": 0
    }
}
 

Request   

PUT api/v2/batches/{id}

PATCH api/v2/batches/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Batch. Example: 0d5f8108-51f1-4961-939a-2d33c7145918

Body Parameters

name   string     

The name of the Batch. Example: Foobar

Store Batch Update Results

requires authentication

Stores the WordPress update results (core / plugins / themes) that were applied around this Batch. The whole blob is replaced on every call, so the endpoint is idempotent and safe to retry.

It is accepted in ANY batch state (also while the batch is still running) and for ANY source. Send it for an empty run too (updates.core = null, empty arrays, summary.total_attempted = 0, summary.status = completed).

For a batch with source = auto_update this is what enables the Auto Update Check result email: it is sent once the batch is finished AND the update results are stored, whichever of the two happens last.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918/update-results';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'timestamp' => 1756800000,
            'updates' => [
                'core' => [
                    'attempted' => false,
                    'success' => false,
                    'from_version' => 'ffedwkwaviftjjugsevy',
                    'to_version' => 'ngbutgcc',
                    'error' => 'onwobjtqxd',
                    'messages' => ['fxmfjvovmwwgkmvimwsrikts'],
                ],
                'plugins' => [
                    [
                        'slug' => 'dioytihgpxcueki',
                        'name' => 'w',
                        'from_version' => 'llevbfamjmfjgeajjecno',
                        'to_version' => 'tswjaujzraupjyzwjqccz',
                        'success' => false,
                        'error' => 'dafzfdvqazwrdxsczvjxppxaq',
                        'messages' => ['zdpdgymqmglrxcycatkpaa'],
                    ],
                ],
                'themes' => [
                    [
                        'slug' => 'ev',
                        'name' => 'xwczqwluaphhvhqc',
                        'from_version' => 'dudfnxlnvxfk',
                        'to_version' => 'qkadabvgwismyieqwbwf',
                        'success' => false,
                        'error' => 'ybatabyimucfpdisbhjrtpogs',
                        'messages' => ['ipvptwwwu'],
                    ],
                ],
            ],
            'summary' => ['total_attempted' => 40, 'successful' => 23, 'failed' => 8, 'status' => 'completed'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918/update-results" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"timestamp\": 1756800000,
    \"updates\": {
        \"core\": {
            \"attempted\": false,
            \"success\": false,
            \"from_version\": \"ffedwkwaviftjjugsevy\",
            \"to_version\": \"ngbutgcc\",
            \"error\": \"onwobjtqxd\",
            \"messages\": [
                \"fxmfjvovmwwgkmvimwsrikts\"
            ]
        },
        \"plugins\": [
            {
                \"slug\": \"dioytihgpxcueki\",
                \"name\": \"w\",
                \"from_version\": \"llevbfamjmfjgeajjecno\",
                \"to_version\": \"tswjaujzraupjyzwjqccz\",
                \"success\": false,
                \"error\": \"dafzfdvqazwrdxsczvjxppxaq\",
                \"messages\": [
                    \"zdpdgymqmglrxcycatkpaa\"
                ]
            }
        ],
        \"themes\": [
            {
                \"slug\": \"ev\",
                \"name\": \"xwczqwluaphhvhqc\",
                \"from_version\": \"dudfnxlnvxfk\",
                \"to_version\": \"qkadabvgwismyieqwbwf\",
                \"success\": false,
                \"error\": \"ybatabyimucfpdisbhjrtpogs\",
                \"messages\": [
                    \"ipvptwwwu\"
                ]
            }
        ]
    },
    \"summary\": {
        \"total_attempted\": 40,
        \"successful\": 23,
        \"failed\": 8,
        \"status\": \"completed\"
    }
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918/update-results"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "timestamp": 1756800000,
    "updates": {
        "core": {
            "attempted": false,
            "success": false,
            "from_version": "ffedwkwaviftjjugsevy",
            "to_version": "ngbutgcc",
            "error": "onwobjtqxd",
            "messages": [
                "fxmfjvovmwwgkmvimwsrikts"
            ]
        },
        "plugins": [
            {
                "slug": "dioytihgpxcueki",
                "name": "w",
                "from_version": "llevbfamjmfjgeajjecno",
                "to_version": "tswjaujzraupjyzwjqccz",
                "success": false,
                "error": "dafzfdvqazwrdxsczvjxppxaq",
                "messages": [
                    "zdpdgymqmglrxcycatkpaa"
                ]
            }
        ],
        "themes": [
            {
                "slug": "ev",
                "name": "xwczqwluaphhvhqc",
                "from_version": "dudfnxlnvxfk",
                "to_version": "qkadabvgwismyieqwbwf",
                "success": false,
                "error": "ybatabyimucfpdisbhjrtpogs",
                "messages": [
                    "ipvptwwwu"
                ]
            }
        ]
    },
    "summary": {
        "total_attempted": 40,
        "successful": 23,
        "failed": 8,
        "status": "completed"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/batches/0d5f8108-51f1-4961-939a-2d33c7145918/update-results'
payload = {
    "timestamp": 1756800000,
    "updates": {
        "core": {
            "attempted": false,
            "success": false,
            "from_version": "ffedwkwaviftjjugsevy",
            "to_version": "ngbutgcc",
            "error": "onwobjtqxd",
            "messages": [
                "fxmfjvovmwwgkmvimwsrikts"
            ]
        },
        "plugins": [
            {
                "slug": "dioytihgpxcueki",
                "name": "w",
                "from_version": "llevbfamjmfjgeajjecno",
                "to_version": "tswjaujzraupjyzwjqccz",
                "success": false,
                "error": "dafzfdvqazwrdxsczvjxppxaq",
                "messages": [
                    "zdpdgymqmglrxcycatkpaa"
                ]
            }
        ],
        "themes": [
            {
                "slug": "ev",
                "name": "xwczqwluaphhvhqc",
                "from_version": "dudfnxlnvxfk",
                "to_version": "qkadabvgwismyieqwbwf",
                "success": false,
                "error": "ybatabyimucfpdisbhjrtpogs",
                "messages": [
                    "ipvptwwwu"
                ]
            }
        ]
    },
    "summary": {
        "total_attempted": 40,
        "successful": 23,
        "failed": 8,
        "status": "completed"
    }
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "0d5f8108-51f1-4961-939a-2d33c7145918",
        "name": "Auto Update Checks",
        "source": "auto_update",
        "trigger_context": null,
        "group_names": [
            "example.com - On-Demand Checks"
        ],
        "website_domains": [
            "example.com"
        ],
        "sc_version": "2.0",
        "finished_at": "2024-07-29 13:37:42",
        "ai_summary": null,
        "update_results": {
            "timestamp": 1756800000,
            "updates": {
                "core": null,
                "plugins": [
                    {
                        "slug": "akismet",
                        "name": "Akismet Anti-Spam",
                        "from_version": "5.3",
                        "to_version": "5.3.1",
                        "success": true,
                        "error": null,
                        "messages": []
                    }
                ],
                "themes": []
            },
            "summary": {
                "total_attempted": 1,
                "successful": 1,
                "failed": 0,
                "status": "completed"
            }
        },
        "comparisons_count": {
            "ok": 0,
            "new": 1,
            "false_positive": 0,
            "to_fix": 0,
            "above_threshold": 10
        },
        "queues_count": {
            "failed": 0
        },
        "browser_console_count": {
            "added": 1,
            "removed": 0,
            "mixed": 0,
            "unchanged": 0
        }
    }
}
 

Request   

PUT api/v2/batches/{id}/update-results

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Batch. Example: 0d5f8108-51f1-4961-939a-2d33c7145918

Body Parameters

timestamp   integer     

Unix timestamp of the update run. Example: 1756800000

updates   object     

The performed updates (core, plugins, themes).

core   object  optional    

WordPress core update, or null when core was not updated.

attempted   boolean  optional    

Example: false

success   boolean  optional    

Example: false

from_version   string  optional    

Must not be greater than 50 characters. Example: ffedwkwaviftjjugsevy

to_version   string  optional    

Must not be greater than 50 characters. Example: ngbutgcc

error   string  optional    

Must not be greater than 500 characters. Example: onwobjtqxd

messages   string[]  optional    

Must not be greater than 300 characters.

plugins   object[]  optional    

Updated plugins. Each item: slug, name, from_version, to_version, success, error, messages. Must not have more than 300 items.

slug   string     

Must not be greater than 255 characters. Example: dioytihgpxcueki

name   string  optional    

Must not be greater than 255 characters. Example: w

from_version   string  optional    

Must not be greater than 50 characters. Example: llevbfamjmfjgeajjecno

to_version   string  optional    

Must not be greater than 50 characters. Example: tswjaujzraupjyzwjqccz

success   boolean     

Example: false

error   string  optional    

Must not be greater than 500 characters. Example: dafzfdvqazwrdxsczvjxppxaq

messages   string[]  optional    

Must not be greater than 300 characters.

themes   object[]  optional    

Updated themes. Same item shape as updates.plugins. Must not have more than 300 items.

slug   string     

Must not be greater than 255 characters. Example: ev

name   string  optional    

Must not be greater than 255 characters. Example: xwczqwluaphhvhqc

from_version   string  optional    

Must not be greater than 50 characters. Example: dudfnxlnvxfk

to_version   string  optional    

Must not be greater than 50 characters. Example: qkadabvgwismyieqwbwf

success   boolean     

Example: false

error   string  optional    

Must not be greater than 500 characters. Example: ybatabyimucfpdisbhjrtpogs

messages   string[]  optional    

Must not be greater than 300 characters.

summary   object     

Run summary (total_attempted, successful, failed, status).

total_attempted   integer     

Must be at least 0. Example: 40

successful   integer     

Must be at least 0. Example: 23

failed   integer     

Must be at least 0. Example: 8

status   string     

One of completed, completed_with_errors, failed. Example: completed

Must be one of:
  • completed
  • completed_with_errors
  • failed

Comparison

This object represents the comparison between 2 Screenshots and how they differ.

List Comparisons

requires authentication

Returns a list of all Comparison. The Comparisons are sorted by creation date, with the most recent Comparisons appearing first.

batch_source is one of manual, monitoring, auto_update. batch_trigger_type is post_save (a saved page started the monitoring run), manual_trigger ("run monitoring now") or null (scheduled monitoring and all non-monitoring runs). browser_console_* fields are null unless the account has the browser-console-comparisons feature. ai_regions, ai_verification_status, and ai_verification_result are null unless the account has the ai-verification feature; ai_verification_status is one of pending, verified, failed, skipped. A comparison whose before or after screenshot came back as a completely blank page is decided automatically, without AI analysis: it carries an ai_verification_result with the marker capture_issue (after_blank, before_blank or both_blank), a summary and alerts, and no regions. Only after_blank (the page went blank) is an alert: it sets status = new and alerts = 1. A page recovering (before_blank) or staying blank (both_blank) is recorded the same way but resolves to status = ok with alerts = 0, because there is no valid comparison to judge against a blank baseline.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/comparisons';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'above_threshold' => '0',
            'from' => '2024-07-01',
            'to' => '2024-07-04',
            'status' => 'to_fix,false_positive',
            'groups' => '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
            'batches' => '0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918',
            'token' => 'autem',
            'urls' => '023c282c-6513-420a-a36b-a654312ab229',
            'websites' => '023c282c-6513-420a-a36b-a654312ab229',
            'source' => 'manual,auto_update',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/comparisons?above_threshold=&from=2024-07-01&to=2024-07-04&status=to_fix%2Cfalse_positive&groups=023c282c-6513-420a-a36b-a654312ab229%2C023c282c-6513-420a-a36b-a654312ab230&batches=0d5f8108-51f1-4961-939a-2d33c7145918%2C1d5f8108-51f1-4961-939a-2d33c7145918&token=autem&urls=023c282c-6513-420a-a36b-a654312ab229&websites=023c282c-6513-420a-a36b-a654312ab229&source=manual%2Cauto_update" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/comparisons"
);

const params = {
    "above_threshold": "0",
    "from": "2024-07-01",
    "to": "2024-07-04",
    "status": "to_fix,false_positive",
    "groups": "023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230",
    "batches": "0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918",
    "token": "autem",
    "urls": "023c282c-6513-420a-a36b-a654312ab229",
    "websites": "023c282c-6513-420a-a36b-a654312ab229",
    "source": "manual,auto_update",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/comparisons'
params = {
  'above_threshold': '0',
  'from': '2024-07-01',
  'to': '2024-07-04',
  'status': 'to_fix,false_positive',
  'groups': '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
  'batches': '0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918',
  'token': 'autem',
  'urls': '023c282c-6513-420a-a36b-a654312ab229',
  'websites': '023c282c-6513-420a-a36b-a654312ab229',
  'source': 'manual,auto_update',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
            "screenshot_1": "958dce2c-8468-47d3-8bc2-7dc6bf0aadae",
            "screenshot_1_created_at": "2025-01-01 13:37:00",
            "screenshot_1_updated_at": "2025-01-01 13:37:01",
            "screenshot_1_link": "https://storage.webchangedetector.com/folder/screenshot1.png",
            "screenshot_2": "958dce2c-8468-47d3-8bc2-7dc6bf0aadaf",
            "screenshot_2_created_at": "2025-01-01 13:37:00",
            "screenshot_2_updated_at": "2025-01-01 13:37:01",
            "screenshot_2_link": "https://storage.webchangedetector.com/folder/screenshot2.png",
            "html_title": "Foobar",
            "device": "mobile",
            "monitoring": true,
            "group": "023c282c-6513-420a-a36b-a654312ab229",
            "group_name": "Barfoo",
            "queue": "8ea7b88d-b5c6-4fad-8e9d-1497a54d9266",
            "link": "https://storage.webchangedetector.com/folder/comparison.png",
            "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
            "batch_name": "Auto Update Checks",
            "batch_source": "monitoring",
            "batch_trigger_type": null,
            "difference_percent": 0.4,
            "threshold": 0.2,
            "status": "new",
            "public_link": "https://www.webchangedetector.com/show-change-detection?token=f00b4r",
            "token": "f00b4r",
            "url": "https://example.com",
            "url_id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "cms": "wordpress",
            "browser_console_added": null,
            "browser_console_removed": null,
            "browser_console_change": null,
            "ai_regions": [
                {
                    "id": 0,
                    "bbox": {
                        "x": 120,
                        "y": 340,
                        "w": 280,
                        "h": 64
                    },
                    "pixel_count": 1840
                }
            ],
            "ai_verification_status": "verified",
            "ai_verification_result": {
                "capture_issue": null,
                "summary": "Pricing text updated, may need review.",
                "processing_time_ms": 1234,
                "total_regions": 1,
                "all_good": 0,
                "not_sure": 0,
                "alerts": 1,
                "regions": [
                    {
                        "region_id": 0,
                        "category": "alert",
                        "description": "Pricing changed from $9 to $12",
                        "confidence": 0.92,
                        "reason": "Numeric price text changed in hero section",
                        "matched_feedback_rule": null
                    }
                ],
                "console_analysis": null
            },
            "created_at": "2025-01-01 13:37:00"
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/comparisons?page=1",
        "last": "http://api.webchangedetector.test/api/v2/comparisons?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/comparisons?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/comparisons",
        "per_page": 15,
        "to": 1,
        "total": 1,
        "above_threshold_count": 3
    }
}
 

Request   

GET api/v2/comparisons

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

above_threshold   boolean  optional    

Is a change detected between 2 screenshots. Example: false

from   string  optional    

Start date for filter. Must be a valid date. Example: 2024-07-01

to   string  optional    

End date for filter, defaults to today. Must be a valid date. Example: 2024-07-04

status   string  optional    

Comma separated list of status to filter for. Example: to_fix,false_positive

groups   string  optional    

Comma separated list of group IDs. Example: 023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230

batches   string  optional    

Batch IDs. Example: 0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918

token   string  optional    

Example: autem

urls   string  optional    

Comma separated list of URL UUIDs. Example: 023c282c-6513-420a-a36b-a654312ab229

websites   string  optional    

Comma separated list of Website UUIDs. Example: 023c282c-6513-420a-a36b-a654312ab229

source   string  optional    

Comma separated list of Batch sources to filter for (manual, monitoring, auto_update). Example: manual,auto_update

Get Comparison

requires authentication

Retrieves a Comparison object identfied by their ID.

batch_source is one of manual, monitoring, auto_update. batch_trigger_type is post_save (a saved page started the monitoring run), manual_trigger ("run monitoring now") or null (scheduled monitoring and all non-monitoring runs). browser_console_* fields are null unless the account has the browser-console-comparisons feature. ai_regions, ai_verification_status, and ai_verification_result are null unless the account has the ai-verification feature; ai_verification_status is one of pending, verified, failed, skipped. A comparison whose before or after screenshot came back as a completely blank page is decided automatically, without AI analysis: it carries an ai_verification_result with the marker capture_issue (after_blank, before_blank or both_blank), a summary and alerts, and no regions. Only after_blank (the page went blank) is an alert: it sets status = new and alerts = 1. A page recovering (before_blank) or staying blank (both_blank) is recorded the same way but resolves to status = ok with alerts = 0, because there is no valid comparison to judge against a blank baseline.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
        "screenshot_1": "958dce2c-8468-47d3-8bc2-7dc6bf0aadae",
        "screenshot_1_created_at": "2025-01-01 13:37:00",
        "screenshot_1_updated_at": "2025-01-01 13:37:01",
        "screenshot_1_link": "https://storage.webchangedetector.com/folder/screenshot1.png",
        "screenshot_2": "958dce2c-8468-47d3-8bc2-7dc6bf0aadaf",
        "screenshot_2_created_at": "2025-01-01 13:37:00",
        "screenshot_2_updated_at": "2025-01-01 13:37:01",
        "screenshot_2_link": "https://storage.webchangedetector.com/folder/screenshot2.png",
        "html_title": "Foobar",
        "device": "mobile",
        "monitoring": true,
        "group": "023c282c-6513-420a-a36b-a654312ab229",
        "group_name": "Barfoo",
        "queue": "8ea7b88d-b5c6-4fad-8e9d-1497a54d9266",
        "link": "https://storage.webchangedetector.com/folder/comparison.png",
        "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
        "batch_name": "Auto Update Checks",
        "batch_source": "monitoring",
        "batch_trigger_type": null,
        "difference_percent": 0.4,
        "threshold": 0.2,
        "status": "new",
        "public_link": "https://www.webchangedetector.com/show-change-detection?token=f00b4r",
        "token": "f00b4r",
        "url": "https://example.com",
        "url_id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "cms": "wordpress",
        "browser_console_added": null,
        "browser_console_removed": null,
        "browser_console_change": null,
        "ai_regions": [
            {
                "id": 0,
                "bbox": {
                    "x": 120,
                    "y": 340,
                    "w": 280,
                    "h": 64
                },
                "pixel_count": 1840
            }
        ],
        "ai_verification_status": "verified",
        "ai_verification_result": {
            "capture_issue": null,
            "summary": "Pricing text updated, may need review.",
            "processing_time_ms": 1234,
            "total_regions": 1,
            "all_good": 0,
            "not_sure": 0,
            "alerts": 1,
            "regions": [
                {
                    "region_id": 0,
                    "category": "alert",
                    "description": "Pricing changed from $9 to $12",
                    "confidence": 0.92,
                    "reason": "Numeric price text changed in hero section",
                    "matched_feedback_rule": null
                }
            ],
            "console_analysis": null
        },
        "created_at": "2025-01-01 13:37:00"
    }
}
 

Request   

GET api/v2/comparisons/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Comparison. Example: 21cf8800-f906-4fa3-b1db-ed3d2977354d

Update Comparison

requires authentication

Updates the specified Comparison by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

batch_source is one of manual, monitoring, auto_update. batch_trigger_type is post_save (a saved page started the monitoring run), manual_trigger ("run monitoring now") or null (scheduled monitoring and all non-monitoring runs). browser_console_* fields are null unless the account has the browser-console-comparisons feature. ai_regions, ai_verification_status, and ai_verification_result are null unless the account has the ai-verification feature; ai_verification_status is one of pending, verified, failed, skipped. A comparison whose before or after screenshot came back as a completely blank page is decided automatically, without AI analysis: it carries an ai_verification_result with the marker capture_issue (after_blank, before_blank or both_blank), a summary and alerts, and no regions. Only after_blank (the page went blank) is an alert: it sets status = new and alerts = 1. A page recovering (before_blank) or staying blank (both_blank) is recorded the same way but resolves to status = ok with alerts = 0, because there is no valid comparison to judge against a blank baseline.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'false_positive',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"false_positive\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "false_positive"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/comparisons/21cf8800-f906-4fa3-b1db-ed3d2977354d'
payload = {
    "status": "false_positive"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "id": "21cf8800-f906-4fa3-b1db-ed3d2977354d",
    "screenshot_1": "958dce2c-8468-47d3-8bc2-7dc6bf0aadae",
    "screenshot_1_created_at": "2025-01-01 13:37:00",
    "screenshot_1_updated_at": "2025-01-01 13:37:01",
    "screenshot_1_link": "https://storage.webchangedetector.com/folder/screenshot1.png",
    "screenshot_2": "958dce2c-8468-47d3-8bc2-7dc6bf0aadaf",
    "screenshot_2_created_at": "2025-01-01 13:37:00",
    "screenshot_2_updated_at": "2025-01-01 13:37:01",
    "screenshot_2_link": "https://storage.webchangedetector.com/folder/screenshot2.png",
    "html_title": "Foobar",
    "device": "mobile",
    "monitoring": true,
    "group": "023c282c-6513-420a-a36b-a654312ab229",
    "group_name": "Barfoo",
    "queue": "8ea7b88d-b5c6-4fad-8e9d-1497a54d9266",
    "link": "https://storage.webchangedetector.com/folder/comparison.png",
    "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
    "batch_name": "Auto Update Checks",
    "batch_source": "monitoring",
    "batch_trigger_type": null,
    "difference_percent": 0.4,
    "threshold": 0.2,
    "status": "new",
    "public_link": "https://www.webchangedetector.com/show-change-detection?token=f00b4r",
    "token": "f00b4r",
    "url": "https://example.com",
    "url_id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
    "cms": "wordpress",
    "browser_console_added": null,
    "browser_console_removed": null,
    "browser_console_change": null,
    "ai_regions": [
        {
            "id": 0,
            "bbox": {
                "x": 120,
                "y": 340,
                "w": 280,
                "h": 64
            },
            "pixel_count": 1840
        }
    ],
    "ai_verification_status": "verified",
    "ai_verification_result": {
        "capture_issue": null,
        "summary": "Pricing text updated, may need review.",
        "processing_time_ms": 1234,
        "total_regions": 1,
        "all_good": 0,
        "not_sure": 0,
        "alerts": 1,
        "regions": [
            {
                "region_id": 0,
                "category": "alert",
                "description": "Pricing changed from $9 to $12",
                "confidence": 0.92,
                "reason": "Numeric price text changed in hero section",
                "matched_feedback_rule": null
            }
        ],
        "console_analysis": null
    },
    "created_at": "2025-01-01 13:37:00"
}
 

Request   

PUT api/v2/comparisons/{id}

PATCH api/v2/comparisons/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Comparison. Example: 21cf8800-f906-4fa3-b1db-ed3d2977354d

Body Parameters

status   string     

Mark the comparison as a false positive, as fixed or simply ok. Example: false_positive

Must be one of:
  • false_positive
  • ok
  • to_fix

Group

This object groups together a list of URLs and group wide settings for them.

List Groups

requires authentication

Returns a list of all Groups. The Groups are sorted by creation date, with the most recent Groups appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '15',
            'monitoring' => '1',
            'ids[0]' => 'autem',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/groups?per_page=15&monitoring=1&ids[]=autem" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups"
);

const params = {
    "per_page": "15",
    "monitoring": "1",
    "ids[0]": "autem",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups'
params = {
  'per_page': '15',
  'monitoring': '1',
  'ids[0]': 'autem',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "023c282c-6513-420a-a36b-a654312ab229",
            "name": "Example Group",
            "monitoring": true,
            "enabled": true,
            "hour_of_day": 0,
            "interval_in_h": 24,
            "alert_emails": "hello@example.com,test@foobar.com",
            "css": null,
            "js": null,
            "cms": "wordpress",
            "threshold": 0.2,
            "urls_count": 42,
            "selected_urls_count": 37,
            "basic_auth_user": null,
            "has_basic_auth": false,
            "proxy_type": "none",
            "proxy_country": "us"
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/groups?page=1",
        "last": "http://api.webchangedetector.test/api/v2/groups?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/groups?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/groups",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/groups

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

per_page   integer  optional    

Maximum number of Groups per page. Example: 15

monitoring   boolean  optional    

Filter to monitoring (true) or manual-checks (false) groups only. Omit to return both. Example: true

ids   string[]  optional    

Create Group

requires authentication

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Example Group',
            'monitoring' => true,
            'enabled' => true,
            'hour_of_day' => 0,
            'interval_in_h' => 24,
            'schedule_type' => 'interval',
            'schedule_days' => [1, 3, 5],
            'quiet_hours_start' => 22,
            'quiet_hours_end' => 6,
            'alert_emails' => ['hello@example.com', 'mail@example.com'],
            'cms' => 'wordpress',
            'basic_auth_user' => 'admin',
            'basic_auth_password' => 'secret123',
            'proxy_type' => 'none',
            'proxy_country' => 'enim',
            'screenshot_region' => 'auto',
            'screenshot_delay' => 10,
            'default_desktop' => false,
            'default_mobile' => false,
            'triggers' => [
                ['type' => 'post_save', 'scope' => 'url'],
            ],
            'css' => '.btn {visibility: none;}',
            'js' => '',
            'threshold' => 0.4,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/groups" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Example Group\",
    \"monitoring\": true,
    \"enabled\": true,
    \"hour_of_day\": 0,
    \"interval_in_h\": 24,
    \"schedule_type\": \"interval\",
    \"schedule_days\": [
        1,
        3,
        5
    ],
    \"quiet_hours_start\": 22,
    \"quiet_hours_end\": 6,
    \"alert_emails\": [
        \"hello@example.com\",
        \"mail@example.com\"
    ],
    \"cms\": \"wordpress\",
    \"basic_auth_user\": \"admin\",
    \"basic_auth_password\": \"secret123\",
    \"proxy_type\": \"none\",
    \"proxy_country\": \"enim\",
    \"screenshot_region\": \"auto\",
    \"screenshot_delay\": 10,
    \"default_desktop\": false,
    \"default_mobile\": false,
    \"triggers\": [
        {
            \"type\": \"post_save\",
            \"scope\": \"url\"
        }
    ],
    \"css\": \".btn {visibility: none;}\",
    \"js\": \"\",
    \"threshold\": 0.4
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Example Group",
    "monitoring": true,
    "enabled": true,
    "hour_of_day": 0,
    "interval_in_h": 24,
    "schedule_type": "interval",
    "schedule_days": [
        1,
        3,
        5
    ],
    "quiet_hours_start": 22,
    "quiet_hours_end": 6,
    "alert_emails": [
        "hello@example.com",
        "mail@example.com"
    ],
    "cms": "wordpress",
    "basic_auth_user": "admin",
    "basic_auth_password": "secret123",
    "proxy_type": "none",
    "proxy_country": "enim",
    "screenshot_region": "auto",
    "screenshot_delay": 10,
    "default_desktop": false,
    "default_mobile": false,
    "triggers": [
        {
            "type": "post_save",
            "scope": "url"
        }
    ],
    "css": ".btn {visibility: none;}",
    "js": "",
    "threshold": 0.4
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups'
payload = {
    "name": "Example Group",
    "monitoring": true,
    "enabled": true,
    "hour_of_day": 0,
    "interval_in_h": 24,
    "schedule_type": "interval",
    "schedule_days": [
        1,
        3,
        5
    ],
    "quiet_hours_start": 22,
    "quiet_hours_end": 6,
    "alert_emails": [
        "hello@example.com",
        "mail@example.com"
    ],
    "cms": "wordpress",
    "basic_auth_user": "admin",
    "basic_auth_password": "secret123",
    "proxy_type": "none",
    "proxy_country": "enim",
    "screenshot_region": "auto",
    "screenshot_delay": 10,
    "default_desktop": false,
    "default_mobile": false,
    "triggers": [
        {
            "type": "post_save",
            "scope": "url"
        }
    ],
    "css": ".btn {visibility: none;}",
    "js": "",
    "threshold": 0.4
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "023c282c-6513-420a-a36b-a654312ab229",
        "name": "Example Group",
        "monitoring": true,
        "enabled": true,
        "hour_of_day": 0,
        "interval_in_h": 24,
        "schedule_type": "interval",
        "schedule_days": null,
        "quiet_hours_start": null,
        "quiet_hours_end": null,
        "triggers": [],
        "alert_emails": "hello@example.com,test@foobar.com",
        "css": ".btn {visibility: none;}",
        "js": "",
        "cms": "wordpress",
        "threshold": 0.2,
        "urls_count": 42,
        "selected_urls_count": 37,
        "basic_auth_user": null,
        "has_basic_auth": false,
        "proxy_type": "none",
        "proxy_country": "us"
    }
}
 

Request   

POST api/v2/groups

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Name of the Group. Example: Example Group

monitoring   boolean  optional    

Defaults to false. Example: true

Must be one of:
  • true
  • false
enabled   boolean  optional    

Defaults to true. Example: true

Must be one of:
  • true
  • false
hour_of_day   integer  optional    

Which hour of the day are the auto updates executed. Defaults to 0 Example: 0

interval_in_h   number  optional    

One of the following intervals of the hour. Defaults to 24 Example: 24

Must be one of:
  • 0.25
  • 0.5
  • 3
  • 6
  • 12
  • 24
schedule_type   string  optional    

Schedule type: interval (default), weekly, monthly, or off (no scheduled runs, only triggers) Example: interval

Must be one of:
  • interval
  • weekly
  • monthly
  • off
schedule_days   string[]  optional    

Days for weekly (1=Mon..7=Sun) or monthly (1-30 or "last") schedules

quiet_hours_start   integer  optional    

Hour (0-23) when quiet period starts. No checks during quiet hours. Example: 22

quiet_hours_end   integer  optional    

Hour (0-23) when quiet period ends Example: 6

alert_emails   string[]  optional    

A list of emails to notify. Defaults to the account email.

cms   string  optional    

Optional CMS hint used by the screenshot pipeline (e.g. wordpress). Allowed values: see config/enums.cms. Example: wordpress

Must be one of:
  • wordpress
basic_auth_user   string  optional    

Username for HTTP Basic Authentication Example: admin

basic_auth_password   string  optional    

Password for HTTP Basic Authentication Example: secret123

proxy_type   string  optional    

Proxy type to use for screenshots Example: none

Must be one of:
  • none
  • static
  • residential
proxy_country   string  optional    

Location code for residential proxy (2-letter). Defaults to the screenshot region when unset; reserved for future country-specific proxies. Example: enim

screenshot_region   string  optional    

Region this group's screenshots are taken from. auto (default) auto-geolocates the target domain to the nearest region, then stores the resolved concrete value back in this field. us/eu pin the region explicitly (eu renders from an EU IP with lower latency for EU sites). Reading this field returns auto transiently right after create, then the resolved us/eu. Allowed values: see config/enums.screenshot_region. Example: auto

Must be one of:
  • us
  • eu
  • auto
screenshot_delay   integer  optional    

Minimum seconds between consecutive screenshot dispatches for this group, per user (7-60). Higher values reduce load on the screenshoter and target site, useful for groups with many URLs. Must be at least 7. Must not be greater than 60. Example: 10

default_desktop   boolean  optional    

Activate newly synced URLs for desktop screenshots by default in this group. Applied only to URLs the next sync first assigns; existing assignments keep their flags. Example: false

default_mobile   boolean  optional    

Activate newly synced URLs for mobile screenshots by default in this group. Applied only to URLs the next sync first assigns; existing assignments keep their flags. Example: false

triggers   object[]  optional    

Events that start a monitoring check in addition to the schedule, e.g. saving a post in WordPress. Objects with type (post_save) and optional scope (url). Monitoring groups only.

type   string     

Example: post_save

Must be one of:
  • post_save
scope   string  optional    

What the trigger checks: url = only the reported URLs. Defaults to url. Example: url

Must be one of:
  • url
css   string  optional    

CSS to be injected before the screenshot is taken for all URLs in this group Example: .btn {visibility: none;}

js   string  optional    

JavaScript to be injected before the screenshot is taken for all URLs in this group

threshold   numeric  optional    

Difference in percent of when this counts as a change detection for all URLs in this group Example: 0.4

Get Group

requires authentication

Retrieves a Group object identfied by their ID

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "023c282c-6513-420a-a36b-a654312ab229",
        "name": "Example Group",
        "monitoring": true,
        "enabled": true,
        "hour_of_day": 0,
        "interval_in_h": 24,
        "alert_emails": "hello@example.com,test@foobar.com",
        "css": null,
        "js": null,
        "cms": "wordpress",
        "threshold": 0.2,
        "urls_count": 42,
        "selected_urls_count": 37,
        "basic_auth_user": null,
        "has_basic_auth": false,
        "proxy_type": "none",
        "proxy_country": "us"
    }
}
 

Request   

GET api/v2/groups/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Update Group

requires authentication

Updates the specified Group by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Example Group',
            'monitoring' => true,
            'enabled' => true,
            'hour_of_day' => 0,
            'interval_in_h' => 24,
            'schedule_type' => 'interval',
            'schedule_days' => [1, 3, 5],
            'quiet_hours_start' => 22,
            'quiet_hours_end' => 6,
            'alert_emails' => ['hello@example.com', 'mail@example.com'],
            'css' => '.btn {visibility: none;}',
            'js' => '',
            'threshold' => 0.4,
            'basic_auth_user' => 'admin',
            'basic_auth_password' => 'secret123',
            'proxy_type' => 'none',
            'proxy_country' => 'facilis',
            'screenshot_region' => 'auto',
            'screenshot_delay' => 10,
            'default_desktop' => false,
            'default_mobile' => false,
            'triggers' => [
                ['type' => 'post_save', 'scope' => 'url'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Example Group\",
    \"monitoring\": true,
    \"enabled\": true,
    \"hour_of_day\": 0,
    \"interval_in_h\": 24,
    \"schedule_type\": \"interval\",
    \"schedule_days\": [
        1,
        3,
        5
    ],
    \"quiet_hours_start\": 22,
    \"quiet_hours_end\": 6,
    \"alert_emails\": [
        \"hello@example.com\",
        \"mail@example.com\"
    ],
    \"css\": \".btn {visibility: none;}\",
    \"js\": \"\",
    \"threshold\": 0.4,
    \"basic_auth_user\": \"admin\",
    \"basic_auth_password\": \"secret123\",
    \"proxy_type\": \"none\",
    \"proxy_country\": \"facilis\",
    \"screenshot_region\": \"auto\",
    \"screenshot_delay\": 10,
    \"default_desktop\": false,
    \"default_mobile\": false,
    \"triggers\": [
        {
            \"type\": \"post_save\",
            \"scope\": \"url\"
        }
    ]
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Example Group",
    "monitoring": true,
    "enabled": true,
    "hour_of_day": 0,
    "interval_in_h": 24,
    "schedule_type": "interval",
    "schedule_days": [
        1,
        3,
        5
    ],
    "quiet_hours_start": 22,
    "quiet_hours_end": 6,
    "alert_emails": [
        "hello@example.com",
        "mail@example.com"
    ],
    "css": ".btn {visibility: none;}",
    "js": "",
    "threshold": 0.4,
    "basic_auth_user": "admin",
    "basic_auth_password": "secret123",
    "proxy_type": "none",
    "proxy_country": "facilis",
    "screenshot_region": "auto",
    "screenshot_delay": 10,
    "default_desktop": false,
    "default_mobile": false,
    "triggers": [
        {
            "type": "post_save",
            "scope": "url"
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229'
payload = {
    "name": "Example Group",
    "monitoring": true,
    "enabled": true,
    "hour_of_day": 0,
    "interval_in_h": 24,
    "schedule_type": "interval",
    "schedule_days": [
        1,
        3,
        5
    ],
    "quiet_hours_start": 22,
    "quiet_hours_end": 6,
    "alert_emails": [
        "hello@example.com",
        "mail@example.com"
    ],
    "css": ".btn {visibility: none;}",
    "js": "",
    "threshold": 0.4,
    "basic_auth_user": "admin",
    "basic_auth_password": "secret123",
    "proxy_type": "none",
    "proxy_country": "facilis",
    "screenshot_region": "auto",
    "screenshot_delay": 10,
    "default_desktop": false,
    "default_mobile": false,
    "triggers": [
        {
            "type": "post_save",
            "scope": "url"
        }
    ]
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "023c282c-6513-420a-a36b-a654312ab229",
        "name": "Example Group",
        "monitoring": true,
        "enabled": true,
        "hour_of_day": 0,
        "interval_in_h": 24,
        "schedule_type": "interval",
        "schedule_days": null,
        "quiet_hours_start": null,
        "quiet_hours_end": null,
        "triggers": [],
        "alert_emails": "hello@example.com,test@foobar.com",
        "css": ".btn {visibility: none;}",
        "js": "",
        "cms": "wordpress",
        "threshold": 0.2,
        "urls_count": 42,
        "selected_urls_count": 37,
        "basic_auth_user": "admin",
        "has_basic_auth": true,
        "proxy_type": "static",
        "proxy_country": "us"
    }
}
 

Request   

PUT api/v2/groups/{id}

PATCH api/v2/groups/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Body Parameters

name   string  optional    

Name of the Group. Example: Example Group

monitoring   boolean  optional    

Defaults to false. Example: true

Must be one of:
  • true
  • false
enabled   boolean  optional    

Defaults to true. Example: true

Must be one of:
  • true
  • false
hour_of_day   integer  optional    

Which hour of the day are the auto updates executed. Defaults to 0 Example: 0

interval_in_h   number  optional    

One of the following intervals of the hour. Defaults to 24 Example: 24

Must be one of:
  • 0.25
  • 0.5
  • 3
  • 6
  • 12
  • 24
schedule_type   string  optional    

Schedule type: interval (default), weekly, monthly, or off (no scheduled runs, only triggers) Example: interval

Must be one of:
  • interval
  • weekly
  • monthly
  • off
schedule_days   string[]  optional    

Days for weekly (1=Mon..7=Sun) or monthly (1-30 or "last") schedules

quiet_hours_start   integer  optional    

Hour (0-23) when quiet period starts. No checks during quiet hours. Example: 22

quiet_hours_end   integer  optional    

Hour (0-23) when quiet period ends Example: 6

alert_emails   string[]  optional    

A list of emails to notify. Defaults to the account email.

css   string  optional    

CSS to be injected before the screenshot is taken for all URLs in this group Example: .btn {visibility: none;}

js   string  optional    

JavaScript to be injected before the screenshot is taken for all URLs in this group

threshold   numeric  optional    

Difference in percent of when this counts as a change detection for all URLs in this group Example: 0.4

basic_auth_user   string  optional    

Username for HTTP Basic Authentication Example: admin

basic_auth_password   string  optional    

Password for HTTP Basic Authentication Example: secret123

proxy_type   string  optional    

Proxy type to use for screenshots Example: none

Must be one of:
  • none
  • static
  • residential
proxy_country   string  optional    

Location code for residential proxy (2-letter). Defaults to the screenshot region when unset; reserved for future country-specific proxies. Example: facilis

screenshot_region   string  optional    

Region this group's screenshots are taken from. auto (default) auto-geolocates the target domain to the nearest region, then stores the resolved concrete value back in this field. us/eu pin the region explicitly (eu renders from an EU IP with lower latency for EU sites). Setting auto re-triggers detection; the API mirrors any region change onto the website's sibling detection group automatically. Allowed values: see config/enums.screenshot_region. Example: auto

Must be one of:
  • us
  • eu
  • auto
screenshot_delay   integer  optional    

Minimum seconds between consecutive screenshot dispatches for this group, per user (7-60). Higher values reduce load on the screenshoter and target site, useful for groups with many URLs. Must be at least 7. Must not be greater than 60. Example: 10

default_desktop   boolean  optional    

Activate newly synced URLs for desktop screenshots by default in this group. Applied only to URLs the next sync first assigns; existing assignments keep their flags. Example: false

default_mobile   boolean  optional    

Activate newly synced URLs for mobile screenshots by default in this group. Applied only to URLs the next sync first assigns; existing assignments keep their flags. Example: false

triggers   object[]  optional    

Events that start a monitoring check in addition to the schedule, e.g. saving a post in WordPress. Objects with type (post_save) and optional scope (url). Monitoring groups only.

type   string     

Example: post_save

Must be one of:
  • post_save
scope   string  optional    

What the trigger checks: url = only the reported URLs. Defaults to url. Example: url

Must be one of:
  • url

Delete Group

requires authentication

Permanently deletes a Group. This cannot be undone.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request DELETE \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "message": "{ID} deleted"
}
 

Request   

DELETE api/v2/groups/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Add Urls To Group

requires authentication

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/add-urls';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'urls' => [
                ['id' => '418e0748-a9cf-480b-86d6-88b00bac00b9', 'desktop' => true],
                ['id' => '418e0748-a9cf-480b-86d6-88b00bac00c1', 'mobile' => false],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/add-urls" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"urls\": [
        {
            \"id\": \"418e0748-a9cf-480b-86d6-88b00bac00b9\",
            \"desktop\": true
        },
        {
            \"id\": \"418e0748-a9cf-480b-86d6-88b00bac00c1\",
            \"mobile\": false
        }
    ]
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/add-urls"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "urls": [
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "desktop": true
        },
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00c1",
            "mobile": false
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/add-urls'
payload = {
    "urls": [
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "desktop": true
        },
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00c1",
            "mobile": false
        }
    ]
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "count": "{Amount of URLs added}"
}
 

Request   

POST api/v2/groups/{id}/add-urls

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Body Parameters

urls   string[]     

Array of URLs with their settings in this Group

id   string     

UUID of an existing URL (registered via POST /api/v2/urls). Must be a valid UUID. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

desktop   boolean  optional    

Capture desktop screenshots for this URL in the Group. Example: true

mobile   boolean  optional    

Capture mobile screenshots for this URL in the Group. Example: true

css   string  optional    

URL-level CSS overrides applied on top of the Group CSS before screenshotting.

js   string  optional    

URL-level JavaScript overrides applied on top of the Group JS before screenshotting.

Remove Urls From Group

requires authentication

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/remove-urls';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'urls' => ['418e0748-a9cf-480b-86d6-88b00bac00b9', '418e0748-a9cf-480b-86d6-88b00bac00c1'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/remove-urls" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"urls\": [
        \"418e0748-a9cf-480b-86d6-88b00bac00b9\",
        \"418e0748-a9cf-480b-86d6-88b00bac00c1\"
    ]
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/remove-urls"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "urls": [
        "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "418e0748-a9cf-480b-86d6-88b00bac00c1"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/remove-urls'
payload = {
    "urls": [
        "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "418e0748-a9cf-480b-86d6-88b00bac00c1"
    ]
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "count": "{Amount of URLs removed}"
}
 

Request   

POST api/v2/groups/{id}/remove-urls

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Body Parameters

urls   string[]     

Array of URL IDs

List Urls For Group

requires authentication

Returns a list of all Urls. The Urls are sorted by creation date, with the most recent Urls appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '25',
            'type' => 'posts',
            'category' => 'types',
            'search' => 'about',
            'url_ids' => '91e9c9fd-b86f-4269-a3f4-f02810167a6d,b3a9d2e5-2c64-4a16-8f1c-9e3b21f8a4e7',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls?per_page=25&type=posts&category=types&search=about&url_ids=91e9c9fd-b86f-4269-a3f4-f02810167a6d%2Cb3a9d2e5-2c64-4a16-8f1c-9e3b21f8a4e7" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls"
);

const params = {
    "per_page": "25",
    "type": "posts",
    "category": "types",
    "search": "about",
    "url_ids": "91e9c9fd-b86f-4269-a3f4-f02810167a6d,b3a9d2e5-2c64-4a16-8f1c-9e3b21f8a4e7",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls'
params = {
  'per_page': '25',
  'type': 'posts',
  'category': 'types',
  'search': 'about',
  'url_ids': '91e9c9fd-b86f-4269-a3f4-f02810167a6d,b3a9d2e5-2c64-4a16-8f1c-9e3b21f8a4e7',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "html_title": "Example Title",
            "url": "example.com/foobar",
            "last_crawled_at": "2025-01-01 13:37:42",
            "desktop": true,
            "mobile": true,
            "css": null,
            "js": null,
            "threshold": null,
            "group": "023c282c-6513-420a-a36b-a654312ab229"
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls?page=1",
        "last": "http://api.webchangedetector.test/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls",
        "per_page": 15,
        "to": 1,
        "total": 1,
        "selected_urls_count": 1
    }
}
 

Request   

GET api/v2/groups/{id}/urls

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

type   string     

URL Type. Example: foobar

category   string     

Category. Example: barfoo

search   string     

Search in html_title and url. Example: loremipsum

url_ids   string     

Comma separated list of URL UUIDs Example: eos

Query Parameters

per_page   integer  optional    

Maximum number of URLs per page. Example: 25

type   string  optional    

Filter by post-type slug (e.g. posts, pages, products). Example: posts

category   string  optional    

Filter by URL category (e.g. types, taxonomies, frontpage). Example: types

search   string  optional    

Substring match against the URL or its title. Example: about

url_ids   string  optional    

Comma-separated list of URL UUIDs to restrict the result to. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d,b3a9d2e5-2c64-4a16-8f1c-9e3b21f8a4e7

Select All Urls In Group

requires authentication

Enables or disables one viewport (desktop or mobile) for ALL URLs of a group in a single operation. Use this instead of sending thousands of individual URL ids when toggling a whole column ("select all" / "deselect all"). Only the given device column changes; the other viewport is left untouched.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/select-all';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'device' => 'desktop',
            'enabled' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/select-all" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"device\": \"desktop\",
    \"enabled\": true
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/select-all"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "device": "desktop",
    "enabled": true
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/select-all'
payload = {
    "device": "desktop",
    "enabled": true
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "message": "All desktop URLs in Group 023c282c-6513-420a-a36b-a654312ab229 updated",
    "selected_urls_count": 42,
    "selected_checks_count": 80
}
 

Request   

PUT api/v2/groups/{group_id}/urls/select-all

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

group_id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Body Parameters

device   string     

Which viewport to toggle for every URL. Example: desktop

Must be one of:
  • desktop
  • mobile
enabled   boolean     

Whether screenshots of that viewport should be taken. Example: true

Must be one of:
  • true
  • false

Update Url In Group

requires authentication

Updates a URL within a group.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/418e0748-a9cf-480b-86d6-88b00bac00b9';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'desktop' => true,
            'mobile' => true,
            'css' => '.btn {visibility: none;}',
            'js' => 'illum',
            'threshold' => 0.4,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/418e0748-a9cf-480b-86d6-88b00bac00b9" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"desktop\": true,
    \"mobile\": true,
    \"css\": \".btn {visibility: none;}\",
    \"js\": \"illum\",
    \"threshold\": 0.4
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/418e0748-a9cf-480b-86d6-88b00bac00b9"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "desktop": true,
    "mobile": true,
    "css": ".btn {visibility: none;}",
    "js": "illum",
    "threshold": 0.4
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls/418e0748-a9cf-480b-86d6-88b00bac00b9'
payload = {
    "desktop": true,
    "mobile": true,
    "css": ".btn {visibility: none;}",
    "js": "illum",
    "threshold": 0.4
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "html_title": "Example Title",
        "url": "example.com/foobar",
        "last_crawled_at": "2025-01-01 13:37:42",
        "desktop": true,
        "mobile": true,
        "css": ".btn {visibility: none;}",
        "js": null,
        "threshold": null,
        "group": "023c282c-6513-420a-a36b-a654312ab229"
    },
    "meta": {
        "selected_urls_count": 1
    }
}
 

Request   

PUT api/v2/groups/{group_id}/urls/{url_id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

group_id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

url_id   string     

The ID of the URL in that Group. Example: 418e0748-a9cf-480b-86d6-88b00bac00b9

Body Parameters

desktop   boolean  optional    

Should a desktop screenshot be taken Example: true

Must be one of:
  • true
  • false
mobile   boolean  optional    

Should a mobile screenshot be taken Example: true

Must be one of:
  • true
  • false
css   string  optional    

CSS to be injected before the screenshot is taken Example: .btn {visibility: none;}

js   string  optional    

JavaScript to be injected before the screenshot is taken Example: illum

threshold   numeric  optional    

Difference in percent of when this counts as a change detection Example: 0.4

Update All Urls In Group

requires authentication

Updates all URLs within a group.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'urls' => [
                ['id' => '418e0748-a9cf-480b-86d6-88b00bac00b9', 'desktop' => true, 'mobile' => true, 'css' => null, 'js' => null, 'threshold' => 0.4],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"urls\": [
        {
            \"id\": \"418e0748-a9cf-480b-86d6-88b00bac00b9\",
            \"desktop\": true,
            \"mobile\": true,
            \"css\": null,
            \"js\": null,
            \"threshold\": 0.4
        }
    ]
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "urls": [
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "desktop": true,
            "mobile": true,
            "css": null,
            "js": null,
            "threshold": 0.4
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/groups/023c282c-6513-420a-a36b-a654312ab229/urls'
payload = {
    "urls": [
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "desktop": true,
            "mobile": true,
            "css": null,
            "js": null,
            "threshold": 0.4
        }
    ]
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "message": "42 URLs in Group 023c282c-6513-420a-a36b-a654312ab229 changed"
}
 

Request   

PUT api/v2/groups/{group_id}/urls

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

group_id   string     

The ID of the Group. Example: 023c282c-6513-420a-a36b-a654312ab229

Body Parameters

urls   string[]  optional    

Array of URLs with settings

desktop   boolean  optional    

Should a desktop screenshot be taken Example: true

Must be one of:
  • true
  • false
mobile   boolean  optional    

Should a mobile screenshot be taken Example: true

Must be one of:
  • true
  • false
css   string  optional    

CSS to be injected before the screenshot is taken Example: .btn {visibility: none;}

js   string  optional    

JavaScript to be injected before the screenshot is taken

threshold   numeric  optional    

Difference in percent of when this counts as a change detection Example: 0.4

id   string     

The ID of the URL in that Group. Example: 418e0748-a9cf-480b-86d6-88b00bac00b9

Monitoring Trigger

Starts a monitoring check for single pages when they change, e.g. when a post is saved in WordPress (trigger-based monitoring).

Trigger Monitoring Check

requires authentication

Reports changed pages of a monitoring Group. After a short wait (so caches serve the new version) a monitoring check runs for exactly these pages and compares them against their last monitoring screenshot, with the Group's usual alerts and webhooks. Further triggers for the same page during the wait are merged into one check.

The Group must be an enabled monitoring Group with the trigger type in its triggers. Only URLs that are active in the Group are checked, the others are listed under ignored (not_in_group, not_active, rate_limited). Nothing is charged for ignored URLs. At most 60 pages per Group and hour are accepted; a request with only pages over that cap gets 429.

type = manual_trigger ("run monitoring now") starts the monitoring check immediately, without the wait, the hourly cap or the merging described above. The check runs as soon as the request returns, so clear or warm the page caches first, then call this endpoint. Send scope = all (and no urls) to check every active URL of the Group, or scope = url with urls. The Group only has to be an enabled monitoring Group; manual_trigger is not configured in triggers. All URLs are checked in one monitoring run (batch_id, with trigger_context.type = manual_trigger); run_at is its start. URLs with a monitoring check in progress are listed under ignored (already_running). Waiting post_save checks of the started URLs are replaced by this run. Errors carry a reason: 409 already_running (every URL is already being checked, or a run for this Group is being started right now), 402 not_enough_credits (the checks do not fit into the remaining checks; nothing is started), 422 unsupported (none of the URLs can be checked), 422 trigger_not_enabled (not an enabled monitoring Group).

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/monitoring/trigger';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'group_id' => '023c282c-6513-420a-a36b-a654312ab229',
            'type' => 'post_save',
            'scope' => 'url',
            'urls' => [
                ['url' => 'https://example.com/about/', 'title' => 'About us', 'post_id' => 42],
            ],
            'editor' => 'Anna',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/monitoring/trigger" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"group_id\": \"023c282c-6513-420a-a36b-a654312ab229\",
    \"type\": \"post_save\",
    \"scope\": \"url\",
    \"urls\": [
        {
            \"url\": \"https:\\/\\/example.com\\/about\\/\",
            \"title\": \"About us\",
            \"post_id\": 42
        }
    ],
    \"editor\": \"Anna\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/monitoring/trigger"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "group_id": "023c282c-6513-420a-a36b-a654312ab229",
    "type": "post_save",
    "scope": "url",
    "urls": [
        {
            "url": "https:\/\/example.com\/about\/",
            "title": "About us",
            "post_id": 42
        }
    ],
    "editor": "Anna"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/monitoring/trigger'
payload = {
    "group_id": "023c282c-6513-420a-a36b-a654312ab229",
    "type": "post_save",
    "scope": "url",
    "urls": [
        {
            "url": "https:\/\/example.com\/about\/",
            "title": "About us",
            "post_id": 42
        }
    ],
    "editor": "Anna"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (202, post_save):


{
    "data": {
        "group_id": "023c282c-6513-420a-a36b-a654312ab229",
        "type": "post_save",
        "accepted": [
            "example.com/about"
        ],
        "ignored": [
            {
                "url": "https://example.com/new-post/",
                "reason": "not_in_group"
            }
        ],
        "run_at": "2026-09-25T10:05:00.000000Z"
    }
}
 

Example response (202, manual_trigger):


{
    "data": {
        "group_id": "023c282c-6513-420a-a36b-a654312ab229",
        "type": "manual_trigger",
        "accepted": [
            "example.com/about",
            "example.com/contact"
        ],
        "ignored": [
            {
                "url": "example.com/news",
                "reason": "already_running"
            }
        ],
        "run_at": "2026-09-25T10:00:03.000000Z",
        "batch_id": "0d5f8108-51f1-4961-939a-2d33c7145918"
    }
}
 

Example response (402, manual_trigger, not enough checks left):


{
    "message": "Bummer, you used your credit already. Please upgrade your account to a bigger plan.",
    "reason": "not_enough_credits"
}
 

Example response (409, manual_trigger, every URL already being checked):


{
    "message": "A monitoring check is already running for these pages.",
    "reason": "already_running"
}
 

Request   

POST api/v2/monitoring/trigger

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

group_id   string     

ID of the monitoring Group the URLs belong to. The Group must have the trigger type in its triggers. Must not be greater than 36 characters. Example: 023c282c-6513-420a-a36b-a654312ab229

type   string     

What happened. Allowed values: see config/enums.trigger_type: post_save (a page was saved; checked after a short wait) or manual_trigger (run the monitoring now; the check starts immediately). Example: post_save

Must be one of:
  • post_save
  • manual_trigger
scope   string  optional    

What to check: url (default) = only the given urls; all = every active URL of the Group, without urls. all is allowed with type = manual_trigger only. Example: url

Must be one of:
  • url
  • all
urls   object[]  optional    

The changed pages, max. 50 per request. Only URLs that are active in the Group are checked; others are returned under ignored. Required unless scope is all; not allowed with scope = all.

url   string     

The page URL, with or without protocol and trailing slash. Must not be greater than 2048 characters. Example: https://example.com/about/

title   string  optional    

Optional page title, shown in the run's trigger_context. Must not be greater than 255 characters. Example: About us

post_id   integer  optional    

Optional CMS id of the page (e.g. the WordPress post ID), stored in trigger_context. Must be at least 1. Example: 42

editor   string  optional    

Optional display name of the person who made the change. Do not send email addresses. Must not be greater than 100 characters. Example: Anna

Extend Monitoring Trigger

requires authentication

Reports pages of a monitoring Group that are still being edited without being saved, e.g. a WordPress autosave. For each page that already waits for its trigger check, the wait is restarted (7 minutes from now), so the check runs only once the editing has stopped.

An extension never starts a check: pages without a waiting check are listed under ignored (not_waiting) and nothing happens for them. Extensions do not count towards the hourly cap of the trigger endpoint and cost nothing. The Group must be an enabled monitoring Group with the trigger type in its triggers.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/monitoring/trigger/extend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'group_id' => '023c282c-6513-420a-a36b-a654312ab229',
            'type' => 'post_save',
            'urls' => [
                ['url' => 'https://example.com/about/'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/monitoring/trigger/extend" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"group_id\": \"023c282c-6513-420a-a36b-a654312ab229\",
    \"type\": \"post_save\",
    \"urls\": [
        {
            \"url\": \"https:\\/\\/example.com\\/about\\/\"
        }
    ]
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/monitoring/trigger/extend"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "group_id": "023c282c-6513-420a-a36b-a654312ab229",
    "type": "post_save",
    "urls": [
        {
            "url": "https:\/\/example.com\/about\/"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/monitoring/trigger/extend'
payload = {
    "group_id": "023c282c-6513-420a-a36b-a654312ab229",
    "type": "post_save",
    "urls": [
        {
            "url": "https:\/\/example.com\/about\/"
        }
    ]
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (202):


{
    "data": {
        "group_id": "023c282c-6513-420a-a36b-a654312ab229",
        "type": "post_save",
        "accepted": [
            "example.com/about"
        ],
        "ignored": [
            {
                "url": "https://example.com/contact/",
                "reason": "not_waiting"
            }
        ],
        "run_at": "2026-09-25T10:07:00.000000Z"
    }
}
 

Request   

POST api/v2/monitoring/trigger/extend

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

group_id   string     

ID of the monitoring Group the URLs belong to. The Group must have the trigger type in its triggers. Must not be greater than 36 characters. Example: 023c282c-6513-420a-a36b-a654312ab229

type   string     

The trigger type of the waiting check. Allowed values: post_save. Example: post_save

Must be one of:
  • post_save
urls   object[]     

The pages that are still being edited, max. 50 per request. Only pages with a waiting check are extended; others are returned under ignored.

url   string     

The page URL, with or without protocol and trailing slash. Must not be greater than 2048 characters. Example: https://example.com/about/

Get Running Monitoring

requires authentication

Tells for each requested monitoring Group whether a monitoring run is in progress, and which one. Counts every monitoring run of the Group: scheduled, trigger-based and manual (manual_trigger), including its comparisons.

Every requested Group that is one of your monitoring Groups is listed, with batch_id = the running Batch or null when nothing runs. Unknown Groups, Groups of other accounts and manual Groups are left out (no 404); treat a missing Group as not running. With several running Batches the newest one is returned. Checks open or processing for more than 60 minutes are considered stuck and do not count.

Whether the run is finished, and its result, comes from GET /batches/{id}: the Batch is finished when finished_at is set, which can be a few minutes after the Group stopped running.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/monitoring/running';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'group_ids' => '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/monitoring/running?group_ids=023c282c-6513-420a-a36b-a654312ab229%2C023c282c-6513-420a-a36b-a654312ab230" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/monitoring/running"
);

const params = {
    "group_ids": "023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/monitoring/running'
params = {
  'group_ids': '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "group_id": "023c282c-6513-420a-a36b-a654312ab229",
            "batch_id": "0d5f8108-51f1-4961-939a-2d33c7145918"
        },
        {
            "group_id": "023c282c-6513-420a-a36b-a654312ab230",
            "batch_id": null
        }
    ]
}
 

Request   

GET api/v2/monitoring/running

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

group_ids   string     

Comma separated list of monitoring Group IDs, max. 50. Example: 023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230

Queue

This object represents a log of taking screenshots, comparing screenshots and their status.

List Queues

requires authentication

Returns a list of all Queues. The Queues are sorted by creation date, with the most recent Queues appearing first.

When filtering by batch or batches parameter, the response includes additional metadata:

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/queues';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'status' => 'done,failed',
            'groups' => '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
            'batches' => '0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/queues?status=done%2Cfailed&groups=023c282c-6513-420a-a36b-a654312ab229%2C023c282c-6513-420a-a36b-a654312ab230&batches=0d5f8108-51f1-4961-939a-2d33c7145918%2C1d5f8108-51f1-4961-939a-2d33c7145918" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/queues"
);

const params = {
    "status": "done,failed",
    "groups": "023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230",
    "batches": "0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/queues'
params = {
  'status': 'done,failed',
  'groups': '023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230',
  'batches': '0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3",
            "url": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
            "group": "023c282c-6513-420a-a36b-a654312ab229",
            "device": "desktop",
            "status": "open",
            "error_msg": null,
            "sc_type": "pre",
            "css": null,
            "js": null,
            "monitoring": true,
            "url_link": "https://example.com",
            "html_title": "Example Title",
            "image_link": "https://images.webchangedetector.com/image.jpg",
            "created_at": "2024-08-08 13:37:42",
            "updated_at": "2024-08-08 13:37:42"
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/queues?page=1",
        "last": "http://api.webchangedetector.test/api/v2/queues?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/queues?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/queues",
        "per_page": 15,
        "to": 1,
        "total": 1,
        "status_counts": {
            "open": 15,
            "processing": 3,
            "done": 42,
            "failed": 2,
            "by_type": {
                "pre": {
                    "open": 5,
                    "processing": 1,
                    "done": 14,
                    "failed": 0
                },
                "post": {
                    "open": 5,
                    "processing": 1,
                    "done": 14,
                    "failed": 1
                },
                "comparison": {
                    "open": 5,
                    "processing": 1,
                    "done": 14,
                    "failed": 1
                }
            }
        },
        "status_counts_by_batch": {
            "0d5f8108-51f1-4961-939a-2d33c7145918": {
                "open": 10,
                "processing": 2,
                "done": 30,
                "failed": 1,
                "by_type": {
                    "pre": {
                        "open": 0,
                        "processing": 0,
                        "done": 15,
                        "failed": 0
                    },
                    "post": {
                        "open": 10,
                        "processing": 2,
                        "done": 3,
                        "failed": 0
                    },
                    "compare": {
                        "open": 0,
                        "processing": 0,
                        "done": 12,
                        "failed": 1
                    }
                }
            }
        }
    }
}
 

Request   

GET api/v2/queues

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

status   string  optional    

Comma separated list of status to filter for. Example: done,failed

groups   string  optional    

Comma separated list of Group UUIDs to filter for. Example: 023c282c-6513-420a-a36b-a654312ab229,023c282c-6513-420a-a36b-a654312ab230

batches   string  optional    

Comma separated list of Batch UUIDs to filter for. Example: 0d5f8108-51f1-4961-939a-2d33c7145918,1d5f8108-51f1-4961-939a-2d33c7145918

Get Queue

requires authentication

Retrieves a Queue object identfied by their ID

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/queues/3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/queues/3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/queues/3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/queues/3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3",
        "url": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
        "group": "023c282c-6513-420a-a36b-a654312ab229",
        "device": "desktop",
        "status": "open",
        "sc_type": "pre",
        "css": null,
        "js": null,
        "monitoring": true,
        "url_link": "https://example.com",
        "html_title": "Example Title",
        "image_link": "https://images.webchangedetector.com/image.jpg",
        "created_at": "2024-08-08 13:37:42",
        "updated_at": "2024-08-08 13:37:42"
    }
}
 

Request   

GET api/v2/queues/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Queue. Example: 3bf0cec1-e8ee-49ba-804e-0db5ed93a7e3

Screenshot

This object represents the actual screenshot and the location it's stored at.

List Screenshots

requires authentication

Returns a list of all Screenshots. The Screenshots are sorted by creation date, with the most recent Screenshots appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/screenshots';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/screenshots" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/screenshots"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/screenshots'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "958dce2c-8468-47d3-8bc2-7dc6bf0aadae",
            "queue": "8ea7b88d-b5c6-4fad-8e9d-1497a54d9266",
            "domain": "example.com",
            "url": "example.com/foobar",
            "link": "https://images.webchangedetector.com/example.jpg",
            "device": "desktop",
            "sc_type": "pre",
            "monitoring": true,
            "browser_console": {
                "text": "Failed to load resource: the server responded with a status of 404 ()",
                "type": "error",
                "location": {
                    "url": "https://example.com.com/favicon.ico"
                }
            }
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/screenshots?page=1",
        "last": "http://api.webchangedetector.test/api/v2/screenshots?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/screenshots?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/screenshots",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/screenshots

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Screenshot

requires authentication

Retrieves a Screenshot object identfied by their ID

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/screenshots/958dce2c-8468-47d3-8bc2-7dc6bf0aadae';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/screenshots/958dce2c-8468-47d3-8bc2-7dc6bf0aadae" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/screenshots/958dce2c-8468-47d3-8bc2-7dc6bf0aadae"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/screenshots/958dce2c-8468-47d3-8bc2-7dc6bf0aadae'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "958dce2c-8468-47d3-8bc2-7dc6bf0aadae",
        "queue": "8ea7b88d-b5c6-4fad-8e9d-1497a54d9266",
        "domain": "example.com",
        "url": "example.com/foobar",
        "link": "https://images.webchangedetector.com/example.jpg",
        "device": "desktop",
        "sc_type": "pre",
        "monitoring": true,
        "browser_console": {
            "text": "Failed to load resource: the server responded with a status of 404 ()",
            "type": "error",
            "location": {
                "url": "https://example.com.com/favicon.ico"
            }
        }
    }
}
 

Request   

GET api/v2/screenshots/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Screenshot. Example: 958dce2c-8468-47d3-8bc2-7dc6bf0aadae

Take Screenshot

requires authentication

Adds screenshots of URLs in the passed groups to the queue. The parameter sc_type indicates if pre-update screenshots are taken (pre) or post-update screenshots and comparisons are taken (post). When post-update screenshots are done, a comparison is automatically triggered.

By default all groups share ONE batch. With batch_per_group=true each group gets its own batch and the response additionally contains a batches map (group UUID => batch UUID).

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/screenshots/take';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'group_ids' => '["023c282c-6513-420a-a36b-a654312ab229", "023c282c-6513-420a-a36b-a654312ab230"]',
            'sc_type' => 'pre',
            'source' => 'manual',
            'batch_per_group' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/screenshots/take" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"group_ids\": \"[\\\"023c282c-6513-420a-a36b-a654312ab229\\\", \\\"023c282c-6513-420a-a36b-a654312ab230\\\"]\",
    \"sc_type\": \"pre\",
    \"source\": \"manual\",
    \"batch_per_group\": false
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/screenshots/take"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "group_ids": "[\"023c282c-6513-420a-a36b-a654312ab229\", \"023c282c-6513-420a-a36b-a654312ab230\"]",
    "sc_type": "pre",
    "source": "manual",
    "batch_per_group": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/screenshots/take'
payload = {
    "group_ids": "[\"023c282c-6513-420a-a36b-a654312ab229\", \"023c282c-6513-420a-a36b-a654312ab230\"]",
    "sc_type": "pre",
    "source": "manual",
    "batch_per_group": false
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200, Success):


{
    "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
    "amount_screenshots": 42,
    "groups": [
        "023c282c-6513-420a-a36b-a654312ab229",
        "023c282c-6513-420a-a36b-a654312ab230"
    ]
}
 

Example response (200, Success (batch_per_group=true)):


{
    "batch": "0d5f8108-51f1-4961-939a-2d33c7145918",
    "amount_screenshots": 42,
    "groups": [
        "023c282c-6513-420a-a36b-a654312ab229",
        "023c282c-6513-420a-a36b-a654312ab230"
    ],
    "batches": {
        "023c282c-6513-420a-a36b-a654312ab229": "0d5f8108-51f1-4961-939a-2d33c7145918",
        "023c282c-6513-420a-a36b-a654312ab230": "1e6f9219-62f2-5a72-a4ab-3e44d8256a29"
    }
}
 

Example response (402, Not enough credits):


{
    "message": "Bummer, you used your credit already. Please upgrade your account to a bigger plan."
}
 

Example response (422, All selected URLs are unsupported content (batch_per_group=true)):


{
    "message": "None of the selected URLs can be checked. Only HTML pages are supported (not PDFs, images, or other files)."
}
 

Request   

POST api/v2/screenshots/take

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

group_ids   string[]     

A list of Groups. Example: ["023c282c-6513-420a-a36b-a654312ab229", "023c282c-6513-420a-a36b-a654312ab230"]

sc_type   string  optional    

Screenshot phase. pre captures the baseline before changes; post captures after changes and triggers comparisons against the matching pre screenshots in the same Batch. Example: pre

Must be one of:
  • pre
  • post
source   string  optional    

Optional batch source tag for filtering / reporting. Allowed values: see Batch::SOURCE_ALL (e.g. manual, auto_update, monitoring). Example: manual

Must be one of:
  • manual
  • monitoring
  • auto_update
batch_per_group   boolean  optional    

When true, one Batch is created PER group instead of a single shared batch. The response then additionally contains a batches object mapping each started group UUID to its batch UUID; groups that could not be started (no credits, nothing to check) are omitted from the map. Defaults to false (unchanged single-batch behavior and response). Send as JSON boolean or 0/1 — the strings "true"/"false" are rejected with 422. Example: false

Subaccount

This object represents a Subaccount

Verify Invitation

requires authentication

Returns subaccount details for a valid invitation token, including the decrypted API token. Idempotent: can be called multiple times until the invitation is accepted or expires. Does NOT consume the token. Use POST /invitations/accept after the user has finished setting up their account to mark the invitation as consumed. This endpoint does not require authentication.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/invitations/verify';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'invitation_token' => 'ducimus',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/invitations/verify" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"invitation_token\": \"ducimus\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/invitations/verify"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "invitation_token": "ducimus"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/invitations/verify'
payload = {
    "invitation_token": "ducimus"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "17482316-2398-4374-9f70-e0e3e2bf292a",
        "name_first": "Jane",
        "name_last": "Doe",
        "email": "mail@example.com",
        "api_token": "V82abQfqPA3hBXL9ZHfMAt94Ta23VUdYt691D7GO"
    }
}
 

Request   

POST api/v2/invitations/verify

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

invitation_token   string     

The invitation token from the email Example: ducimus

Accept Invitation

requires authentication

Marks a subaccount invitation as accepted, consuming the token. Should be called by the webapp AFTER the local user account has been successfully created — calling this before is a self-DoS because the token is cleared and cannot be re-verified afterwards. This endpoint does not require authentication.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/invitations/accept';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'invitation_token' => 'reprehenderit',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/invitations/accept" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"invitation_token\": \"reprehenderit\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/invitations/accept"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "invitation_token": "reprehenderit"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/invitations/accept'
payload = {
    "invitation_token": "reprehenderit"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "message": "Invitation accepted"
}
 

Request   

POST api/v2/invitations/accept

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

invitation_token   string     

The invitation token from the email Example: reprehenderit

List Subaccounts

requires authentication

Returns a list of all Subaccounts. The Subaccounts are sorted by creation date, with the most recent Subaccounts appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/subaccounts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/subaccounts" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/subaccounts"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/subaccounts'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "17482316-2398-4374-9f70-e0e3e2bf292a",
            "name_first": "Jane",
            "name_last": "Doe",
            "email": "mail@example.com",
            "checks_done": 42,
            "checks_left": 1337,
            "timezone": "UTC",
            "is_subaccount": true
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/subaccounts?page=1",
        "last": "http://api.webchangedetector.test/api/v2/subaccounts?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/subaccounts?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/subaccounts",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/subaccounts

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Subaccount

requires authentication

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/subaccounts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name_first' => 'Hans',
            'name_last' => 'Hacker',
            'email' => 'mail@example.com',
            'limit_checks' => 1000.0,
            'plan_label' => 'Agency Starter',
            'can_create_subaccounts' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/subaccounts" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name_first\": \"Hans\",
    \"name_last\": \"Hacker\",
    \"email\": \"mail@example.com\",
    \"limit_checks\": 1000,
    \"plan_label\": \"Agency Starter\",
    \"can_create_subaccounts\": false
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/subaccounts"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name_first": "Hans",
    "name_last": "Hacker",
    "email": "mail@example.com",
    "limit_checks": 1000,
    "plan_label": "Agency Starter",
    "can_create_subaccounts": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/subaccounts'
payload = {
    "name_first": "Hans",
    "name_last": "Hacker",
    "email": "mail@example.com",
    "limit_checks": 1000,
    "plan_label": "Agency Starter",
    "can_create_subaccounts": false
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "17482316-2398-4374-9f70-e0e3e2bf292a",
        "name_first": "Jane",
        "name_last": "Doe",
        "email": "mail@example.com",
        "checks_done": 42,
        "checks_left": 1337,
        "timezone": "UTC",
        "api_token": "V82abQfqPA3hBXL9ZHfMAt94Ta23VUdYt691D7GO",
        "is_subaccount": true
    }
}
 

Request   

POST api/v2/subaccounts

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name_first   string     

First Name Example: Hans

name_last   string     

Last Name Example: Hacker

email   string     

Must be a valid email Example: mail@example.com

limit_checks   number     

Maximum number of checks per renewal period this subaccount may consume. Capped at the parent account's plan limit (not remaining quota). Must be at least 0. Example: 1000

plan_label   string  optional    

Optional display label shown in the subaccount UI (e.g. "Agency Starter"). No billing semantics — purely cosmetic. Must not be greater than 255 characters. Example: Agency Starter

can_create_subaccounts   boolean  optional    

Allow this subaccount to itself manage subaccounts (level-1 reseller). Defaults to false. Example: false

Get Subaccount

requires authentication

Retrieves a Subaccount object identfied by their ID

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "17482316-2398-4374-9f70-e0e3e2bf292a",
        "name_first": "Jane",
        "name_last": "Doe",
        "email": "mail@example.com",
        "checks_done": 42,
        "checks_left": 1337,
        "timezone": "UTC",
        "is_subaccount": true
    }
}
 

Request   

GET api/v2/subaccounts/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Subaccount. Example: 17482316-2398-4374-9f70-e0e3e2bf292a

Update Subaccount

requires authentication

Updates the specified Subaccount by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name_first' => 'Hans',
            'name_last' => 'Hacker',
            'limit_checks' => 1500.0,
            'plan_label' => 'Agency Pro',
            'can_create_subaccounts' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name_first\": \"Hans\",
    \"name_last\": \"Hacker\",
    \"limit_checks\": 1500,
    \"plan_label\": \"Agency Pro\",
    \"can_create_subaccounts\": false
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name_first": "Hans",
    "name_last": "Hacker",
    "limit_checks": 1500,
    "plan_label": "Agency Pro",
    "can_create_subaccounts": false
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a'
payload = {
    "name_first": "Hans",
    "name_last": "Hacker",
    "limit_checks": 1500,
    "plan_label": "Agency Pro",
    "can_create_subaccounts": false
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "17482316-2398-4374-9f70-e0e3e2bf292a",
        "name_first": "Jane",
        "name_last": "Doe",
        "email": "mail@example.com",
        "checks_done": 42,
        "checks_left": 1337,
        "timezone": "UTC",
        "is_subaccount": true
    }
}
 

Request   

PUT api/v2/subaccounts/{id}

PATCH api/v2/subaccounts/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Subaccount. Example: 17482316-2398-4374-9f70-e0e3e2bf292a

Body Parameters

name_first   string     

First Name Example: Hans

name_last   string     

Last Name Example: Hacker

limit_checks   number  optional    

New per-renewal-period check quota. Capped at the parent account's plan limit. Email is immutable on update. Must be at least 0. Example: 1500

plan_label   string  optional    

Optional display label shown in the subaccount UI. No billing semantics. Must not be greater than 255 characters. Example: Agency Pro

can_create_subaccounts   boolean  optional    

Toggle subaccount-management permission for this user. Example: false

Delete Subaccount

requires authentication

Permanently deletes a Subaccount. This cannot be undone.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request DELETE \
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "message": "Subaccount deleted"
}
 

Request   

DELETE api/v2/subaccounts/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Subaccount. Example: 17482316-2398-4374-9f70-e0e3e2bf292a

Invite Subaccount

requires authentication

Sends an invitation email to a Subaccount user, allowing them to create their own webapp login. Works for both new and existing subaccounts.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a/invite';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a/invite" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a/invite"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/subaccounts/17482316-2398-4374-9f70-e0e3e2bf292a/invite'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers)
response.json()

Example response (200):


{
    "message": "Invitation sent"
}
 

Request   

POST api/v2/subaccounts/{id}/invite

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The UUID of the Subaccount. Example: 17482316-2398-4374-9f70-e0e3e2bf292a

Url

This object represents a single URL and specific settings

List Urls

requires authentication

Returns a list of all Urls. The Urls are sorted by creation date, with the most recent Urls appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/urls';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/urls" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/urls"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/urls'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
            "html_title": "Example Title",
            "url": "example.com/foobar",
            "last_crawled_at": "2025-01-01 13:37:42",
            "type": "types",
            "category": "Seiten",
            "website": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
            "groups": [
                "023c282c-6513-420a-a36b-a654312ab229",
                "023c282c-6513-420a-a36b-a654312ab230"
            ],
            "status_code": 200,
            "error_count": 0
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/urls?page=1",
        "last": "http://api.webchangedetector.test/api/v2/urls?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/urls?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/urls",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/urls

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Url

requires authentication

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/urls';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'url' => 'https://example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/urls" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"url\": \"https:\\/\\/example.com\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/urls"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "url": "https:\/\/example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/urls'
payload = {
    "url": "https:\/\/example.com"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "html_title": "Example Title",
        "url": "example.com/foobar",
        "last_crawled_at": "2025-01-01 13:37:42",
        "type": null,
        "category": null,
        "website": null,
        "groups": [],
        "status_code": 200,
        "error_count": 0
    }
}
 

Request   

POST api/v2/urls

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

url   string     

Must be a valid URL Example: https://example.com

Get Url

requires authentication

Retrieves a Url object identfied by their ID

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "html_title": "Example Title",
        "url": "example.com/foobar",
        "last_crawled_at": "2025-01-01 13:37:42",
        "type": "types",
        "category": "Seiten",
        "website": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
        "groups": [
            "023c282c-6513-420a-a36b-a654312ab229",
            "023c282c-6513-420a-a36b-a654312ab230"
        ],
        "status_code": 200,
        "error_count": 0
    }
}
 

Request   

GET api/v2/urls/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Url. Example: 418e0748-a9cf-480b-86d6-88b00bac00b9

Update Url

requires authentication

Updates the specified Url by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'url' => 'https://example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"url\": \"https:\\/\\/example.com\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "url": "https:\/\/example.com"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9'
payload = {
    "url": "https:\/\/example.com"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "418e0748-a9cf-480b-86d6-88b00bac00b9",
        "html_title": "Example Title",
        "url": "example.com/foobar",
        "last_crawled_at": "2025-01-01 13:37:42",
        "type": "types",
        "category": "Seiten",
        "website": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
        "status_code": 200,
        "error_count": 0,
        "groups": [
            "023c282c-6513-420a-a36b-a654312ab229",
            "023c282c-6513-420a-a36b-a654312ab230"
        ]
    }
}
 

Request   

PUT api/v2/urls/{id}

PATCH api/v2/urls/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Url. Example: 418e0748-a9cf-480b-86d6-88b00bac00b9

Body Parameters

url   string     

Must be a valid URL Example: https://example.com

Delete Url

requires authentication

Permanently deletes a Url. This cannot be undone.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request DELETE \
    "https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/urls/418e0748-a9cf-480b-86d6-88b00bac00b9'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "message": "URL deleted"
}
 

Request   

DELETE api/v2/urls/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Url. Example: 418e0748-a9cf-480b-86d6-88b00bac00b9

Add From Sitemap

requires authentication

Adds all the URLs found in a sitemap for a given domain.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/urls/add-from-sitemap';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'domain' => 'example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/urls/add-from-sitemap" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"domain\": \"example.com\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/urls/add-from-sitemap"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "domain": "example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/urls/add-from-sitemap'
payload = {
    "domain": "example.com"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "message": "Sitemap parsing started."
}
 

Request   

POST api/v2/urls/add-from-sitemap

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

domain   string     

The domain to parse the sitemap from Example: example.com

Webhook

This object contains the webhook calls a user can set to be notified for various events.

List Webhooks

requires authentication

Returns a list of all Webhooks. The Webhooks are sorted by creation date, with the most recent Webhooks appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/webhooks';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'event' => 'comparison_status_new',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/webhooks?event=comparison_status_new" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/webhooks"
);

const params = {
    "event": "comparison_status_new",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/webhooks'
params = {
  'event': 'comparison_status_new',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "d86e9245-3524-4f8d-b5d9-e4a472136d27",
            "event": "comparison_status_new",
            "url": "https://example.com"
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/webhooks?page=1",
        "last": "http://api.webchangedetector.test/api/v2/webhooks?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/webhooks?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/webhooks",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/webhooks

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

event   string  optional    

Event of the Queue. Example: comparison_status_new

Must be one of:
  • batch_finished
  • comparison_status_new
  • queue_status_done
  • queue_status_failed

Create Webhook

requires authentication

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/webhooks';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'url' => 'https://example.com/webhooks/wcd',
            'event' => 'batch_finished',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/webhooks" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"url\": \"https:\\/\\/example.com\\/webhooks\\/wcd\",
    \"event\": \"batch_finished\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/webhooks"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "url": "https:\/\/example.com\/webhooks\/wcd",
    "event": "batch_finished"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/webhooks'
payload = {
    "url": "https:\/\/example.com\/webhooks\/wcd",
    "event": "batch_finished"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "d86e9245-3524-4f8d-b5d9-e4a472136d27",
        "event": "comparison_status_new",
        "url": "https://example.com"
    }
}
 

Request   

POST api/v2/webhooks

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

url   string     

HTTPS endpoint that receives the webhook POST. Requests are signed with HMAC-SHA256 in the Signature header — verify against your account webhook secret (GET /api/v2/account). Must be a valid URL. Example: https://example.com/webhooks/wcd

event   string     

Event to subscribe to. Allowed values for the v2 API: batch_finished (all Screenshots in a Batch complete; the payload carries the Batch source and trigger_context, see List Batches), comparison_status_new (a change was detected), queue_status_done, queue_status_failed. See Webhook::EVENTS_ALL_API_V2. Example: batch_finished

Must be one of:
  • batch_finished
  • comparison_status_new
  • comparison_status_new_collection
  • comparison_summary
  • queue_status_done
  • queue_status_failed
  • wordpress_cron
  • wordpress_single_call

Get Webhook

requires authentication

Retrieves a Webhook object identfied by their ID

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "d86e9245-3524-4f8d-b5d9-e4a472136d27",
        "event": "comparison_status_new",
        "url": "https://example.com"
    }
}
 

Request   

GET api/v2/webhooks/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Webhook. Example: d86e9245-3524-4f8d-b5d9-e4a472136d27

Update Webhook

requires authentication

Updates the specified Webhook by setting the values of the parameters passed.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'url' => 'https://example.com',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"url\": \"https:\\/\\/example.com\"
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "url": "https:\/\/example.com"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27'
payload = {
    "url": "https:\/\/example.com"
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": "d86e9245-3524-4f8d-b5d9-e4a472136d27",
        "event": "comparison_status_new",
        "url": "https://example.com"
    }
}
 

Request   

PUT api/v2/webhooks/{id}

PATCH api/v2/webhooks/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Webhook. Example: d86e9245-3524-4f8d-b5d9-e4a472136d27

Body Parameters

url   string     

Must be a valid URL Example: https://example.com

Delete Webhook

requires authentication

Permanently deletes a Webhook. This cannot be undone.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request DELETE \
    "https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/webhooks/d86e9245-3524-4f8d-b5d9-e4a472136d27'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "message": "{ID} deleted"
}
 

Request   

DELETE api/v2/webhooks/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Webhook. Example: d86e9245-3524-4f8d-b5d9-e4a472136d27

Website

This object represents Websites.

List Websites

requires authentication

Returns a list of all Website. The Websites are sorted by creation date, with the most recent Websites appearing first.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/websites';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'domain' => 'example.com',
            'managed_by' => 'mainwp',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/websites?domain=example.com&managed_by=mainwp" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/websites"
);

const params = {
    "domain": "example.com",
    "managed_by": "mainwp",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/websites'
params = {
  'domain': 'example.com',
  'managed_by': 'mainwp',
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
            "manual_detection_group": "023c282c-6513-420a-a36b-a654312ab229",
            "auto_detection_group": "023c282c-6513-420a-a36b-a654312ab230",
            "domain": "example.com",
            "last_seen_at": "2025-07-14T16:19:30Z",
            "plugin_version": "4.0.3",
            "is_multisite_subsite": false,
            "parent_multisite_website": null,
            "sync_url_types": [
                {
                    "url_type_slug": "types",
                    "url_type_name": "Post Types",
                    "post_type_slug": "posts",
                    "post_type_name": "Posts"
                }
            ],
            "auto_update_settings": {
                "auto_update_checks_enabled": false,
                "auto_update_checks_from": "13:37",
                "auto_update_checks_to": "13:42",
                "auto_update_checks_monday": true,
                "auto_update_checks_tuesday": true,
                "auto_update_checks_wednesday": true,
                "auto_update_checks_thursday": true,
                "auto_update_checks_friday": true,
                "auto_update_checks_saturday": false,
                "auto_update_checks_sunday": false,
                "auto_update_checks_emails": "mail@example.com",
                "allow_core_security_updates": true
            },
            "allowances": {
                "change_detections_view": true,
                "ai_rules_view": true,
                "manual_checks_view": true,
                "manual_checks_start": true,
                "manual_checks_settings": true,
                "manual_checks_urls": true,
                "monitoring_checks_view": true,
                "monitoring_checks_settings": true,
                "monitoring_checks_urls": true,
                "logs_view": true,
                "settings_view": true,
                "settings_add_urls": true,
                "settings_account_settings": true,
                "upgrade_account": true,
                "wizard_start": true,
                "only_frontpage": false,
                "plugin_view": true
            }
        }
    ],
    "links": {
        "first": "http://api.webchangedetector.test/api/v2/websites?page=1",
        "last": "http://api.webchangedetector.test/api/v2/websites?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "« Previous",
                "active": false
            },
            {
                "url": "http://api.webchangedetector.test/api/v2/websites?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": null,
                "label": "Next »",
                "active": false
            }
        ],
        "path": "http://api.webchangedetector.test/api/v2/websites",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}
 

Request   

GET api/v2/websites

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

domain   string  optional    

Filter websites by domain. Must not be greater than 253 characters. Example: example.com

managed_by   string  optional    

Filter websites by owning integration (e.g. native, mainwp). Omit for all. Example: mainwp

Must be one of:
  • native
  • mainwp

Create Website

requires authentication

Creates a Website by setting the values of the parameters passed. Typically only done by the WordPress plugin.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/websites';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'domain' => 'example.com',
            'manual_detection_group_id' => '91e9c9fd-b86f-4269-a3f4-f02810167a6d',
            'auto_detection_group_id' => '91e9c9fd-b86f-4269-a3f4-f02810167a6d',
            'managed_by' => 'native',
            'parent_multisite_website_id' => '91e9c9fd-b86f-4269-a3f4-f02810167a6d',
            'allowances' => [
                'change_detections_view' => true, 'ai_rules_view' => true, 'manual_checks_view' => true, 'manual_checks_start' => true, 'manual_checks_settings' => true, 'manual_checks_urls' => true, 'monitoring_checks_view' => true, 'monitoring_checks_settings' => true, 'monitoring_checks_urls' => true, 'logs_view' => true,
                'settings_view' => true, 'settings_add_urls' => true, 'settings_account_settings' => true, 'upgrade_account' => true, 'wizard_start' => true, 'only_frontpage' => false, 'plugin_view' => true,
            ],
            'sync_url_types' => [
                ['url_type_slug' => 'types', 'url_type_name' => 'Post Types', 'post_type_slug' => 'posts', 'post_type_name' => 'Posts'],
                ['url_type_slug' => 'types', 'url_type_name' => 'Post Types', 'post_type_slug' => 'pages', 'post_type_name' => 'Pages'],
                ['url_type_slug' => 'taxonomies', 'url_type_name' => 'Taxonomies', 'post_type_slug' => 'category', 'post_type_name' => 'Category'],
            ],
            'auto_update_settings' => [
                'auto_update_checks_enabled' => false, 'auto_update_checks_monday' => true, 'auto_update_checks_tuesday' => true, 'auto_update_checks_wednesday' => true, 'auto_update_checks_thursday' => true, 'auto_update_checks_friday' => true, 'auto_update_checks_saturday' => false, 'auto_update_checks_sunday' => false, 'auto_update_checks_from' => null, 'auto_update_checks_to' => null,
                'auto_update_checks_emails' => null, 'allow_core_security_updates' => true,
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request POST \
    "https://api.webchangedetector.com/api/v2/websites" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"domain\": \"example.com\",
    \"manual_detection_group_id\": \"91e9c9fd-b86f-4269-a3f4-f02810167a6d\",
    \"auto_detection_group_id\": \"91e9c9fd-b86f-4269-a3f4-f02810167a6d\",
    \"managed_by\": \"native\",
    \"parent_multisite_website_id\": \"91e9c9fd-b86f-4269-a3f4-f02810167a6d\",
    \"allowances\": {
        \"change_detections_view\": true,
        \"ai_rules_view\": true,
        \"manual_checks_view\": true,
        \"manual_checks_start\": true,
        \"manual_checks_settings\": true,
        \"manual_checks_urls\": true,
        \"monitoring_checks_view\": true,
        \"monitoring_checks_settings\": true,
        \"monitoring_checks_urls\": true,
        \"logs_view\": true,
        \"settings_view\": true,
        \"settings_add_urls\": true,
        \"settings_account_settings\": true,
        \"upgrade_account\": true,
        \"wizard_start\": true,
        \"only_frontpage\": false,
        \"plugin_view\": true
    },
    \"sync_url_types\": [
        {
            \"url_type_slug\": \"types\",
            \"url_type_name\": \"Post Types\",
            \"post_type_slug\": \"posts\",
            \"post_type_name\": \"Posts\"
        },
        {
            \"url_type_slug\": \"types\",
            \"url_type_name\": \"Post Types\",
            \"post_type_slug\": \"pages\",
            \"post_type_name\": \"Pages\"
        },
        {
            \"url_type_slug\": \"taxonomies\",
            \"url_type_name\": \"Taxonomies\",
            \"post_type_slug\": \"category\",
            \"post_type_name\": \"Category\"
        }
    ],
    \"auto_update_settings\": {
        \"auto_update_checks_enabled\": false,
        \"auto_update_checks_monday\": true,
        \"auto_update_checks_tuesday\": true,
        \"auto_update_checks_wednesday\": true,
        \"auto_update_checks_thursday\": true,
        \"auto_update_checks_friday\": true,
        \"auto_update_checks_saturday\": false,
        \"auto_update_checks_sunday\": false,
        \"auto_update_checks_from\": null,
        \"auto_update_checks_to\": null,
        \"auto_update_checks_emails\": null,
        \"allow_core_security_updates\": true
    }
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/websites"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "domain": "example.com",
    "manual_detection_group_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "auto_detection_group_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "managed_by": "native",
    "parent_multisite_website_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "allowances": {
        "change_detections_view": true,
        "ai_rules_view": true,
        "manual_checks_view": true,
        "manual_checks_start": true,
        "manual_checks_settings": true,
        "manual_checks_urls": true,
        "monitoring_checks_view": true,
        "monitoring_checks_settings": true,
        "monitoring_checks_urls": true,
        "logs_view": true,
        "settings_view": true,
        "settings_add_urls": true,
        "settings_account_settings": true,
        "upgrade_account": true,
        "wizard_start": true,
        "only_frontpage": false,
        "plugin_view": true
    },
    "sync_url_types": [
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "posts",
            "post_type_name": "Posts"
        },
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "pages",
            "post_type_name": "Pages"
        },
        {
            "url_type_slug": "taxonomies",
            "url_type_name": "Taxonomies",
            "post_type_slug": "category",
            "post_type_name": "Category"
        }
    ],
    "auto_update_settings": {
        "auto_update_checks_enabled": false,
        "auto_update_checks_monday": true,
        "auto_update_checks_tuesday": true,
        "auto_update_checks_wednesday": true,
        "auto_update_checks_thursday": true,
        "auto_update_checks_friday": true,
        "auto_update_checks_saturday": false,
        "auto_update_checks_sunday": false,
        "auto_update_checks_from": null,
        "auto_update_checks_to": null,
        "auto_update_checks_emails": null,
        "allow_core_security_updates": true
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/websites'
payload = {
    "domain": "example.com",
    "manual_detection_group_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "auto_detection_group_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "managed_by": "native",
    "parent_multisite_website_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "allowances": {
        "change_detections_view": true,
        "ai_rules_view": true,
        "manual_checks_view": true,
        "manual_checks_start": true,
        "manual_checks_settings": true,
        "manual_checks_urls": true,
        "monitoring_checks_view": true,
        "monitoring_checks_settings": true,
        "monitoring_checks_urls": true,
        "logs_view": true,
        "settings_view": true,
        "settings_add_urls": true,
        "settings_account_settings": true,
        "upgrade_account": true,
        "wizard_start": true,
        "only_frontpage": false,
        "plugin_view": true
    },
    "sync_url_types": [
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "posts",
            "post_type_name": "Posts"
        },
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "pages",
            "post_type_name": "Pages"
        },
        {
            "url_type_slug": "taxonomies",
            "url_type_name": "Taxonomies",
            "post_type_slug": "category",
            "post_type_name": "Category"
        }
    ],
    "auto_update_settings": {
        "auto_update_checks_enabled": false,
        "auto_update_checks_monday": true,
        "auto_update_checks_tuesday": true,
        "auto_update_checks_wednesday": true,
        "auto_update_checks_thursday": true,
        "auto_update_checks_friday": true,
        "auto_update_checks_saturday": false,
        "auto_update_checks_sunday": false,
        "auto_update_checks_from": null,
        "auto_update_checks_to": null,
        "auto_update_checks_emails": null,
        "allow_core_security_updates": true
    }
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "domain": "example.com",
    "last_seen_at": "2025-07-14T16:19:30Z",
    "plugin_version": "4.0.3",
    "manual_detection_group": "023c282c-6513-420a-a36b-a654312ab229",
    "auto_detection_group": "023c282c-6513-420a-a36b-a654312ab230",
    "is_multisite_subsite": false,
    "parent_multisite_website": null,
    "allowances": {
        "change_detections_view": true,
        "ai_rules_view": true,
        "manual_checks_view": true,
        "manual_checks_start": true,
        "manual_checks_settings": true,
        "manual_checks_urls": true,
        "monitoring_checks_view": true,
        "monitoring_checks_settings": true,
        "monitoring_checks_urls": true,
        "logs_view": true,
        "settings_view": true,
        "settings_add_urls": true,
        "settings_account_settings": true,
        "upgrade_account": true,
        "wizard_start": true,
        "only_frontpage": false,
        "plugin_view": true
    },
    "sync_url_types": [
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "posts",
            "post_type_name": "Posts"
        },
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "pages",
            "post_type_name": "Pages"
        },
        {
            "url_type_slug": "taxonomies",
            "url_type_name": "Taxonomies",
            "post_type_slug": "category",
            "post_type_name": "Category"
        }
    ],
    "auto_update_settings": {
        "auto_update_checks_enabled": false,
        "auto_update_checks_from": "13:37",
        "auto_update_checks_to": "13:42",
        "auto_update_checks_monday": true,
        "auto_update_checks_tuesday": true,
        "auto_update_checks_wednesday": true,
        "auto_update_checks_thursday": true,
        "auto_update_checks_friday": true,
        "auto_update_checks_saturday": false,
        "auto_update_checks_sunday": false,
        "auto_update_checks_emails": "mail@example.com",
        "allow_core_security_updates": true
    }
}
 

Request   

POST api/v2/websites

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

domain   string     

The domain of the Website. Example: example.com

manual_detection_group_id   string     

The ID of the Manual Detection Group. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

auto_detection_group_id   string     

The ID of the Auto Detection Group. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

managed_by   string  optional    

Example: native

Must be one of:
  • native
  • mainwp
parent_multisite_website_id   string  optional    

UUID of the multisite main Website if this Website is a subsite. Must belong to the same user and not itself be a subsite. Subsites inherit the Schedule from the main; only their own auto_update_checks_enabled takes effect. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

allowances   string[]  optional    

The allowances of the Website.

change_detections_view   boolean  optional    

Show the Change Detections page in the WP plugin. This field is required when allowances is present. Example: true

ai_rules_view   boolean  optional    

Show the AI Rules page in the WP plugin. This field is required when allowances is present. Example: true

manual_checks_view   boolean  optional    

Show the Manual Checks page. This field is required when allowances is present. Example: true

manual_checks_start   boolean  optional    

Allow user to start a manual checks workflow. This field is required when allowances is present. Example: true

manual_checks_settings   boolean  optional    

Allow user to edit the manual checks group settings. This field is required when allowances is present. Example: true

manual_checks_urls   boolean  optional    

Allow user to edit URL selection for manual checks. This field is required when allowances is present. Example: true

monitoring_checks_view   boolean  optional    

Show the Monitoring page. This field is required when allowances is present. Example: true

monitoring_checks_settings   boolean  optional    

Allow user to edit the monitoring group settings. This field is required when allowances is present. Example: true

monitoring_checks_urls   boolean  optional    

Allow user to edit URL selection for monitoring. This field is required when allowances is present. Example: true

logs_view   boolean  optional    

Show the Logs page. This field is required when allowances is present. Example: true

settings_view   boolean  optional    

Show the Settings page. This field is required when allowances is present. Example: true

settings_add_urls   boolean  optional    

Allow user to add URLs from the Settings page. This field is required when allowances is present. Example: true

settings_account_settings   boolean  optional    

Show account-level settings (token, email, upgrade link). This field is required when allowances is present. Example: true

upgrade_account   boolean  optional    

Show the upgrade-plan call-to-action. This field is required when allowances is present. Example: true

wizard_start   boolean  optional    

Show the onboarding wizard for new sites. This field is required when allowances is present. Example: true

only_frontpage   boolean  optional    

Restrict URL discovery to the homepage only (cheap-plan limit). This field is required when allowances is present. Example: false

plugin_view   boolean  optional    

Whether the WebChange Detector plugin shows its UI (admin menu and admin bar) in the WordPress admin of this Website. false hides it; URL sync and auto updates keep running. An absent key means true. This field is required when allowances is present. Example: true

sync_url_types   string[]  optional    

The sync url types of the Website.

url_type_slug   string  optional    

Internal slug for the URL type group (e.g. types, taxonomies, frontpage). This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: types

url_type_name   string  optional    

Human-readable label for the URL type group. This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: Post Types

post_type_slug   string  optional    

WordPress post-type or taxonomy slug. This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: posts

post_type_name   string  optional    

Human-readable label for the specific post type / taxonomy. This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: Posts

auto_update_settings   string[]  optional    

The auto update settings of the Website.

auto_update_checks_enabled   boolean  optional    

Run pre/post-update visual checks during WP auto-updates on this Website. Example: false

auto_update_checks_monday   boolean  optional    

Allow auto-updates on Mondays. Example: true

auto_update_checks_tuesday   boolean  optional    

Allow auto-updates on Tuesdays. Example: true

auto_update_checks_wednesday   boolean  optional    

Allow auto-updates on Wednesdays. Example: true

auto_update_checks_thursday   boolean  optional    

Allow auto-updates on Thursdays. Example: true

auto_update_checks_friday   boolean  optional    

Allow auto-updates on Fridays. Example: true

auto_update_checks_saturday   boolean  optional    

Allow auto-updates on Saturdays. Example: false

auto_update_checks_sunday   boolean  optional    

Allow auto-updates on Sundays. Example: false

auto_update_checks_emails   string[]  optional    

Must be a valid email address.

auto_update_checks_from   string  optional    

Start of the daily allowed time window in UTC (HH:mm). Must be a valid date in the format H:i. Example: 09:00

auto_update_checks_to   string  optional    

End of the daily allowed time window in UTC (HH:mm). Must be a valid date in the format H:i. Example: 17:00

allow_core_security_updates   boolean  optional    

Install WordPress core security releases (minor updates) immediately, even outside the Auto Update Checks timeframe. Defaults to true when absent. Example: true

Get Website

requires authentication

Retrieves a Website object identified by its ID.

On Multisite Subsites the returned auto_update_settings reflects the effective (inherited) values: Schedule fields (timeframe, weekdays, notification emails) come from the parent main Website, while auto_update_checks_enabled reflects this Subsite's own toggle. The is_multisite_subsite boolean and the nested parent_multisite_website object indicate the link.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request GET \
    --get "https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
        "manual_detection_group": "023c282c-6513-420a-a36b-a654312ab229",
        "auto_detection_group": "023c282c-6513-420a-a36b-a654312ab230",
        "domain": "example.com",
        "last_seen_at": "2025-07-14T16:19:30Z",
        "plugin_version": "4.0.3",
        "is_multisite_subsite": false,
        "parent_multisite_website": null,
        "sync_url_types": [
            {
                "url_type_slug": "types",
                "url_type_name": "Post Types",
                "post_type_slug": "posts",
                "post_type_name": "Posts"
            }
        ],
        "auto_update_settings": {
            "auto_update_checks_enabled": false,
            "auto_update_checks_from": "13:37",
            "auto_update_checks_to": "13:42",
            "auto_update_checks_monday": true,
            "auto_update_checks_tuesday": true,
            "auto_update_checks_wednesday": true,
            "auto_update_checks_thursday": true,
            "auto_update_checks_friday": true,
            "auto_update_checks_saturday": false,
            "auto_update_checks_sunday": false,
            "auto_update_checks_emails": "mail@example.com",
            "allow_core_security_updates": true
        },
        "allowances": {
            "change_detections_view": true,
            "ai_rules_view": true,
            "manual_checks_view": true,
            "manual_checks_start": true,
            "manual_checks_settings": true,
            "manual_checks_urls": true,
            "monitoring_checks_view": true,
            "monitoring_checks_settings": true,
            "monitoring_checks_urls": true,
            "logs_view": true,
            "settings_view": true,
            "settings_add_urls": true,
            "settings_account_settings": true,
            "upgrade_account": true,
            "wizard_start": true,
            "only_frontpage": false,
            "plugin_view": true
        }
    }
}
 

Example response (200, Multisite Subsite (auto_update_settings reflect effective inherited values from the parent main Website)):


{
    "data": {
        "id": "b3a9d2e5-2c64-4a16-8f1c-9e3b21f8a4e7",
        "manual_detection_group": "d4d2af0b-7a35-4b99-aa46-5c4f2bb9f1a1",
        "auto_detection_group": "d4d2af0b-7a35-4b99-aa46-5c4f2bb9f1a2",
        "domain": "shop.example.com",
        "last_seen_at": "2025-07-14T16:19:30Z",
        "plugin_version": "4.0.3",
        "is_multisite_subsite": true,
        "parent_multisite_website": {
            "id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
            "domain": "example.com"
        },
        "sync_url_types": [
            {
                "url_type_slug": "types",
                "url_type_name": "Post Types",
                "post_type_slug": "posts",
                "post_type_name": "Posts"
            }
        ],
        "auto_update_settings": {
            "auto_update_checks_enabled": true,
            "auto_update_checks_from": "13:37",
            "auto_update_checks_to": "13:42",
            "auto_update_checks_monday": true,
            "auto_update_checks_tuesday": true,
            "auto_update_checks_wednesday": true,
            "auto_update_checks_thursday": true,
            "auto_update_checks_friday": true,
            "auto_update_checks_saturday": false,
            "auto_update_checks_sunday": false,
            "auto_update_checks_emails": "mail@example.com",
            "allow_core_security_updates": true
        },
        "allowances": {
            "change_detections_view": true,
            "ai_rules_view": true,
            "manual_checks_view": true,
            "manual_checks_start": true,
            "manual_checks_settings": true,
            "manual_checks_urls": true,
            "monitoring_checks_view": true,
            "monitoring_checks_settings": true,
            "monitoring_checks_urls": true,
            "logs_view": true,
            "settings_view": true,
            "settings_add_urls": true,
            "settings_account_settings": true,
            "upgrade_account": true,
            "wizard_start": true,
            "only_frontpage": false,
            "plugin_view": true
        }
    }
}
 

Request   

GET api/v2/websites/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Website. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

Update Website

requires authentication

Updates the specified Website by setting the values of the parameters passed. Any parameters not provided will be left unchanged.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'parent_multisite_website_id' => '91e9c9fd-b86f-4269-a3f4-f02810167a6d',
            'allowances' => [
                'change_detections_view' => true, 'ai_rules_view' => true, 'manual_checks_view' => true, 'manual_checks_start' => true, 'manual_checks_settings' => true, 'manual_checks_urls' => true, 'monitoring_checks_view' => true, 'monitoring_checks_settings' => true, 'monitoring_checks_urls' => true, 'logs_view' => true,
                'settings_view' => true, 'settings_add_urls' => true, 'settings_account_settings' => true, 'upgrade_account' => true, 'wizard_start' => true, 'only_frontpage' => false, 'plugin_view' => true,
            ],
            'sync_url_types' => [
                ['url_type_slug' => 'types', 'url_type_name' => 'Post Types', 'post_type_slug' => 'posts', 'post_type_name' => 'Posts'],
                ['url_type_slug' => 'types', 'url_type_name' => 'Post Types', 'post_type_slug' => 'pages', 'post_type_name' => 'Pages'],
                ['url_type_slug' => 'taxonomies', 'url_type_name' => 'Taxonomies', 'post_type_slug' => 'category', 'post_type_name' => 'Category'],
            ],
            'auto_update_settings' => [
                'auto_update_checks_enabled' => false, 'auto_update_checks_monday' => true, 'auto_update_checks_tuesday' => true, 'auto_update_checks_wednesday' => true, 'auto_update_checks_thursday' => true, 'auto_update_checks_friday' => true, 'auto_update_checks_saturday' => false, 'auto_update_checks_sunday' => false, 'auto_update_checks_from' => null, 'auto_update_checks_to' => null,
                'auto_update_checks_emails' => null, 'allow_core_security_updates' => true,
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request PUT \
    "https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"parent_multisite_website_id\": \"91e9c9fd-b86f-4269-a3f4-f02810167a6d\",
    \"allowances\": {
        \"change_detections_view\": true,
        \"ai_rules_view\": true,
        \"manual_checks_view\": true,
        \"manual_checks_start\": true,
        \"manual_checks_settings\": true,
        \"manual_checks_urls\": true,
        \"monitoring_checks_view\": true,
        \"monitoring_checks_settings\": true,
        \"monitoring_checks_urls\": true,
        \"logs_view\": true,
        \"settings_view\": true,
        \"settings_add_urls\": true,
        \"settings_account_settings\": true,
        \"upgrade_account\": true,
        \"wizard_start\": true,
        \"only_frontpage\": false,
        \"plugin_view\": true
    },
    \"sync_url_types\": [
        {
            \"url_type_slug\": \"types\",
            \"url_type_name\": \"Post Types\",
            \"post_type_slug\": \"posts\",
            \"post_type_name\": \"Posts\"
        },
        {
            \"url_type_slug\": \"types\",
            \"url_type_name\": \"Post Types\",
            \"post_type_slug\": \"pages\",
            \"post_type_name\": \"Pages\"
        },
        {
            \"url_type_slug\": \"taxonomies\",
            \"url_type_name\": \"Taxonomies\",
            \"post_type_slug\": \"category\",
            \"post_type_name\": \"Category\"
        }
    ],
    \"auto_update_settings\": {
        \"auto_update_checks_enabled\": false,
        \"auto_update_checks_monday\": true,
        \"auto_update_checks_tuesday\": true,
        \"auto_update_checks_wednesday\": true,
        \"auto_update_checks_thursday\": true,
        \"auto_update_checks_friday\": true,
        \"auto_update_checks_saturday\": false,
        \"auto_update_checks_sunday\": false,
        \"auto_update_checks_from\": null,
        \"auto_update_checks_to\": null,
        \"auto_update_checks_emails\": null,
        \"allow_core_security_updates\": true
    }
}"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "parent_multisite_website_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "allowances": {
        "change_detections_view": true,
        "ai_rules_view": true,
        "manual_checks_view": true,
        "manual_checks_start": true,
        "manual_checks_settings": true,
        "manual_checks_urls": true,
        "monitoring_checks_view": true,
        "monitoring_checks_settings": true,
        "monitoring_checks_urls": true,
        "logs_view": true,
        "settings_view": true,
        "settings_add_urls": true,
        "settings_account_settings": true,
        "upgrade_account": true,
        "wizard_start": true,
        "only_frontpage": false,
        "plugin_view": true
    },
    "sync_url_types": [
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "posts",
            "post_type_name": "Posts"
        },
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "pages",
            "post_type_name": "Pages"
        },
        {
            "url_type_slug": "taxonomies",
            "url_type_name": "Taxonomies",
            "post_type_slug": "category",
            "post_type_name": "Category"
        }
    ],
    "auto_update_settings": {
        "auto_update_checks_enabled": false,
        "auto_update_checks_monday": true,
        "auto_update_checks_tuesday": true,
        "auto_update_checks_wednesday": true,
        "auto_update_checks_thursday": true,
        "auto_update_checks_friday": true,
        "auto_update_checks_saturday": false,
        "auto_update_checks_sunday": false,
        "auto_update_checks_from": null,
        "auto_update_checks_to": null,
        "auto_update_checks_emails": null,
        "allow_core_security_updates": true
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d'
payload = {
    "parent_multisite_website_id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "allowances": {
        "change_detections_view": true,
        "ai_rules_view": true,
        "manual_checks_view": true,
        "manual_checks_start": true,
        "manual_checks_settings": true,
        "manual_checks_urls": true,
        "monitoring_checks_view": true,
        "monitoring_checks_settings": true,
        "monitoring_checks_urls": true,
        "logs_view": true,
        "settings_view": true,
        "settings_add_urls": true,
        "settings_account_settings": true,
        "upgrade_account": true,
        "wizard_start": true,
        "only_frontpage": false,
        "plugin_view": true
    },
    "sync_url_types": [
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "posts",
            "post_type_name": "Posts"
        },
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "pages",
            "post_type_name": "Pages"
        },
        {
            "url_type_slug": "taxonomies",
            "url_type_name": "Taxonomies",
            "post_type_slug": "category",
            "post_type_name": "Category"
        }
    ],
    "auto_update_settings": {
        "auto_update_checks_enabled": false,
        "auto_update_checks_monday": true,
        "auto_update_checks_tuesday": true,
        "auto_update_checks_wednesday": true,
        "auto_update_checks_thursday": true,
        "auto_update_checks_friday": true,
        "auto_update_checks_saturday": false,
        "auto_update_checks_sunday": false,
        "auto_update_checks_from": null,
        "auto_update_checks_to": null,
        "auto_update_checks_emails": null,
        "allow_core_security_updates": true
    }
}
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PUT', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "id": "91e9c9fd-b86f-4269-a3f4-f02810167a6d",
    "domain": "example.com",
    "last_seen_at": "2025-07-14T16:19:30Z",
    "plugin_version": "4.0.3",
    "manual_detection_group": "023c282c-6513-420a-a36b-a654312ab229",
    "auto_detection_group": "023c282c-6513-420a-a36b-a654312ab230",
    "is_multisite_subsite": false,
    "parent_multisite_website": null,
    "allowances": {
        "change_detections_view": true,
        "ai_rules_view": true,
        "manual_checks_view": true,
        "manual_checks_start": true,
        "manual_checks_settings": true,
        "manual_checks_urls": true,
        "monitoring_checks_view": true,
        "monitoring_checks_settings": true,
        "monitoring_checks_urls": true,
        "logs_view": true,
        "settings_view": true,
        "settings_add_urls": true,
        "settings_account_settings": true,
        "upgrade_account": true,
        "wizard_start": true,
        "only_frontpage": false,
        "plugin_view": true
    },
    "sync_url_types": [
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "posts",
            "post_type_name": "Posts"
        },
        {
            "url_type_slug": "types",
            "url_type_name": "Post Types",
            "post_type_slug": "pages",
            "post_type_name": "Pages"
        }
    ],
    "auto_update_settings": {
        "auto_update_checks_enabled": false,
        "auto_update_checks_from": "13:37",
        "auto_update_checks_to": "13:42",
        "auto_update_checks_monday": true,
        "auto_update_checks_tuesday": true,
        "auto_update_checks_wednesday": true,
        "auto_update_checks_thursday": true,
        "auto_update_checks_friday": true,
        "auto_update_checks_saturday": false,
        "auto_update_checks_sunday": false,
        "auto_update_checks_emails": "mail@example.com",
        "allow_core_security_updates": true
    }
}
 

Request   

PUT api/v2/websites/{id}

PATCH api/v2/websites/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Website. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

Body Parameters

parent_multisite_website_id   string  optional    

UUID of the multisite main Website. Pass null to detach. On subsites only auto_update_checks_enabled takes effect in auto_update_settings; Schedule fields are inherited from the main at read time. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d

allowances   string[]  optional    

The allowances of the Website.

change_detections_view   boolean  optional    

Show the Change Detections page in the WP plugin. This field is required when allowances is present. Example: true

ai_rules_view   boolean  optional    

Show the AI Rules page in the WP plugin. This field is required when allowances is present. Example: true

manual_checks_view   boolean  optional    

Show the Manual Checks page. This field is required when allowances is present. Example: true

manual_checks_start   boolean  optional    

Allow user to start a manual checks workflow. This field is required when allowances is present. Example: true

manual_checks_settings   boolean  optional    

Allow user to edit the manual checks group settings. This field is required when allowances is present. Example: true

manual_checks_urls   boolean  optional    

Allow user to edit URL selection for manual checks. This field is required when allowances is present. Example: true

monitoring_checks_view   boolean  optional    

Show the Monitoring page. This field is required when allowances is present. Example: true

monitoring_checks_settings   boolean  optional    

Allow user to edit the monitoring group settings. This field is required when allowances is present. Example: true

monitoring_checks_urls   boolean  optional    

Allow user to edit URL selection for monitoring. This field is required when allowances is present. Example: true

logs_view   boolean  optional    

Show the Logs page. This field is required when allowances is present. Example: true

settings_view   boolean  optional    

Show the Settings page. This field is required when allowances is present. Example: true

settings_add_urls   boolean  optional    

Allow user to add URLs from the Settings page. This field is required when allowances is present. Example: true

settings_account_settings   boolean  optional    

Show account-level settings (token, email, upgrade link). This field is required when allowances is present. Example: true

upgrade_account   boolean  optional    

Show the upgrade-plan call-to-action. This field is required when allowances is present. Example: true

wizard_start   boolean  optional    

Show the onboarding wizard for new sites. This field is required when allowances is present. Example: true

only_frontpage   boolean  optional    

Restrict URL discovery to the homepage only (cheap-plan limit). This field is required when allowances is present. Example: false

plugin_view   boolean  optional    

Whether the WebChange Detector plugin shows its UI (admin menu and admin bar) in the WordPress admin of this Website. false hides it; URL sync and auto updates keep running. An absent key means true. This field is required when allowances is present. Example: true

sync_url_types   string[]  optional    

The sync url types of the Website.

url_type_slug   string  optional    

Internal slug for the URL type group (e.g. types, taxonomies, frontpage). This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: types

url_type_name   string  optional    

Human-readable label for the URL type group. This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: Post Types

post_type_slug   string  optional    

WordPress post-type or taxonomy slug. This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: posts

post_type_name   string  optional    

Human-readable label for the specific post type / taxonomy. This field is required when sync_url_types is present. Must not be greater than 255 characters. Example: Posts

auto_update_settings   string[]  optional    

The auto update settings of the Website.

auto_update_checks_enabled   boolean  optional    

Run pre/post-update visual checks during WP auto-updates on this Website. Example: false

auto_update_checks_monday   boolean  optional    

Allow auto-updates on Mondays. Example: true

auto_update_checks_tuesday   boolean  optional    

Allow auto-updates on Tuesdays. Example: true

auto_update_checks_wednesday   boolean  optional    

Allow auto-updates on Wednesdays. Example: true

auto_update_checks_thursday   boolean  optional    

Allow auto-updates on Thursdays. Example: true

auto_update_checks_friday   boolean  optional    

Allow auto-updates on Fridays. Example: true

auto_update_checks_saturday   boolean  optional    

Allow auto-updates on Saturdays. Example: false

auto_update_checks_sunday   boolean  optional    

Allow auto-updates on Sundays. Example: false

auto_update_checks_emails   string[]  optional    

Must be a valid email address.

auto_update_checks_from   string  optional    

Start of the daily allowed time window in UTC (HH:mm). Must be a valid date in the format H:i. Example: 09:00

auto_update_checks_to   string  optional    

End of the daily allowed time window in UTC (HH:mm). Must be a valid date in the format H:i. Example: 17:00

allow_core_security_updates   boolean  optional    

Install WordPress core security releases (minor updates) immediately, even outside the Auto Update Checks timeframe. Defaults to true when absent. Example: true

domain   string  optional    

Delete Website

requires authentication

Soft-deletes a Website together with both of its detection groups (monitoring and on-demand). The underlying screenshot/comparison history is retained so check-usage counting stays correct.

Example request:
$client = new \GuzzleHttp\Client();
$url = 'https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl --request DELETE \
    "https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
import requests
import json

url = 'https://api.webchangedetector.com/api/v2/websites/91e9c9fd-b86f-4269-a3f4-f02810167a6d'
headers = {
  'Authorization': 'Bearer {YOUR_TOKEN}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "message": "{ID} deleted"
}
 

Request   

DELETE api/v2/websites/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the Website. Example: 91e9c9fd-b86f-4269-a3f4-f02810167a6d