{"openapi":"3.1.0","info":{"title":"Watchmint API","version":"1.0.0","description":"Know when a web page changes. Watchmint checks the pages you care about on a schedule and sends a signed webhook with exactly what changed: the text, the look, or both.","contact":{"email":"support@watchmint.plxi.us","url":"https://watchmint.plxi.us/contact"},"termsOfService":"https://watchmint.plxi.us/terms"},"externalDocs":{"url":"https://watchmint.plxi.us/docs"},"servers":[{"url":"https://api-watchmint.plxi.us"}],"security":[{"apiKey":[]}],"paths":{"/v1/diff/text":{"post":{"operationId":"diffText","summary":"Compare two texts","description":"Compare two versions of a text (two copies of a page you kept, two drafts) the way a Watchmint check compares a page with its last version. Spacing and blank lines don't count, and anything an `ignore_patterns` regular expression matches is taken out of both first (dates, counters). Send `before` and `after` (up to 200,000 characters each). Returns `changed`, a one-line `summary`, counts of added and removed lines and words, the first 50 added and removed lines, and a unified `diff`.","x-credits":1,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-diff-text"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"before":{"type":"string","maxLength":200000,"description":"The earlier version."},"after":{"type":"string","maxLength":200000,"description":"The later version."},"ignore_patterns":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Regular expressions whose matches are left out of both texts, e.g. \"Updated \\\\d+ minutes ago\"."},"context":{"type":"integer","minimum":0,"maximum":10,"default":3,"description":"Unchanged lines shown around each change in the diff."}},"required":["before","after"],"additionalProperties":false},"example":{"before":"Pro plan: $49/month\nFree trial: 14 days\nSupport: email","after":"Pro plan: $59/month\nFree trial: 14 days\nSupport: email and chat"}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"changed":{"type":"boolean"},"summary":{"type":"string"},"added_lines":{"type":"integer"},"removed_lines":{"type":"integer"},"added_words":{"type":"integer"},"removed_words":{"type":"integer"},"added":{"type":"array","items":{"type":"string"}},"removed":{"type":"array","items":{"type":"string"}},"diff":{"type":"string"},"diff_truncated":{"type":"boolean"},"credits":{"type":"integer","description":"Credits this call was charged."}},"required":["changed","summary","added_lines","removed_lines","added_words","removed_words","added","removed","diff","diff_truncated","credits"]},"example":{"changed":true,"summary":"2 lines added, 2 removed","added_lines":2,"removed_lines":2,"added_words":3,"removed_words":1,"added":["Pro plan: $59/month","Support: email and chat"],"removed":["Pro plan: $49/month","Support: email"],"diff":"--- before\n+++ after\n@@ -1,3 +1,3 @@\n-Pro plan: $49/month\n+Pro plan: $59/month\n Free trial: 14 days\n-Support: email\n+Support: email and chat\n","diff_truncated":false,"credits":1}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/pages/compare":{"post":{"operationId":"pageCompare","summary":"Compare a page with what it said before","description":"Read a public web page now and compare its text with text you kept from an earlier read (`text`, for example the `text` an earlier compare_page returned). Use it to see what changed on a page since you last looked, without setting up a monitor. Optional `selector` reads one part of the page (a CSS selector such as \"#pricing\"), `ignore_selectors` leaves parts out, and `ignore_patterns` (regular expressions) drop dates and counters from both sides. `render: true` reads the page in a browser, for pages built by script. Returns what changed (as diff_text does), the page's current `text` to keep for next time, its HTTP `status` and `final_url`. A page that is down or behind a bot check is an error and isn't charged.","x-credits":1,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-page-compare"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":8,"maxLength":2048,"description":"The page to read (http or https)."},"text":{"type":"string","maxLength":200000,"description":"The page's text from before; leave it out to just read the page."},"selector":{"type":"string","minLength":1,"maxLength":400,"description":"Read only what this CSS selector matches."},"ignore_selectors":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":400},"description":"CSS selectors of parts to leave out."},"ignore_patterns":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Regular expressions whose matches are left out of both versions."},"render":{"type":"boolean","default":false,"description":"Read the page in a browser with its scripts run."}},"required":["url"],"additionalProperties":false},"example":{"url":"https://info.cern.ch/hypertext/WWW/TheProject.html","selector":"h1","text":"The World Wide Web project"}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"final_url":{"type":"string"},"status":{"type":["integer","null"]},"rendered":{"type":"boolean"},"changed":{"type":"boolean"},"summary":{"type":"string"},"added_lines":{"type":"integer"},"removed_lines":{"type":"integer"},"added_words":{"type":"integer"},"removed_words":{"type":"integer"},"added":{"type":"array","items":{"type":"string"}},"removed":{"type":"array","items":{"type":"string"}},"diff":{"type":"string"},"diff_truncated":{"type":"boolean"},"text":{"type":"string"},"text_truncated":{"type":"boolean"},"credits":{"type":"integer"}},"required":["url","final_url","changed","summary","text","credits"]},"example":{"changed":true,"summary":"1 line added, 1 removed","added_lines":1,"removed_lines":1,"added_words":0,"removed_words":2,"added":["World Wide Web"],"removed":["The World Wide Web project"],"diff":"--- before\n+++ after\n@@ -1 +1 @@\n-The World Wide Web project\n+World Wide Web\n","diff_truncated":false,"url":"https://info.cern.ch/hypertext/WWW/TheProject.html","final_url":"https://info.cern.ch/hypertext/WWW/TheProject.html","status":200,"rendered":false,"text":"World Wide Web","text_truncated":false,"credits":1}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors/check":{"post":{"operationId":"monitorsCheck","summary":"Check a watched page now","description":"Check one of your monitors (`monitor_id`, from watch_page or list_watches) right now instead of waiting for its schedule. The page is compared with the monitor's baseline exactly as a scheduled check does: if it changed, the change is recorded, becomes the new baseline and its webhook gets monitor.changed. The first check of a monitor sets its baseline. Returns `changed`, a `summary`, whether a confirmation check is pending, and the `change` when there is one. A page that is down or behind a bot check is an error and isn't charged.","x-credits":1,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-monitors-check"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"monitor_id":{"type":"string","minLength":36,"maxLength":36,"description":"The monitor to check."}},"required":["monitor_id"],"additionalProperties":false},"example":{"monitor_id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b"}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"monitor_id":{"type":"string"},"baseline":{"type":"boolean"},"changed":{"type":"boolean"},"pending_confirmation":{"type":"boolean"},"summary":{"type":"string"},"status":{"type":"integer"},"final_url":{"type":"string"},"text_changed":{"type":"boolean"},"visual_changed_pct":{},"change":{},"credits":{"type":"integer"}}},"example":{"monitor_id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b","baseline":false,"changed":false,"pending_confirmation":false,"summary":"No change","status":200,"final_url":"https://info.cern.ch/hypertext/WWW/TheProject.html","text_changed":false,"visual_changed_pct":null,"change":null,"credits":1}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors/create":{"post":{"operationId":"monitorsCreate","summary":"Watch a page for changes","description":"Start watching a public web page for changes. Watchmint checks it every `every_minutes` (your plan sets the shortest: 60 on Free, 5 on Starter, 1 on Pro) and records a change when its text (`watch: \"text\"`, the default), its look (`\"visual\"`, a screenshot compared pixel by pixel against `threshold` percent) or both differ from the last version. The first check, due at once, sets the baseline. Narrow what counts with `selector` (one part of the page), `ignore_selectors` and `ignore_patterns` (regular expressions for dates and counters); `confirm: true` reports a change only when two checks in a row see it. Give `webhook_id` (one of the account's webhooks) to get a signed monitor.changed POST. Each check costs one credit; creating the monitor is free. Returns the `monitor`, with the `id` the other tools take.","x-credits":0,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-monitors-create"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":8,"maxLength":2048,"description":"The page to watch (http or https)."},"name":{"type":"string","maxLength":100,"description":"A name for the monitor."},"watch":{"type":"string","enum":["text","visual","both"],"default":"text","description":"What counts as a change: the page's text, its look, or either."},"every_minutes":{"type":"integer","minimum":1,"maximum":10080,"default":60,"description":"How often to check, in minutes."},"selector":{"type":"string","minLength":1,"maxLength":400,"description":"Watch only what this CSS selector matches."},"ignore_selectors":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":400},"description":"CSS selectors of parts to leave out (ads, tickers)."},"ignore_patterns":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200},"description":"Regular expressions whose matches don't count as changes."},"threshold":{"type":"number","minimum":0.1,"maximum":100,"default":1,"description":"For visual checks: the percent of the page that must look different."},"confirm":{"type":"boolean","default":false,"description":"Report a change only when two checks in a row see it."},"render":{"type":"boolean","default":false,"description":"Read the text in a browser with the page's scripts run (visual checks always do)."},"full_page":{"type":"boolean","default":true,"description":"For visual checks: the whole page, not just the first screen."},"viewport_width":{"type":"integer","minimum":320,"maximum":1920,"default":1280},"viewport_height":{"type":"integer","minimum":320,"maximum":2000,"default":800},"webhook_id":{"type":"string","minLength":36,"maxLength":36,"description":"One of the account's webhooks, to get monitor.changed events."}},"required":["url"],"additionalProperties":false},"example":{"url":"https://info.cern.ch/hypertext/WWW/TheProject.html","name":"The first web page","watch":"text","every_minutes":60}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"monitor":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"watch":{"type":"string"},"selector":{},"ignore_selectors":{"type":"array","items":{}},"ignore_patterns":{"type":"array","items":{}},"render":{"type":"boolean"},"full_page":{"type":"boolean"},"viewport_width":{"type":"integer"},"viewport_height":{"type":"integer"},"threshold":{"type":"integer"},"confirm":{"type":"boolean"},"every_minutes":{"type":"integer"},"webhook_id":{},"status":{"type":"string"},"next_check_at":{"type":"string"},"last_check_at":{},"last_change_at":{},"last_error":{},"checks":{"type":"integer"},"changes":{"type":"integer"},"created_at":{"type":"string"}}},"credits":{"type":"integer"}}},"example":{"monitor":{"id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b","name":"The first web page","url":"https://info.cern.ch/hypertext/WWW/TheProject.html","watch":"text","selector":null,"ignore_selectors":[],"ignore_patterns":[],"render":false,"full_page":true,"viewport_width":1280,"viewport_height":800,"threshold":1,"confirm":false,"every_minutes":60,"webhook_id":null,"status":"active","next_check_at":"2026-10-31T12:00:00Z","last_check_at":null,"last_change_at":null,"last_error":null,"checks":0,"changes":0,"created_at":"2026-10-31T12:00:00Z"},"credits":0}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors/list":{"post":{"operationId":"monitorsList","summary":"List watched pages","description":"List the pages this account watches, newest first: each monitor's settings, `status` (active, paused, paused_quota when the month's credits ran out, failing after three failed checks in a row), when it was last checked and last changed, and its counts. Filter with `status`; page with `limit` (default 50) and `offset`. Free.","x-credits":0,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-monitors-list"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["active","paused","paused_quota","failing"],"description":"Only monitors with this status."},"limit":{"type":"integer","minimum":1,"maximum":200,"default":50},"offset":{"type":"integer","minimum":0,"maximum":100000,"default":0}},"additionalProperties":false},"example":{"limit":20}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"monitors":{"type":"array","items":{}},"total":{"type":"integer"},"credits":{"type":"integer"}}},"example":{"monitors":[],"total":0,"credits":0}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors/update":{"post":{"operationId":"monitorsUpdate","summary":"Change or pause a watched page","description":"Change a monitor (`monitor_id`): any of watch_page's settings, or `paused: true` to stop its checks and `paused: false` to start them again (resuming checks at once). Fields you leave out keep their values; send null to clear `selector`, `name` or `webhook_id`. Changing what is read (the URL, what to watch, selectors, patterns, rendering or the viewport) makes the next check, due at once, the new baseline. Free.","x-credits":0,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-monitors-update"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"monitor_id":{"type":"string","minLength":36,"maxLength":36},"paused":{"type":"boolean","description":"true pauses the monitor; false resumes it."},"url":{"type":"string","minLength":8,"maxLength":2048},"name":{"type":["string","null"],"maxLength":100},"watch":{"type":"string","enum":["text","visual","both"]},"every_minutes":{"type":"integer","minimum":1,"maximum":10080},"selector":{"type":["string","null"],"minLength":1,"maxLength":400},"ignore_selectors":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":400}},"ignore_patterns":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"threshold":{"type":"number","minimum":0.1,"maximum":100},"confirm":{"type":"boolean"},"render":{"type":"boolean"},"full_page":{"type":"boolean"},"viewport_width":{"type":"integer","minimum":320,"maximum":1920},"viewport_height":{"type":"integer","minimum":320,"maximum":2000},"webhook_id":{"type":["string","null"],"minLength":36,"maxLength":36}},"required":["monitor_id"],"additionalProperties":false},"example":{"monitor_id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b","paused":true}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"monitor":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"watch":{"type":"string"},"status":{"type":"string"},"every_minutes":{"type":"integer"}}},"credits":{"type":"integer"}}},"example":{"monitor":{"id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b","name":"The first web page","url":"https://info.cern.ch/hypertext/WWW/TheProject.html","watch":"text","status":"paused","every_minutes":60},"credits":0}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors/changes":{"post":{"operationId":"monitorsChanges","summary":"Get recent changes","description":"Get the changes Watchmint found on your watched pages, newest first: for one monitor (`monitor_id`) or all of them, optionally only those after `since` (an ISO 8601 date-time). Each change has a `summary`, the added and removed lines (the first 50 of each), a unified `diff` (cut to 5,000 characters here; the webhook carries up to 20,000), and for visual checks the percent of the page that changed with before, after and diff image links. Up to `limit` (default 10, at most 50). Changes are kept 90 days. Free.","x-credits":0,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-monitors-changes"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"monitor_id":{"type":"string","minLength":36,"maxLength":36,"description":"Only this monitor's changes."},"since":{"type":"string","maxLength":40,"description":"Only changes found after this ISO 8601 date-time, e.g. 2026-10-31T09:00:00Z."},"limit":{"type":"integer","minimum":1,"maximum":50,"default":10}},"additionalProperties":false},"example":{"limit":5}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"changes":{"type":"array","items":{}},"credits":{"type":"integer"}}},"example":{"changes":[],"credits":0}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors/delete":{"post":{"operationId":"monitorsDelete","summary":"Stop watching a page","description":"Stop watching a page: deletes the monitor (`monitor_id`) with its stored snapshots and changes. This can't be undone; to stop checks for a while instead, call update_watch with `paused: true`. Free.","x-credits":0,"externalDocs":{"url":"https://watchmint.plxi.us/docs#op-monitors-delete"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"monitor_id":{"type":"string","minLength":36,"maxLength":36}},"required":["monitor_id"],"additionalProperties":false},"example":{"monitor_id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b"}}}},"responses":{"200":{"description":"Success. The body includes the credits charged.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"monitor_id":{"type":"string"},"credits":{"type":"integer"}}},"example":{"deleted":true,"monitor_id":"7c3f1a2e-5b4d-4e8f-9a6b-2d1c0e9f8a7b","credits":0}}}},"400":{"description":"Invalid JSON or input. Not charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit, concurrency limit or monthly quota reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/usage":{"get":{"operationId":"getUsage","summary":"Usage and limits for the current billing period.","responses":{"200":{"description":"Usage summary.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Stable error code, listed in the API reference."},"message":{"type":"string"},"reset_at":{"type":"string","format":"date-time"}}}}}}