How to Monitor Website Changes with an API
Updated October 28, 2026

Quick answer: Use the Visualping API to create and manage page monitors, retrieve detected changes, and configure notifications. API access is included on every plan. Your account's page limit, check allowance, available intervals, and notification features still apply.
Illustration of a monitoring workflow. Native Slack notifications require Business; email and custom webhooks are available on every plan.
Use the API when monitoring needs to fit into your own software: a script that creates monitors from an approved URL list, a service that retrieves change history, or a webhook receiver that routes alerts to another application.
This guide covers API keys, workspace scope, monitor creation, AI-powered change summaries, notifications, and pagination. The examples use the published API contract and were checked offline against its schemas and mocked responses. They have not been executed against a live account.
Choose the API for programmatic workflows
The Visualping dashboard supports bulk creation and editing on every plan, within the plan's page limit. Use its Bulk tab when you want to add a list of URLs through the interface. The API is useful when you need those operations inside a script or application. See current plan limits before sizing either approach.
For example, a script can take an approved URL list from your system, check for existing monitors, and create the missing ones. A webhook receiver can pass a detected change to a review queue. Those are integration choices; a monitor count alone does not determine which approach you need.
Get your API key
Sign in and open Developer settings. On Business accounts, an organization Admin must create and manage keys.
- Click Create API Key and give it a recognizable name.
- Where scope options are available, choose the organization or the specific workspace the integration needs. Choose Read for retrieval, or Write for creating and changing monitors.
- Choose an expiration and save the key in your secret manager. The secret is displayed only once.
The API key guide describes these controls and the five-active-key limit. Requests use a Bearer token:
Authorization: Bearer YOUR_API_KEY
Keep the actual key out of source code, screenshots, logs, and AI prompts. The Python examples read VISUALPING_API_KEY from the process environment; supply it through your local secret-handling workflow. A key's access and workspace scope must cover the operation you request.
Example of the Developer settings screen. Account roles determine access to these controls.
Your first monitor: one POST request
The API calls monitors jobs. Install the Python requests package, set your key in the environment, and use the shared setup below for the following examples.
GET /describe-user lists accessible workspaces. The code selects a single available workspace automatically. If several are returned, set VISUALPING_WORKSPACE_ID to the intended ID and rerun. Business requests require that ID in create payloads and list/detail query parameters. Personal-account requests can omit it when no Business workspace is being used.
import os
import requests
JOBS_URL = "https://job.api.visualping.io/v2/jobs"
ACCOUNT_URL = "https://account.api.visualping.io/describe-user"
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {os.environ['VISUALPING_API_KEY']}",
"Content-Type": "application/json",
})
def request_json(method, url, **kwargs):
response = session.request(method, url, timeout=(10, 30), **kwargs)
response.raise_for_status()
return response.json()
profile = request_json("GET", ACCOUNT_URL)
workspaces = profile["workspaces"]
requested = os.getenv("VISUALPING_WORKSPACE_ID")
if requested:
workspace_id = int(requested)
if workspace_id not in {w["id"] for w in workspaces}:
raise ValueError("The requested workspace is not in describe-user.")
elif len(workspaces) == 1:
workspace_id = workspaces[0]["id"]
elif len(workspaces) > 1:
choices = [(w["id"], w.get("name", "")) for w in workspaces]
raise ValueError(f"Set VISUALPING_WORKSPACE_ID to a workspace: {choices}")
elif profile.get("organisation"):
raise ValueError("No accessible Business workspace was returned; check key scope.")
else:
workspace_id = None
scope = {"workspaceId": workspace_id} if workspace_id is not None else {}
def job_payload(url, description, interval="1440"):
return {
**scope,
"url": url,
"description": description,
"interval": interval,
"active": True,
"trigger": "1",
"mode": "ALL",
"target_device": "4",
"wait_time": 0,
}
def create_job(payload, execute=False):
if not execute:
# Deliberately omit credentials and notification URLs from preview output.
print("Preview:", {k: payload[k] for k in
("url", "description", "interval", "active")})
return None
result = request_json("POST", JOBS_URL, json=payload)
public_id = result["jobid"]
if not isinstance(public_id, str) or not public_id.isdecimal():
raise ValueError("Unexpected jobid; inspect the monitor list before retrying.")
return int(public_id)
payload = job_payload("https://example.com/pricing", "Pricing page")
create_job(payload) # Preview only. Replace the example URL before creating.
# After reviewing scope, settings and plan capacity:
# job_id = create_job(payload, execute=True)
A successful create response documents jobid as the new monitor's public ID. Its separate id field is for internal use. Keep the numeric jobid for subsequent /v2/jobs/{jobId} requests. The creation acknowledgement does not prove the target page was captured successfully; inspect the monitor preview and first result.
interval is a string containing minutes: "60" is hourly, "360" is every six hours, "1440" is daily, and "10080" is weekly. Available intervals depend on the plan. One monitor checked hourly for 30 uninterrupted days uses 720 scheduled checks; daily checks use 30. Add any other checks and monitors when estimating your allowance.
The request helper stops on an HTTP error or connection failure. After an uncertain create outcome, inspect the monitor list before retrying so you do not create a duplicate. Handle throttling according to the API response and current documentation; the example does not assume a universal requests-per-second limit.
Narrow the focus to one page section
The create schema exposes an xpath field for selecting a page element. The schema describes target_device="1" as desktop capture of a specific area; the earlier "4" requests full-page desktop capture. This example requests an element by ID and creates the monitor inactive so you can review its captured area and settings before activating it:
section_payload = job_payload(
"https://example.com/products", "Product availability section"
)
section_payload.update({
"xpath": "//*[@id='inventory-status']",
"target_device": "1",
"active": False,
})
create_job(section_payload) # Preview only; use execute=True when ready.
Replace the example XPath with one that matches your page. Confirm the intended content is visible in the monitor preview. A missing element, page redesign, or required interaction can affect what is captured. The area-selection guide explains the current interface.
Pages that need a click or login first
Actions run before capture on each check. Configure and test cookie-banner clicks, form entries, waits, and login steps in the dashboard first. Review any preview warnings before saving.
The API exposes these settings as preactions, but its published JSON schema leaves the individual action objects unspecified. Start with a working dashboard monitor, review its saved Actions in the dashboard, and confirm the current action format with the API documentation or support. Enter login secrets through the monitor's settings, and keep any retrieved sensitive configuration out of logs and AI prompts.
Tell the AI what matters
A detected change can include an AI Summary and a binary Important classification. The detail schema marks both fields as optional, so integrations must handle missing values. A custom summalyzer prompt guides that classification. The notification setting onlyImportantAlerts controls whether enabled channels receive only changes classified as important.
terms_payload = job_payload(
"https://example.com/terms", "Vendor terms for review"
)
terms_payload["summalyzer"] = {
"importantDefinitionType": "custom",
"importantDefinition":
"Changes to liability clauses, data handling policies, or pricing terms",
}
terms_payload["notification"] = {
"enableEmailAlert": True,
"enableSmsAlert": False,
"onlyImportantAlerts": True,
"config": {},
}
create_job(terms_payload) # Preview only.
Entries in the job-detail response's changes array can include englishSummary and analyzerAlertTriggered. Here is an illustrative excerpt:
{
"changes": [
{
"englishSummary": "The vendor changed its data retention clause.",
"analyzerAlertTriggered": true
}
]
}
Treat the flag as the AI's classification and review the changed page before acting. A prompt does not guarantee that every intended change will be classified correctly. See important-alert settings for prompt and notification controls.
Send alerts where your team actually looks
Email and custom webhooks are available on every plan. Native Slack, Microsoft Teams, and Google Sheets notifications require Business. A custom webhook can send an alert to your own receiver, where you implement any additional routing. That receiver must accept and handle Visualping's payload; a third-party endpoint may expect a different format.
Slack example
For a Business account with Slack notifications configured, add an explicit notification object. Keep the Slack webhook URL in a secret environment variable as well:
slack_payload = job_payload("https://example.com/pricing", "Pricing alerts")
slack_payload["summalyzer"] = {
"importantDefinitionType": "custom",
"importantDefinition": "Listed price changes, new plans, or removed features",
}
slack_payload["notification"] = {
"enableEmailAlert": False,
"enableSmsAlert": False,
"onlyImportantAlerts": True,
"config": {
"slack": {
"url": os.environ["VISUALPING_SLACK_WEBHOOK_URL"],
"active": True,
"notificationType": "slack",
"channels": ["#competitive-intel"],
}
},
}
create_job(slack_payload) # Preview only.
Custom webhook endpoint
For an all-plan option, use your own webhook receiver. This example enables webhook notifications and disables email and SMS:
webhook_payload = job_payload("https://example.com/pricing", "Webhook example")
webhook_payload["notification"] = {
"enableEmailAlert": False,
"enableSmsAlert": False,
"onlyImportantAlerts": False,
"config": {
"webhook": {
"url": os.environ["VISUALPING_WEBHOOK_URL"],
"active": True,
"notificationType": "webhook",
}
},
}
create_job(webhook_payload) # Preview only.
Use the webhook payload guide when writing your receiver. Webhook fields differ from job-detail fields: the guide describes summarizer and an important string with values "true" or "false". Optional fields depend on the event and configuration.
A receiver could log change alerts, build a digest, or submit a record to another application. Test your receiver with a controlled event before relying on it.
Create monitors from a URL list
Use this paginated helper before creating a batch. The OpenAPI contract starts pageIndex at 0 and includes id_asc in the sortBy options. The helper reads all matching pages in the selected scope, in ID order:
def iter_jobs(**filters):
page_index = 0
while True:
params = {**scope, **filters, "mode": "normal", "pageIndex": page_index,
"pageSize": 100, "sortBy": "id_asc"}
data = request_json("GET", JOBS_URL, params=params)
jobs = data["jobs"]
total_pages = data["totalPages"]
if not isinstance(jobs, list) or not isinstance(total_pages, int):
raise ValueError("Unexpected list response; inspect the API contract.")
for job in jobs:
yield job
page_index += 1
if page_index >= total_pages:
break
Avoid changing the monitor list during pagination. Concurrent additions or removals can change the results between page requests.
The following example skips exact URLs already present in the selected scope, including paused monitors, and deduplicates its input list. It previews by default:
EXECUTE = False
urls = [
"https://example.com/pricing",
"https://example.com/products",
"https://example.com/news",
]
existing_urls = {job["url"] for job in iter_jobs()}
for url in dict.fromkeys(urls):
if url in existing_urls:
print("Skip existing URL:", url)
continue
new_payload = job_payload(url, "Website monitoring", interval="1440")
job_id = create_job(new_payload, execute=EXECUTE)
if job_id is not None:
existing_urls.add(url)
print("Created job:", job_id)
Replace the example URLs and review the list before setting EXECUTE = True. This exact-URL check does not identify different URLs for the same page, or distinguish multiple configurations of one URL. It is suitable for this one-monitor-per-URL example.
Budget both page slots and checks. As a planning example, 200 monitors checked daily for 30 days require 200 page slots and 6,000 scheduled checks. The same 200 monitors checked hourly require 144,000 scheduled checks. Both calculations assume continuous scheduling and exclude additional checks. Bulk URL monitoring describes the dashboard alternative.
Illustration of creating monitors from a URL list. The example script sends requests sequentially.
Manage monitors programmatically
The API supports creating, listing, reading, updating, and deleting monitors. A Write key is needed for mutations. Review the target monitor and workspace before changing or deleting it.
Pull all active monitors
Reuse the paginated helper above with the active filter:
for job in iter_jobs(activeFilter=[1]):
print(job["id"], job["url"], job.get("description", ""))
List results expose an integer id. The detail example below uses the numeric jobid saved from the create response; that response's separate id is for internal use.
Read a monitor's change history
Set VISUALPING_JOB_ID to the numeric jobid saved from a successful create response:
job_id = int(os.environ["VISUALPING_JOB_ID"])
data = request_json("GET", f"{JOBS_URL}/{job_id}", params=scope)
for change in data["changes"]:
important = change.get("analyzerAlertTriggered")
flag = "IMPORTANT" if important is True else (
"REGULAR" if important is False else "UNCLASSIFIED"
)
print(flag, change.get("created", ""), change.get("englishSummary", ""))
The documented job-detail response places changes and history at the top level. history represents returned check records; changes contains detected changes. A missing importance value should not be treated as proof that a change was unimportant. The response may not include every past check record; do not treat it as a permanent archive.
Find monitors with recent important changes
from datetime import datetime, timedelta, timezone
since = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat(timespec="milliseconds")
for job in iter_jobs(
eventFilter="changedImportant",
dateFilter="since_custom_date",
dateFilterStart=since,
):
print(job["id"], job["url"])
This lists matching monitors in the selected scope. Review their change history in the dashboard, or through the detail endpoint for monitors whose create jobid you saved.
What if you do not write code?
An AI coding assistant can help draft a script. Give it the current API documentation and the workflow you want, then review the generated code and test it with mocked responses before connecting an account.
For example:
Write a Python script using the published Visualping API contract. Read the API key from
VISUALPING_API_KEY, resolve the intended workspace throughdescribe-user, and preview daily monitors for the URLs I provide. Include pagination, a check-budget estimate, and explicit notification settings. Keep creation disabled until I enable it. Do not print secrets or automatically retry uncertain create requests.
Provide URLs and alert criteria separately. Keep API keys, login credentials, and webhook secrets in your environment or secret manager. An assistant's generated code still needs review for account scope, required fields, response handling, and plan limits.
To manage monitors through a connected AI client, see the Visualping MCP setup guide. That connection uses Visualping sign-in; keep REST API keys in your own integration environment.
Five API workflow examples
The following examples show how you could organize a monitoring workflow.
Competitive intelligence
Create monitors for approved competitor pricing, product, or messaging pages. Use a specific importance prompt, then route detected changes for review. See competitor monitoring for page-selection examples.
Compliance and regulatory review
Monitor relevant policy, filing, or regulator pages and ask the AI to highlight defined subjects. Have the responsible person review the source before making a compliance decision. Compliance monitoring can support that review process; an alert does not establish compliance.
E-commerce pricing
Monitor a product page's displayed price or availability, and verify the selected variant and region in the preview. A webhook receiver can log changes for review. See competitor pricing data for planning considerations.
Financial research
Monitor SEC filing pages or investor-relations announcements relevant to a research question. Route detected changes to an analyst for interpretation. Page changes alone do not establish their market impact.
API dependency tracking
Monitor a vendor's changelog or documentation page for deprecation notices and other content changes. Feed relevant alerts into an engineering review queue. Changelog monitoring supplements the team's testing and release process.
Advanced API features
Run checks during business hours only
An advanced_schedule restricts checks to selected days and hours. The scheduling guide covers Business and other plans: Business users can set the workspace timezone explicitly; other plans inherit the user's timezone. This example requests Monday through Friday, from 9:00 to 18:00. Confirm the timezone and saved schedule in the dashboard:
scheduled_payload = job_payload("https://example.com/news", "Weekday news", "60")
scheduled_payload["advanced_schedule"] = {
"start_time": 9,
"stop_time": 18,
"active_days": [1, 2, 3, 4, 5],
}
create_job(scheduled_payload) # Preview only.
Restricting scheduled hours changes check usage. It does not change which page content is relevant to your alert prompt.
Organize with labels
Business users can organize monitors with labels. Use existing label IDs from your organization in labelIds, then pass those IDs through labelsFilter when listing. The example IDs below are placeholders:
labeled_payload = job_payload("https://example.com/pricing", "Pricing review")
labeled_payload["labelIds"] = [101, 205]
create_job(labeled_payload) # Replace IDs and review before execution.
for job in iter_jobs(labelsFilter=[101]):
print(job["id"], job["url"])
Search across monitors in your scope
fullTextSearchFilter matches a substring in URLs or descriptions. Use it with pagination:
for job in iter_jobs(fullTextSearchFilter="pricing"):
print(job["id"], job["url"])
From setup to working automation
Create a Visualping account, choose an authorized account or workspace, and test a monitor on a page you intend to track. Review its preview, schedule, and alert settings before expanding the URL list. Keep the current API documentation alongside your integration code.
Frequently asked questions
How do I monitor a website for changes automatically?
Create a job with the target URL, supported interval, and notification settings. Include the workspace ID for Business scope. Review the first capture and keep an eye on monitor health; page access and site changes can affect later checks.
What types of website changes can the API detect?
Use ALL, the default AI Mode, for new examples in this guide. It compares page content and can produce a summary and importance classification for detected changes. Style-only changes such as a CSS color change do not trigger AI Mode alerts. Confirm the relevant page area in the preview.
Do I need coding experience to use a website monitoring API?
You need enough understanding to review the requests and handle errors. An AI coding assistant can help draft code, and the examples above show the request structure. For interface-based setup, the dashboard supports individual and bulk monitor creation.
How many websites can I monitor through the API?
Your plan limits active pages, monthly checks, and the shortest available interval. API access does not remove those limits. Consult the plan table and your account's current usage before creating a batch.
Can I get website change alerts in Slack or Microsoft Teams?
Yes, native Slack and Microsoft Teams notifications are Business features. Custom webhooks are available on every plan. With a custom receiver, you are responsible for translating and routing the payload to the destination service.
What is the difference between the API and the Visualping dashboard?
The dashboard provides monitor setup and management, including bulk creation and editing. The API provides programmatic operations over the same monitors, within your key's scope and permissions. Choose the interface that fits the workflow you need to maintain.
Want to monitor web changes that impact your business?
Monitor selected pages and receive alerts when detected changes meet your settings.
Eric Do Couto
Eric Do Couto is the Head of Marketing at Visualping. He leads content strategy, growth operations, and brand positioning for website change detection.
More articles by Eric Do Couto