Competitor pricing pages
Track the headline price and the per seat price on a rival plan page, and fire only on a move worth reacting to rather than on every copy edit.
- field
- starter_price
- rule
- field_decreases_by_percent, threshold 5
A change detection API watches a page on a schedule, compares each check against the one before it, and calls your code when the parts you named are different. Verid does that for any public URL: you POST the page, the fields to extract and the rule that counts as a change, and a signed webhook arrives carrying the previous value, the current value and the list of fields that moved.
Website change detection lands in your stack as JSON you can act on, not in your inbox as an email you read on Monday.
Spec sheet
Twelve answers, no adjectives. Every number below is read from the shipping product rather than from a marketing sheet, and the ones that differ by plan say so.
The loop
Four things happen on every check, in the same order, whether the monitor runs once a day or every five minutes. Three of them you configure once. The fourth is the only one you ever hear about.
POST the page you want watched plus a check interval in seconds. Verid fetches statically first and escalates to a real Chrome render when the page needs JavaScript, so a client rendered page needs no extra configuration. Set fetch_mode to browser when you want to skip the static attempt entirely.
"url": "https://competitor.example/pricing"An extract_config maps your field names to selectors. Choose CSS, XPath, JSONPath for JSON endpoints, regex for loose text, a full page hash when any edit counts, or an AI prompt when the markup is rewritten every deploy. Every run then produces the same JSON shape.
"starter_price": ".plan-starter .price"A diff_predicate decides what counts as a change. Fire on any field moving, on one named field, on a percentage or absolute move, on a regex match, on an exact value, or on AND and OR combinations of those. Fields you never extracted can never trigger an alert.
"type": "field_decreases_by_percent"When the predicate is true, Verid POSTs the previous object, the current object and the list of changed fields to your endpoint, signed with HMAC-SHA256 in the Verid-Signature header. A failed delivery retries six times, then waits in a queue you can replay.
POST /hooks/verid 200 OKExtraction
Change detection is only as good as the thing being compared. Before a diff can mean anything, a run has to turn a page into the same JSON object every time, and that is the job of the extract_config. Verid ships six extraction methods and a monitor uses exactly one of them. This is the decision that determines whether your monitor is still telling the truth six months from now, so it is worth two minutes rather than a guess.
Is the response JSON?
Use json_path
Is the element stable?
Use css, then xpath
Does the markup churn?
Use prompt, or hash it
| Method | Reach for it when | Example | What breaks it | Cost |
|---|---|---|---|---|
| cssCSS selector | Server rendered HTML where the element has a class or id that survives a deploy. | .plan-starter .price | Build tools that hash class names on every release. | Unmetered |
| xpathXPath expression | You need position, an ancestor relationship or a text predicate that CSS cannot express. | //table//tr[2]/td[3]/text() | A layout change that reorders siblings or adds a wrapper element. | Unmetered |
| json_pathJSONPath | The target is a JSON endpoint: a public API, a status feed, a releases list, a price route. | $.data.attributes.price | The provider ships a new API version with a different response shape. | Unmetered |
| regexRegular expression | The value sits in prose rather than in an element of its own, such as a version string in a paragraph. | Version ([0-9]+\.[0-9]+\.[0-9]+) | Someone rewords the sentence around the value. | Unmetered |
| full_pageFull page hash | Any edit at all matters and you cannot enumerate the parts, for example a terms of service document. | no fields, the whole document is hashed | Rotating banners, timestamps and session ids, which all count as changes. | Unmetered |
| promptAI prompt | The markup is rewritten often enough that no selector survives, or the value needs reading rather than locating. | "Return the current price and whether it is in stock" | Nothing structural, but each run spends one call from your monthly budget. | Metered |
Unmetered means the extraction runs on every check with no separate allowance. Prompt extraction calls a model each run, so it draws on a monthly budget of 50 calls on Free rising to 25,000 on Scale. The full syntax for each method, including how nested and repeated values are flattened, is on the extraction methods page.
A field that exists is a field that can fire. The most common cause of a noisy change detection monitor is extracting a whole section when you only cared about one number inside it. Anything you did not name cannot reach the diff, which is a stronger noise filter than any downstream rule.
The playground runs a real fetch and a real extraction against a live URL in the browser and shows you the exact JSON a run would store. Getting a selector wrong is the difference between an alert on the price and an alert on a cookie banner, and it is much cheaper to find out before the schedule starts.
Extract the value you care about and one nearby anchor, for example a product title next to a price. When the anchor goes empty you know the page was restructured rather than repriced, and a composite predicate can require both before it fires.
If a selector returns empty on a page that clearly shows the value, the markup arrived after hydration. Set fetch_mode to browser and the check runs in real Chrome. From Starter upward a residential proxy fallback recovers fetches that datacenter traffic gets blocked on.
The selector playground runs a real extraction against a live URL in the browser, with no account, so you can settle the CSS versus XPath question on the actual page rather than in the abstract.
The contract
The request on the left watches a competitor pricing page every hour and fires only when the starter price drops by more than 5 percent or the per seat price changes at all. Nothing else on that page can trigger it. The response on the right is what your endpoint actually receives when it does.
curl -X POST https://api.verid.dev/v1/monitors \
-H "Authorization: Bearer vrd_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Competitor pricing page",
"url": "https://competitor.example/pricing",
"schedule_interval_seconds": 3600,
"fetch_mode": "auto",
"extract_config": {
"method": "css",
"fields": {
"starter_price": ".plan-starter .price",
"seat_price": ".plan-team .price-per-seat"
}
},
"diff_predicate": {
"type": "composite",
"operator": "OR",
"conditions": [
{
"type": "field_decreases_by_percent",
"field": "starter_price",
"threshold": 5
},
{ "type": "field_changes", "field": "seat_price" }
]
},
"deliveries": [
{ "type": "webhook", "url": "https://api.yourapp.com/hooks/verid" }
]
}'{
"id": "dlv_8f2c1a94",
"version": "2026-05-01",
"monitor_id": "mon_3ab77c10",
"run_id": "run_51d0e2f8",
"fired_at": "2026-08-07T09:15:04.219Z",
"diff": {
"fields_changed": ["starter_price"],
"before": {
"starter_price": "$49.00",
"seat_price": "$12.00"
},
"after": {
"starter_price": "$42.00",
"seat_price": "$12.00"
}
},
"monitor": {
"url": "https://competitor.example/pricing",
"name": "Competitor pricing page"
}
}The signature is an HMAC-SHA256 over the timestamp and the raw body joined by a dot, so a replayed or edited payload fails verification. The same monitor is one method call in the Node.js SDK, and every field is documented in the API reference with an OpenAPI 3.1 spec you can generate a client from.
Beyond the diff
Change detection software is easy to demo and hard to run. A diff on a static page takes an afternoon. Keeping a hundred monitors honest through redesigns, rate limits, endpoint outages and a page that renders after hydration is the actual product.
Change detection stops being useful the first time it fires on a rotating banner. Verid diffs the object you defined, so the comparison happens on starter_price and availability rather than on a page that never stops moving. The predicate then evaluates that diff, which is why "the price dropped more than 5% while the plan name stayed the same" is a single rule instead of a filter you write downstream.
One rule, evaluated on the diff
field_decreases_by_percent, threshold 5, fires
Every payload carries an HMAC-SHA256 signature in the Verid-Signature header, formatted t=timestamp,v1=signature over the timestamped body. Delivery is attempted six times, immediately then after 5, 15 and 30 minutes and 1 and 2 hours, before the attempt lands in a queue you can replay from the dashboard or the API.
Each check writes the extracted object and the computed diff, so you can read how a page moved over 14 days on Free or 2 years on Scale instead of reconstructing it from alert emails you half remember.
A check starts as a static fetch and escalates to a real Chrome render when the page needs JavaScript. Force it per monitor with fetch_mode, send custom request headers on every check, and from Starter upward fall back to a residential proxy when a datacenter fetch is refused.
The same change can reach a webhook endpoint, a Slack channel, a Discord channel and an email address at once, with one destination per monitor on Free rising to 25 on Scale. The webhook is the integration surface, the rest are the humans.
Create, list, update, pause, resume, run now, read runs, replay deliveries and rotate keys are all endpoints. The Node.js SDK and the OpenAPI 3.1 spec are generated from the same contract, so a client you generate today matches the API the dashboard uses.
Recipes
A change detection API is a general tool, which makes it hard to picture until you see a configured monitor. These nine are the shapes that recur most often across accounts, each written the way you would create it: the target, the extraction method, the field name, and the exact predicate that decides when your endpoint hears about it.
Track the headline price and the per seat price on a rival plan page, and fire only on a move worth reacting to rather than on every copy edit.
Watch the availability string on a product page. An exact value predicate turns the moment it flips back to in stock into a webhook rather than a refresh habit.
Point a monitor at a releases API route instead of the human page. JSONPath reads the tag directly, so a new release becomes an event in your own tooling within the check interval.
Most providers publish a machine readable status document. Extract the overall indicator and alert on it changing, so a dependency going down reaches your on call channel without a human refreshing a page.
Legal and policy pages change quietly and rarely. A full page hash is the right tool because you cannot list in advance which clause will move, and a rare false positive costs less than a missed amendment.
Registrar, nameserver and expiry fields on a public lookup page are stable text in a stable layout, which makes them a clean target for a daily check and an exact match rule.
Extract the link block or the nav from a page you own and diff it on a schedule. A CMS edit that quietly drops an internal link shows up as a field level diff instead of surfacing months later in a crawl report.
A competitor opening five infrastructure roles is a roadmap leak. Extract the listing count or the first few titles and alert on an absolute increase rather than on any edit to the page.
Marketplace result pages render at a stable URL, so the same monitor shape works there. Track your own listing position, the top listing, or a price band, and let the diff tell you which one moved.
Detection lag is the check interval plus the time the page takes to render. The interval is set per monitor in seconds and floored by your plan, so the soonest a change can reach you is the interval on your row. Most accounts mix the two: the page that pays the bills runs at the floor, the long tail runs daily.
| Plan | Per month | Fastest check | Monitors | Run history kept |
|---|---|---|---|---|
| Free | $0 | Once a day | 5 | 14 days |
| Lite | $9 | Every 2 hours | 25 | 90 days |
| Starter | $19 | Every hour | 50 | 180 days |
| Pro | $49 | Every 15 minutes | 250 | 1 year |
| Scale | $149 | Every 5 minutes | 1,500 | 2 years |
Honest comparison
Five categories that overlap on the surface and answer different questions underneath. Read down the row that matters to you rather than across the column with the most ticks, because on at least two of these rows a different tool is the better answer.
| Criterion | Verid | Google Alerts | Visual diff tools | Self hosted text diff | Scraping APIs |
|---|---|---|---|---|---|
| What is compared | The JSON object you defined, field by field | Newly indexed pages matching a keyword | Screenshot pixels, sometimes summarised by AI | Raw text or HTML of the whole page | Nothing. Each call is stateless |
| What you can match on | Nine predicates, composable with AND and OR | A search query, nothing else | A visual change percentage | Any text change, with optional filters | Not applicable |
| What arrives | JSON: before, after, fields_changed | An email digest with links | An image plus a summary line | A text diff you parse yourself | A one off scrape response |
| Coverage | Any public URL, including JSON endpoints | Only pages the index picked up | Pages that render visually | Any URL you configure | Any URL you call |
| Scheduling | Per monitor, from once a day to every 5 minutes | Fixed digest cadence, not configurable | Built in, per page | Built in, per watch | You run the cron and store the state |
| Delivery reliability | Signed, six attempts, dead letter queue, replay | Email only, no retries, no signature | Fire and forget on most plans | Fire and forget | Completion callbacks only |
| Setup cost | One POST, or a template in the dashboard | Type a query | Paste a URL | Docker, or a paid cloud plan | You build the monitoring layer yourself |
Where it stops
Five places where Verid is the wrong tool or stops on purpose. If one of these is your actual requirement, you will find out on day one rather than after you have wired it in, which is the only reason to put a section like this on a page trying to sell you something.
Verid is built around the state between two runs. If you need one off extraction at high volume with no diff and no schedule, a scraping API is the cheaper tool for that job. Plenty of teams run both: scrape on demand, monitor with Verid.
Checks run from Verid infrastructure with the headers you set. Pages behind an interactive login, a CAPTCHA solve or a per user session are out of scope. A URL you control that carries its own token is fine.
Verid tells you a field moved and hands you the diff. Repricing, opening a ticket, posting to a thread: that is your webhook handler. The boundary is deliberate, because workflow logic belongs in your stack rather than in a vendor rule builder.
Prompt based extraction calls a model on every run, so it is metered per plan: 50 calls a month on Free, 250 on Lite, 500 on Starter, 5,000 on Pro and 25,000 on Scale. CSS, XPath, JSONPath, regex and full page extraction have no such cap.
The soonest you can hear about a change is one check interval after it happened, plus render time. That floor is set by your plan, so a page that matters in minutes needs Pro or Scale, and treating a daily plan as real time will disappoint you.
The thirteen things developers ask before wiring the first monitor, answered with the same numbers the product enforces.
A change detection API watches a URL on a schedule, compares what it reads against the previous check, and notifies your code when the parts you named are different. With Verid you send a URL, an extraction config and a rule, and you receive a signed webhook containing the previous object, the current object and the list of fields that changed.
Create a monitor with the page URL and a check interval, name the fields you care about with a CSS selector, XPath, JSONPath, regex or an AI prompt, and pick a diff predicate. When the predicate is true Verid delivers the change to a webhook endpoint, a Slack channel, a Discord channel or an email address. The webhook is the one your code can act on automatically.
A scraping API answers "what is on this page right now" and forgets. Change detection keeps the previous result, diffs the new one against it, and only speaks when your rule is satisfied. Scheduling, run history, diffing and delivery retries are exactly the parts you would otherwise build yourself on top of a scraper.
For monitoring specific pages, yes. Google Alerts watches the search index for new content matching a keyword and emails you a digest, so it cannot see a price change on a page it already indexed and it has no API. Verid watches the page itself, compares named fields between runs, and delivers a signed JSON webhook your code can act on. For discovering brand new mentions across the open web, a keyword alert service is still the right tool.
Yes. Every check starts as a static fetch and escalates to a real Chrome render when the page needs it, and you can force browser mode per monitor by setting fetch_mode to browser. Starter and above add a residential proxy fallback for pages that refuse datacenter traffic outright.
Use CSS when the element has a class or id that survives a deploy, because it is shorter and easier to keep. Use XPath when you need something CSS cannot express: a position in a table, a parent relationship, or a match on the text inside a node. If the response is JSON rather than HTML, use JSONPath instead of either. You can test any of them against a live URL in the playground before creating the monitor.
Yes, and it is usually the more reliable target. Set the method to json_path and point expressions such as $.data.attributes.price at the response. Status endpoints, release feeds and price routes all work this way, and they break far less often than the rendered page in front of them.
The floor depends on your plan: once a day on Free, every 2 hours on Lite, hourly on Starter, every 15 minutes on Pro and every 5 minutes on Scale. You set the interval per monitor in seconds, so slow moving pages stay cheap while one critical page runs at your plan floor.
A delivery id, a payload version, the monitor and run identifiers, the fired_at timestamp, a diff object holding fields_changed, before and after, and the monitor name and URL. It is signed with HMAC-SHA256 in the Verid-Signature header, formatted t=timestamp,v1=signature, so your endpoint can verify it came from Verid before acting on it.
Delivery is attempted six times: immediately, then after 5 minutes, 15 minutes, 30 minutes, 1 hour and 2 hours. If all six fail the attempt moves to a dead letter queue, where the full payload is kept and can be replayed from the dashboard or the API once your endpoint is healthy again.
Two mechanisms, and the first does most of the work. Name only the fields you care about rather than diffing the whole page, so rotating banners, timestamps and session ids never enter the diff because they were never extracted. Then pick a predicate that encodes the threshold, for example a 5 percent decrease rather than any change at all.
Yes, as a separate layer. Add a visual config to a monitor and each check also screenshots the page and pixel diffs it against the previous shot, with its own change threshold and optional ignore selectors. That layer has its own per plan caps because screenshots cost more to run and store.
Free is permanent, needs no card, and includes 5 monitors on a once a day check with 14 days of run history, one delivery destination per monitor and one visual monitor. API access is included on every plan including Free, so you can integrate before you pay anything.