API
The REST API v1 manages the same page monitors as the dashboard: list them, create one, read one, change one, delete one, and read its change history. Every address is under https://pagechangedetector.com/api/v1 and answers JSON. API access is included on Pro and Business; see pricing.
Authentication
Create a key in your dashboard. It is shown once, so copy it then. Send it on every request as Authorization: Bearer <key>. The x-api-key header carries the same key if a client cannot set an Authorization header.
Only the hash of a key is stored. A missing key, a key that was never issued and a revoked key all answer the same 401, so a guess learns nothing. Keys belong to one account: they see that account's monitors and no one else's.
Rate limit
60 requests a minute per key, in fixed one minute windows. The window is per key, so one integration cannot spend another's budget. Past the limit the answer is 429 with a retryAfterSeconds field. A 429 is a request that was refused before it was served: it is never counted, and the monthly API call count in the dashboard does not move for it.
Endpoints
GET /api/v1/monitorslists the account's monitors.POST /api/v1/monitorscreates one.GET /api/v1/monitors/{id}reads one.PATCH /api/v1/monitors/{id}changes the fields the body names.DELETE /api/v1/monitors/{id}removes one.GET /api/v1/monitors/{id}/changesreads its change history, newest first.
A monitor that belongs to another account, and a monitor that does not exist, both answer 404: one account learns nothing about another's monitors.
List monitors
curl https://pagechangedetector.com/api/v1/monitors \ -H "Authorization: Bearer sk_your_key"
Response, 200:
{
"monitors": [
{
"id": "clz8k4m2p0000abcd1234efgh",
"userId": "clz8k4m2p0000ijkl5678mnop",
"url": "https://example.com/pricing",
"host": "example.com",
"mode": "PAGE",
"selector": null,
"intervalMin": 60,
"render": false,
"trigger": "ANY",
"triggerText": null,
"ignoreRules": [],
"status": "ACTIVE",
"lastStatus": "unchanged",
"lastCheckedAt": "2026-10-15T09:00:00.000Z",
"nextCheckAt": "2026-10-15T10:00:00.000Z",
"failCount": 0,
"lastHash": "9f2c41d0e6b7a8c5f4e3d2b1a09c8e7d6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c",
"createdAt": "2026-10-01T08:30:00.000Z"
}
]
}lastStatus is the result of the last check: ok, unchanged, changed, selector-missing, blocked or error. It is null before the first check. Timestamps are ISO 8601 in UTC.
Create a monitor
curl -X POST https://pagechangedetector.com/api/v1/monitors \
-H "Authorization: Bearer sk_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"intervalMin": 60,
"mode": "SELECTOR",
"selector": "main .price"
}'The body is a JSON object. url (an absolute http or https address) and intervalMin (one of 10, 15, 30, 60, 360, 1440) are required. Optional fields are mode (PAGE by default, or SELECTOR), selector (required by selector mode), render (browser rendering, on the plans that include it), trigger (ANY, APPEARS or DISAPPEARS), triggerText and ignoreRules (an array of strings). The plan caps monitors, the per-host count, browser rendering and the fastest interval, so the server refuses a monitor the plan does not allow with a 400.
Response, 201, the created monitor:
{
"monitor": {
"id": "clz8k4m2p0000abcd1234efgh",
"url": "https://example.com/pricing",
"mode": "SELECTOR",
"selector": "main .price",
"intervalMin": 60,
"render": false,
"trigger": "ANY",
"triggerText": null,
"ignoreRules": [],
"status": "ACTIVE",
"createdAt": "2026-10-15T12:00:00.000Z"
}
}Read one monitor
curl https://pagechangedetector.com/api/v1/monitors/clz8k4m2p0000abcd1234efgh \ -H "Authorization: Bearer sk_your_key"
Response, 200, { "monitor": { ... } } in the same shape as a list entry.
Change a monitor
curl -X PATCH https://pagechangedetector.com/api/v1/monitors/clz8k4m2p0000abcd1234efgh \
-H "Authorization: Bearer sk_your_key" \
-H "Content-Type: application/json" \
-d '{ "intervalMin": 30, "status": "PAUSED" }'Only the fields in the body change; everything else stays as it was. The changeable fields are the create fields plus status (ACTIVE or PAUSED). A faster interval than the plan allows, or any other refused value, answers 400. Response, 200, { "monitor": { ... } }.
Delete a monitor
curl -X DELETE https://pagechangedetector.com/api/v1/monitors/clz8k4m2p0000abcd1234efgh \ -H "Authorization: Bearer sk_your_key"
Response, 200:
{
"ok": true
}Change history
curl "https://pagechangedetector.com/api/v1/monitors/clz8k4m2p0000abcd1234efgh/changes?limit=20" \ -H "Authorization: Bearer sk_your_key"
Newest first. limit is optional: the default is 20 and the most one read returns is 100. added and removed count the lines that entered and left, summary is the stored human-readable diff, and alerted records whether the email went out.
Response, 200:
{
"changes": [
{
"id": "clz8k4m2p0000qrst9012uvwx",
"monitorId": "clz8k4m2p0000abcd1234efgh",
"fromSnapshotId": "clz8k4m2p0000yzab3456cdef",
"toSnapshotId": "clz8k4m2p0000ghij7890klmn",
"summary": "2 lines added, 1 line removed",
"added": 2,
"removed": 1,
"alerted": true,
"createdAt": "2026-10-14T07:15:00.000Z"
}
]
}Errors
An error is always a JSON object with a plain error sentence, shown unchanged:
{
"error": "That page does not exist."
}400the body is missing a required field or names a value the plan refuses, such as an interval that is not on the menu or is faster than the plan allows.401the key is missing, unknown or revoked.403the account's plan does not include the API: The REST API is included on Pro and Business.404no such address, or the monitor is not the account's.405the method is not allowed on that address.429the key passed 60 requests in the current minute. CarriesretryAfterSeconds.