Skip to content

Page content monitoring (Content monitor)

Content monitor watches the content, not the resource availability: Notifly fetches the specified URL every N seconds, extracts the value of interest from the response and compares it with the previous one. As soon as the value changes — an alert is sent. This is an active monitor (Notifly initiates the check), but it answers the question “what changed on the page” rather than “is the site up”.

Typical use cases:

  • Price — track the price of a product on a store page or a competitor’s site.
  • Status / availability — “in stock” ↔ “out of stock”, order status, service status.
  • Competitor content — a new blog post appeared, the price list changed, the changelog updated.
  • JSON API — the value of a field in a public JSON response (rate, balance, version).

What to compare is defined by the mode (mode). Five modes are supported:

modeWhat is extractedField selector
hashSHA-256 of the entire response body — alert on any page changenot needed
jsonValue by JSONPath from a JSON responserequired — JSONPath, e.g. $.price or $.items.0.status
textSubstring by regular expression from the response textregex; if empty — behaves like hash
cssText of the first element matched by a CSS selectorrequired — valid CSS selector
xpathText of the first node matched by an XPath expressionrequired — valid XPath expression

The check logic is the same for all modes: extract the value → compare with the stored lastValue → if different, send an alert and remember the new value. The first check just stores the current value and does not send an alert.

Instead of a single mode+selector you can provide an array of rules (rules) — up to 20 rules per monitor. Each rule is { "mode", "selector", "label" }, where label is an optional human-readable name. An alert is triggered if any rule changed. This is convenient when you need to monitor several values on the same page (e.g., price + availability + rating).

"rules": [
{ "mode": "css", "selector": ".product-price", "label": "Цена" },
{ "mode": "css", "selector": ".availability", "label": "Наличие" },
{ "mode": "text", "selector": "Скидка\\s+\\d+%", "label": "Скидка" }
]

If rules is set, top-level fields mode/selector are ignored. If rules is empty — the single mode (mode+selector) works, preserved for backward compatibility.

By default the page is loaded via a plain HTTP GET. If the needed value is injected by scripts in the browser (SPA, lazy-loaded price, etc.), enable the flag useBrowser: true — then the page is rendered via headless Chromium and values are extracted from the final DOM after JS execution.

Before creating a monitor, it is convenient to try and validate a rule on the fly — there are two endpoints that do not require creating a monitor.

Test a rule — POST /content-monitor/test-rule

Section titled “Test a rule — POST /content-monitor/test-rule”

Loads the page and applies a single rule, returning the extracted value or a clear error:

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor/test-rule" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"url": "https://example.com/product/42",
"useBrowser": false,
"mode": "css",
"selector": ".product-price"
}'
# → {"ok":true,"value":"1 299 ₽"}

Response: ok (boolean), value (extracted value, truncated to 200 characters), error (error text, if the rule failed) and note (diagnostics, e.g. about browser mode fallback to GET).

Suggest a selector using AI — POST /content-monitor/suggest-selector

Section titled “Suggest a selector using AI — POST /content-monitor/suggest-selector”

Notifly loads the page once, compresses the HTML and in one request to the LLM chooses the mode and selector according to your description. The result is always verified on the real page content, so the response contains a valid sampleValue:

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor/suggest-selector" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"url": "https://example.com/product/42",
"useBrowser": false,
"prompt": "следить за ценой товара"
}'

The response includes: mode, selector, label, sampleValue (current value on the page), reason (why), confidence (high/medium/low), alternatives (other candidates) and fallback (true if the LLM is unavailable and the selector was chosen heuristically). The prompt field is up to 500 characters. A request like “monitor the whole page” will immediately return hash mode without calling the LLM.

  1. Open app.notifly.ruMonitors → Content.
  2. Enter the URL, choose a mode (or click “Suggest selector” and describe in words what to monitor), enable useBrowser if needed.
  3. Use the “Test” button to ensure the extracted value is exactly what you need.
  4. Set the interval, alert text and priority — and save.

