What a Verid Monitor Is Made Of
Every monitor is five decisions: where to look, how often, what to capture, when it counts as a change, and who to tell. Learn those five and the rest of Verid is configuration.
What is a monitor?
A monitor is a saved instruction that Verid carries out on a schedule. In plain words it says:
Look at this page every hour. Pull out the price. If the price changes, post a message in Slack.
That single sentence contains everything a monitor is. Broken up, it is five decisions:
| Part | The question it answers | Example |
|---|---|---|
| URL | Where do I look? | https://example.com/pricing |
| Interval | How often do I look? | Every 3600 seconds (1 hour) |
| Extractor | What do I capture from the page? | The text inside .plan-pro .price |
| Predicate | When does a change deserve an alert? | When the price field changes |
| Deliveries | Who do I tell? | A Slack channel |
Nothing else about Verid makes sense until these five are clear, and once they are, almost every other feature is a variation on one of them. This guide walks through each part, then follows a single check from start to finish so you can see them working together.
Part 1 - The URL: where to look
One monitor watches one URL. If you want to watch three product pages, that is three monitors.
The URL must be a complete, valid web address including https://. It can point at:
- A normal web page, like a pricing page or a status page
- A JSON API endpoint, like
https://api.example.com/v1/products/42
Verid fetches whatever that address returns. It does not follow links, crawl deeper, or discover other pages on the site. It looks at exactly the address you gave it.
Your monitor also has a name, between 1 and 200 characters. The name is for you and for your alerts, so make it something you will recognize in a Slack message six months from now. "Competitor pricing page" beats "Monitor 4".
Part 2 - The interval: how often to look
The interval is how many seconds Verid waits between checks. After every check, the next one is scheduled for the interval you set, counted from the moment the check finished.
How fast you can go depends on your plan:
| Plan | Fastest interval | Monitors included |
|---|---|---|
| Free | Once a day (86,400 seconds) | 5 |
| Lite | Every 2 hours (7,200 seconds) | 25 |
| Starter | Every hour (3,600 seconds) | 50 |
| Pro | Every 15 minutes (900 seconds) | 250 |
| Scale | Every 5 minutes (300 seconds) | 1,500 |
In the dashboard, the interval box is pre-filled with the fastest interval your plan allows, so you can leave it alone unless you want to check less often. If you try to set something faster than your plan permits, Verid rejects it and names the limit in the error.
One thing worth understanding early: Verid reports the difference between two checks, not everything that happened between them. If a price goes from $49 to $59 and back to $49 inside a single hour, an hourly monitor sees $49 both times and reports nothing at all. That is not a bug, it is what a scheduled check means. If you need to catch brief changes, you need a shorter interval, which is one of the real reasons the paid plans exist.
Part 3 - The extractor: what to capture
This is the part people find least obvious, and it is the most important.
Verid does not store the whole page and compare pictures of it. Instead it captures a small set of named fields on every check, and it is those fields that get compared. A field is just a name you choose paired with an instruction for finding a value:
{
"pro_plan_price": ".plan-pro .price"
}
Here pro_plan_price is your name for the value, and .plan-pro .price is how Verid finds it. The name is entirely yours to pick. It shows up in the dashboard, in the change history, and in your alerts, so name fields after what they mean.
Fields matter because the field is the unit of everything downstream. Changes are detected per field, alert rules are written against a field by name, and your alert tells you which fields changed. Two fields in one monitor are tracked independently.
The six ways to capture a value
The instruction you write depends on the method you pick. There are six:
| Method | What you write | Best for |
|---|---|---|
| CSS Selector | .plan-pro .price |
Pages with meaningful class names. The usual first choice. |
| XPath | //span[@id="price"] |
Finding an element by the text it contains, or reaching a parent |
| JSONPath | $.items[0].price |
URLs that return JSON rather than a web page |
| Regex | v(\d+\.\d+\.\d+) |
A value that only exists in the raw page source |
| Full Page Hash | Nothing to write | "Tell me if anything at all changed" |
| AI (LLM) | "the current price in USD" |
Messy or shifting pages, described in plain English |
Each of these has its own guide with worked examples. Three notes apply to all of them.
What you get back is usually text. CSS, XPath, Regex and Full Page Hash always hand you text. If the page says $59.00, your field holds the string $59.00, dollar sign included. JSONPath is the exception: it returns the value exactly as it appears in the JSON, so a JSON number stays a number. This distinction matters in Part 4.
A field that matches nothing goes empty rather than disappearing. If your selector finds no element, the field is still there on that check, holding an empty value. That is worth knowing, because an empty value is a different value: a field that goes from $49 to empty counts as a change, and a monitor watching that field will alert you. A broken selector announces itself instead of going quiet, which is the behaviour you want.
Before that happens, Verid gives your selectors a second chance. If a plain request comes back with every field empty, that usually means the page needs JavaScript to render, so Verid automatically retries the same check with a real browser. Only if the browser attempt also finds nothing do your fields end up empty. (This retry does not apply to JSONPath, where empty means the path simply did not match, and a browser would not help.)
A selector that matches several elements gives you a list. Point a field at three separate prices and it holds all three, in page order, and the comparison treats the whole list as one value. If you meant one price, make the selector more specific.
The exception: Full Page Hash
Full Page Hash is the one method with no fields to write, and it is worth understanding because it is the default in the dashboard.
In the create form, the first question is "What do you want to monitor?" with two answers. Choosing The whole page selects Full Page Hash. Choosing A specific value lets you pick one of the other five methods and write your fields.
When you pick the whole page, Verid downloads the HTML, strips out the parts that change on every single load (scripts, styles, comments, security tokens, and similar noise), and computes a SHA-256 hash of what is left. It stores two fields for you automatically:
{
"page_hash": "a3f5b2c1d4e6f7890abc...",
"content_length": "42381"
}
page_hash is the fingerprint, and content_length is the character count of the normalized content. If either changes, the page changed. The trade-off is that a hash tells you that something changed, never what. It is the fastest way to start and the least informative to receive.
Part 4 - The predicate: when a change deserves an alert
Here is the distinction that saves the most confusion:
A change and an alert are not the same thing.
Verid records every change it finds, whether or not it tells you about it. The predicate is the rule that decides which changes are worth interrupting you for. A price that moved by one cent is a change. Whether it is an alert is your call.
There are nine kinds of rule:
| Predicate | Fires when |
|---|---|
any_field_changes |
Any field in the monitor changed |
field_changes |
One named field changed, whatever it changed to |
field_increases_by_percent |
A named field rose by at least X percent |
field_decreases_by_percent |
A named field fell by at least X percent |
field_increases_by_absolute |
A named field rose by at least X |
field_decreases_by_absolute |
A named field fell by at least X |
field_matches_regex |
A named field changed, and the new value matches a pattern |
field_equals |
A named field changed, and the new value equals something exact |
composite |
Several of the above combined with AND or OR |
any_field_changes is the default, and for a whole-page monitor it is the only rule that makes sense, since there is nothing to name.
Two rules about predicates that are easy to miss and cost real time:
Every field-specific rule requires that field to have changed first. A rule like "alert me when stock_status equals In Stock" does not fire on every check where the value happens to be In Stock. It fires on a check where stock_status changed and the new value is In Stock. Predicates describe changes, not states.
The numeric rules need a value that reads as a number. Percent and absolute thresholds parse your captured text as a number. $59.00 does not read as a number, because of the dollar sign, so a threshold rule on that field will silently never fire. If you want a threshold, capture the number by itself: use a regex capture group, an XPath onto a numeric attribute, a JSONPath at a numeric JSON value, or an AI prompt that asks for a bare number. If you just want to know about any movement, field_changes works on text and needs no parsing at all.
Part 5 - The deliveries: who gets told
A delivery is one place an alert goes. A monitor can have several, and it can have none.
| Type | What you provide |
|---|---|
| Webhook | An HTTPS URL of your own, plus optional custom headers |
| Slack | A Slack incoming webhook URL |
| Discord | A Discord webhook URL |
| An email address |
With no deliveries at all, the monitor still runs and still records every change. You just have to open the dashboard to see them. That is a legitimate setup for something you review weekly rather than react to.
How many endpoints one monitor can have depends on your plan: 1 on Free, 2 on Lite, 3 on Starter, 10 on Pro, and 25 on Scale.
Every endpoint on a monitor fires on the same predicate. There is no way to send price drops to one channel and stock changes to another from a single monitor. When you need different rules for different audiences, make two monitors on the same URL with different predicates and different deliveries.
Webhook deliveries are signed so your endpoint can prove the request came from Verid. The signing secret is deliberately never included when you read a monitor back. You fetch it once from GET /v1/monitors/{id}/signing-secret and keep it on your side.
How the five parts work together on one check
Every check follows the same path:
- Fetch. Verid requests your URL.
- Extract. It applies your extractor and produces the named fields.
- Compare. It compares those fields against the fields from the last successful check, field by field. The result lists which fields changed, plus their old and new values. Fields that did not change are left out of the comparison entirely.
- Decide. It evaluates your predicate against that comparison.
- Deliver. If the predicate says yes and something actually changed, every delivery endpoint fires.
Everything from that check is saved as a run: the values captured, the comparison, whether an alert was triggered, and every delivery attempt. Runs are your audit trail and the first place to look when something behaves unexpectedly.
Two behaviours of this pipeline are worth knowing in advance.
The first check has nothing to compare against. With no previous successful run, Verid treats every field as new. Your old values are empty and every field counts as changed, so a monitor set to any_field_changes normally does send an alert on its very first check. That first alert is expected. It is establishing the baseline, and the checks after it compare against real history.
A check can succeed, find changes, and still send nothing. That is your predicate doing its job. The run will show the change with the alert marked as not triggered, which is exactly how you confirm a threshold is set where you meant it.
A complete monitor, end to end
Say you want to know when a competitor changes the price of their Pro plan.
In the dashboard:
- Name it
Competitor pricing pageand paste the URL. - Under "What do you want to monitor?", choose A specific value.
- Leave the extraction method on CSS Selector, and add a field named
pro_plan_pricewith the selector.plan-pro .price. - Click the test button. Verid fetches the page live and shows you what your selector actually captured. Do this every time. It is the difference between a monitor and a monitor that quietly watches nothing.
- Set the trigger condition to Specific field changes and enter
pro_plan_price. - Leave the interval at the pre-filled value.
- Add a Slack notification and paste your Slack webhook URL.
The same monitor through the API:
{
"name": "Competitor pricing page",
"url": "https://example.com/pricing",
"schedule_interval_seconds": 3600,
"extract_config": {
"method": "css",
"fields": {
"pro_plan_price": ".plan-pro .price"
}
},
"diff_predicate": {
"type": "field_changes",
"field": "pro_plan_price"
},
"deliveries": [
{
"type": "slack",
"webhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX"
}
]
}
What the first check captures:
{
"pro_plan_price": "$49"
}
What the next check captures after they raise the price:
{
"pro_plan_price": "$59"
}
The comparison Verid computes:
{
"fields_changed": ["pro_plan_price"],
"before": { "pro_plan_price": "$49" },
"after": { "pro_plan_price": "$59" }
}
pro_plan_price changed, the predicate is satisfied, so the Slack delivery fires. A webhook endpoint on the same monitor would receive:
{
"id": "8f14e45f-ceea-467a-9a1b-2b3c4d5e6f70",
"version": "2026-05-01",
"monitor_id": "3f9a1c72-8d4e-4b1a-9f2e-7c6d5b4a3e21",
"run_id": "b21c9f08-4a7d-4c3e-8b15-9e0f1a2b3c4d",
"fired_at": "2026-08-12T09:00:04.512Z",
"diff": {
"fields_changed": ["pro_plan_price"],
"before": { "pro_plan_price": "$49" },
"after": { "pro_plan_price": "$59" }
},
"monitor": {
"url": "https://example.com/pricing",
"name": "Competitor pricing page"
}
}
Notice that the alert carries the old value and the new value. Your Slack message or your own service can act on the actual numbers, not just the fact that something moved.
The optional parts
Four settings sit outside the five core decisions. You can ignore all of them at first.
Fetch mode. By default this is Auto, and it is the right answer nearly always. Verid tries a plain, fast request first and steps up to a real browser by itself when a page turns out to need one. Setting it to Browser forces the browser on every check, which is worth doing when you already know the content only appears after JavaScript runs.
Custom request headers. Up to 20 headers sent with each request, for pages that need an API key or a specific Accept header. A few headers are reserved and Verid will refuse them, including Cookie and Host. Bear in mind that a token which later expires turns into a monitor that fails.
Templates. The create form offers a list of ready-made starting points for common jobs, such as a GitHub release, an npm version, or an RSS feed. A template pre-fills the extractor and the predicate for you. Treat it as a starting point, then test it and adjust, because it cannot know the specific page you are pointing at.
Visual monitors. If you want to watch how a page looks rather than what it says, that is a separate kind of monitor with its own screenshot comparison and no selectors to write. Create it from the Visual monitors section instead of this form.
Monitor statuses
A monitor is always in one of these states, and only the first one runs on schedule.
| Status | Meaning |
|---|---|
active |
Running on its interval |
paused |
You paused it. Nothing runs until you resume it. |
error |
Verid stopped it after 10 failed checks in a row, and emailed you about it |
billing_paused |
Stopped for a billing reason |
deleted |
Removed |
The error status is the one to understand. A single failed check is not unusual, and Verid keeps trying on schedule. Ten consecutive failures means something is genuinely broken, most often that the site now blocks the request or that the page was redesigned and your selector points at nothing. Verid stops rather than retrying forever, tells you, and waits for you to fix the configuration and resume it. A successful check at any point resets the counter to zero.
Common issues
| Problem | What is happening |
|---|---|
| Monitor runs, no alerts, but you expected some | Either nothing changed, or the predicate is stricter than you intended. Open the latest run and check whether the comparison found changes but the alert was not triggered. |
| A threshold rule never fires | The captured value is not readable as a number, most likely because it includes a currency symbol or a comma. Capture the number on its own. |
| An alert on the very first check | Expected. The first check has no history to compare against, so every field counts as new. |
| A field suddenly went empty | Its expression matched nothing on this run, even after the browser retry. The page structure probably changed. Test it again against the live page. |
| Too many alerts | Usually the extractor is capturing more than you meant, or the predicate is any_field_changes where a threshold belongs. |
Status changed to error |
Ten consecutive failed checks. Check the last run's error, fix the configuration, and resume. |
Ready to try it? Point Verid at your URL and get a signed alert on every change. 5 monitors free, no credit card.