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

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."
}