Integrate Context.dev's Monitors API in your app
Prerequisites
-
A Context.dev API key. Sign up at context.dev/signup, copy the key from the dashboard (prefix
ctxt_secret_), and export it: -
An SDK (optional). Install for your language, or skip the install and call directly with
curl:
Create a monitor
A monitor is defined by four things: atarget (what to watch), a change_detection strategy (how to compare), a schedule (how often), and an optional webhook (where to notify). This creates a monitor that checks a pricing page every 6 hours:
sample response
Request parameters
Pick a target
Each target type pairs with a supported detection strategy. Supported combinations:page + exact, page + semantic, sitemap + exact, and extract + semantic. Unsupported pairs (for example sitemap + semantic) are rejected with a 400.
Page: did this page’s text change?
Watches a single URL and diffs the visible page text. Whitespace is normalized by default (normalize_whitespace: true).
Page + semantic: alert only on changes you care about
Addtarget.instructions — a plain-language goal describing which page changes matter — and Context.dev judges each confirmed diff against that goal, so ticker widgets, “as of” timestamps, and rotating testimonials stay quiet while a real edit fires. When instructions is set and you omit change_detection, semantic detection is inferred; passing change_detection: { type: "exact" } alongside instructions is rejected, and semantic page detection without instructions is rejected too.
instructions. Small numeric drift on values you didn’t ask about is ignored; watched numeric changes (for example a price moving from $29 to $49) are promoted even when the delta is small.
Tune sensitivity with change_detection.confidence_threshold (0–1, default 0.75) — raise it to only get notified about changes the judge is more certain matter.
Sitemap: were URLs added or removed?
Watches a sitemap for URL additions and removals, ideal for catching new blog posts, product pages, or docs. URLs are normalized and scoped to the monitored site and its subdomains; on a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps.include / exclude accept URL path patterns, and max_urls caps tracking at up to 10,000 URLs (default 5,000).
Extract: did the data I care about change?
Watches the meaning of a site, not its markup. A crawl guided by yourinstructions (and optional JSON schema, the same shape the Extract API uses) selects up to max_pages relevant pages; each run re-checks exactly those pages and judges confirmed changes against your instructions, so a reworded paragraph doesn’t fire, but a new pricing tier does.
The schema does three things: it steers which pages get selected for tracking, it gives the change judge extra context on which changes matter (alongside your instructions), and it defines the shape of the data snapshot on the monitor’s baseline (refreshed by the re-discovery crawl, at most about once a day). It is not a response format for alerts — change events and webhook payloads always contain diffs, summaries, and evidence excerpts, never data shaped by your schema. Skip it and a general summary + key-points schema is used.
confidence_threshold (0–1, default 0.75) to only get notified about changes the judge is more certain matter. The tracked page set is refreshed by a periodic re-discovery crawl (at most about once a day).
Receive webhooks
Context.dev delivers two webhook events. Subscribe to either or both by settingwebhook.events when you create or update a monitor.
Each subscribed event fires independently, so a change-run with both events subscribed produces two deliveries — one per event — with matching
data.change records.
Choose which events to receive
webhook.events is a list. When you create a webhook, omitting it defaults to ["change.detected"] for backward compatibility. When you update an existing webhook, omitting events preserves its current subscription; send the list explicitly to change it.
change.detected payload
Sent after a run detects a change. data.change is the full change record — the same one returned by GET /monitors/changes/{change_id}.
change.detected payload
run.completed payload
Sent after every completed run when your monitor subscribes to run.completed. data.run is a snapshot of the run at delivery time. data.change is the same full change record delivered by change.detected, or null when the run found no change (including baseline runs, where baseline_created is true).
run.completed payload — no change
Verify the signature
Every delivery includes anX-Context-Signature: t=<unix>,v1=<hmac> header, where the HMAC is SHA-256 over "{t}.{rawRequestBody}" keyed by your monitor’s webhook secret. Recompute it with a constant-time compare and reject stale timestamps to prevent replays:
X-Context-Event header (change.detected or run.completed) so you can route events without parsing the body first, and an X-Context-Id header that matches the payload’s top-level id.
Inspect delivery status
Each monitor run records the outcome of every webhook it attempted, so you can debug misbehaving endpoints without wiring up your own logs. The run object returned byGET /monitors/{monitor_id}/runs (and GET /monitors/runs) includes a webhook_deliveries array — one entry per subscribed event that fired for that run:
status is one of:
event_id matches the X-Context-Id header on the delivery, so you can correlate a run with the exact request your server received. webhook_deliveries is omitted when no webhook was attempted, including runs that predate delivery tracking.
The legacy top-level
webhook_delivery field is still returned for backward compatibility. It contains the change.detected attempt when present; otherwise it contains the run’s sole attempted delivery, such as run.completed for a monitor subscribed only to that event. It is deprecated — new integrations should read webhook_deliveries.Track consecutive delivery failures
Beyond per-run delivery status, the monitor object itself carries a rolling health signal:webhook_failure counts consecutive delivery-attempting runs with at least one failed delivery so you can surface a broken endpoint in your own dashboard. Runs that attempt no webhook leave the streak unchanged.
consecutive_failures— number of consecutive delivery-attempting runs whose webhooks did not fully succeed. When a run delivers multiple subscribed events, a single failure among them advances the counter by one.last_status— outcome of the most recent failed delivery:rejected(non-2xx response),failed(no HTTP response), orskipped_unsafe_url(the URL failed the public-endpoint safety check).last_message— human-readable description of the most recent failure.last_failed_at— ISO timestamp of the last failed attempt.
webhook_failure is null when deliveries are healthy or no webhook is configured. It clears after the next run that attempts at least one webhook and delivers every attempt successfully, and resets when you change or remove the webhook URL via PATCH /monitors/{monitor_id}.
Delivery retries
Every webhook delivery is retried on network errors,5xx responses, and 429 rate-limit responses. When the receiver returns a numeric Retry-After header, Context.dev waits that long before the next attempt (capped at 10 seconds). If every attempt fails, the run’s webhook_deliveries[].status and the monitor’s webhook_failure record the outcome.
Get notified when your endpoint breaks
Onceconsecutive_failures reaches 3, Context.dev emails every user in your organization to flag the broken endpoint. You get one email per streak — a run that attempts at least one webhook and delivers every attempt successfully re-arms the notification for the next episode. When several monitors deliver to the same URL, the notification is coalesced: one email covers the endpoint for 24 hours instead of one per monitor.
Read changes over the API
No webhook? Poll instead. List a monitor’s detected changes (paginated viacursor, filterable by since / until / tag), then fetch full details for any change: text diff for page monitors, added_urls / removed_urls for sitemaps, and evidence with before/after snapshots plus confidence and importance for semantic monitors.
GET /monitors/changes (client.monitors.listAccountChanges()), and the same pair for run history: GET /monitors/{monitor_id}/runs and GET /monitors/runs.
Run, pause, and manage
Trigger an off-schedule check (queued and processed asynchronously), pause and resume, or delete:- Updating
targetorchange_detectionresets the baseline. The next run re-captures it instead of reporting a spurious change. Unsupported target/detection combinations are rejected — see Pick a target for the allowed pairs. - Failures don’t kill monitors. A failed run flips
statustofailed(withlast_errorpopulated), but the monitor keeps running on schedule and flips back toactiveon the next success. Monitors auto-pause after repeated consecutive failures or insufficient-credit skips; resume by updatingstatustoactive. - A failed baseline run pauses the monitor immediately. The baseline is the first run after you create the monitor or edit its
targetorchange_detection— if that run fails, the configuration itself is probably wrong, so Context.dev setsstatustopausedand populateslast_erroron the first failure instead of burning retries. Fix the configuration (or leave it alone if the source was transiently down) and resume withstatus: "active"to clearlast_errorand re-run the baseline. - Empty, blocked, error, parked, or collapsed pages don’t overwrite the baseline. If a run’s snapshot looks degraded — a bot-block interstitial, an error page, a parked domain, or a page that shrank to almost nothing — the run fails cleanly instead of poisoning the baseline with a bad snapshot and firing a spurious disappearance/recovery alert pair on the next healthy run.
Pricing
Creating, listing, updating, and reading monitors, runs, and changes is free. You pay per run, priced like the equivalent public endpoint:
Monitor spend appears on the usage page under the path
/v1/monitors/run, and GET /monitors/credit-usage returns a per-monitor credit and run breakdown over any time window:
cURL
Use cases
- Competitor price tracking: a
pagemonitor on each competitor’s pricing page, with the webhook posting diffs into Slack. Addtarget.instructionslike “report pricing or plan availability changes, ignore counters and testimonials” to make it semantic and skip the cosmetic churn. - Content ops: a
sitemapmonitor on a competitor’s blog to catch new posts and landing pages the day they ship. - Positioning intelligence: an
extract+semanticmonitor with instructions like “track packaging, pricing, and headline feature claims”; cosmetic rewording won’t fire it. - Compliance watch: an
extractmonitor over terms or privacy pages, alerting only when obligations meaningfully change. - Your own site: a
sitemapmonitor as a tripwire for pages accidentally dropping out of your sitemap after a deploy.
Next steps
Monitors API Reference
Every endpoint, parameter, and response schema.
Extract Structured Data
The schema-driven extraction that powers extract monitors.
Best Practices
Caching, error handling, and key hygiene.
Troubleshooting
Status codes, retry patterns, and common errors.