Via the REST API — POST /content-monitor

Section titled “Via the REST API — POST /content-monitor”

Single mode (monitoring one CSS value):

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "Цена товара #42",
"url": "https://example.com/product/42",
"mode": "css",
"selector": ".product-price",
"useBrowser": false,
"intervalSec": 3600,
"timeoutSec": 15,
"alertMessage": "Цена товара #42 изменилась!",
"alertPriority": 7
}'

Multiple mode (several rules on one page):

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "Карточка товара",
"url": "https://example.com/product/42",
"rules": [
{ "mode": "css", "selector": ".product-price", "label": "Цена" },
{ "mode": "css", "selector": ".availability", "label": "Наличие" }
],
"intervalSec": 3600,
"alertMessage": "Изменилось содержимое карточки товара.",
"alertPriority": 5
}'

Monitoring a field in a JSON API:

Окно терминала
curl -X POST "$NOTIFLY_URL/content-monitor" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"appid": 12345,
"name": "Курс из API",
"url": "https://api.example.com/rates",
"mode": "json",
"selector": "$.usd.value",
"intervalSec": 600,
"alertMessage": "Курс USD изменился."
}'
FieldTypeRequiredDescription / constraints
appiduintyesID of the application (channel) to send the alert to
namestringyesMonitor name, 1–200 characters
urlstringyesValid http(s) URL
modestringyes*Single mode: hash / json / text / css / xpath
selectorstringdependsJSONPath for json, regex for text, CSS for css, XPath for xpath; required for json/css/xpath
rulesarrayyes*Array of rules {mode, selector, label}, up to 20; alternative to mode+selector
useBrowserboolnotrue — render via headless Chromium
intervalSecuintyesCheck interval, 60–86400 seconds
timeoutSecuintnoLoad timeout, default 15, maximum 60
alertMessagestringyesAlert text, 1–2000 characters
alertTitlestringnoAlert title
alertMessageMarkdownboolnoTreat alert text as Markdown
alertPriorityintnoAlert priority, 0–10

* You must set either rules (multiple mode) or mode+selector (single mode). If rules is non-empty — mode/selector are ignored.

A new monitor is created with status pending and is checked almost immediately (nextCheckAt = “now”). After changing “significant” fields (URL, mode, selector, rules, useBrowser, interval, timeout) the check is rescheduled for “now”. Cosmetic edits (name, alert title/message, priority) do not shift the schedule.

  • POST /content-monitor/:id/pause — pause (checks are not performed; nextCheckAt is moved far into the future).
  • POST /content-monitor/:id/resume — resume (status becomes pending again, check is scheduled for “now”).

The monitor record stores lastValue (last extracted value), lastError (last load/extraction error), lastCheckAt, nextCheckAt and alertedAt.

  • Check interval: 60–86400 seconds (1 minute to 24 hours).
  • Load timeout: default 15 s, maximum 60 s.
  • Up to 20 rules per monitor.
  • Alert priority: 0–10; name length — 1–200, alertMessage — 1–2000 characters.
  • Requests originate from the cloud, so the target URL must be accessible from the public internet.
  • Browser mode (useBrowser) works only if the corresponding container is configured on the server.

All endpoints require a client token (or an MCP code with write rights for mutating operations); the list is available in read-only mode too. More about tokens in First login.

MethodPathDescription
GET/content-monitorList of the user’s content monitors
POST/content-monitorCreate a monitor
POST/content-monitor/suggest-selectorSuggest mode + selector with AI by description
POST/content-monitor/test-ruleApply one rule to a page (dry-run), return value or error
PUT/content-monitor/:idUpdate settings (cannot change appid)
DELETE/content-monitor/:idDelete a monitor
POST/content-monitor/:id/pausePause checks
POST/content-monitor/:id/resumeResume checks

See also: Active monitors, HTTP monitors, Workflow monitors, Browser-workflow.