Get API key

Watch API

Rendex Watch monitors a URL for changes. You create a watch, and Rendex renders the page in real Chrome on your schedule and tells you when it changes. By default it catches any real change — big or small, visual or text: every check runs a before/after visual diff (returning a highlighted overlay) and a full-page text diff, and alerts you over an HMAC-signed webhook or email if either one moves. You don't have to pre-guess what counts — a swapped hero image and a single changed price are both caught. It uses the same rdx_ key and credit pool as the rest of Rendex — see the product overview or how it compares to other monitors.

Prefer no code? You can create and manage watches point-and-click in your dashboard — no API required. This page covers the REST API for developers and AI agents.

Endpoints

All endpoints live under /v1/watches and require your API key.

MethodPathDescription
POST/v1/watchesCreate a watch (charges 1 credit for the baseline capture)
POST/v1/watches/testDry-run a config before creating it — renders + returns reachability + a screenshot (1 credit, refunded if unreadable)
GET/v1/watchesList your watches (filter with ?status=active|paused|all — default all)
GET/v1/watches/:idFetch one watch
GET/v1/watches/:id/runsRun history with signed before/after/overlay URLs
POST/v1/watches/:id/runRun a check now (charges 1 credit)
PATCH/v1/watches/:idUpdate, pause, resume, or change the URL
DELETE/v1/watches/:idDelete a watch and its run history

Create a watch

POST /v1/watches
curl -X POST https://api.rendex.dev/v1/watches \
  -H "Authorization: Bearer rdx_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://stripe.com/pricing",
    "intervalMinutes": 180,
    "webhookUrl": "https://your.app/hooks/rendex",
    "notifyEmail": "you@youraccount.com"
  }'
# That's it — diffMode defaults to "both", so this catches any visual OR text change.

url is the only required field. intervalMinutes is the check frequency — an integer from 5 to 43200 (30 days, the longest gap between checks), default 1440 (daily), subject to your plan's floor. webhookUrl and notifyEmail are both optional alert channels — see How you get alerted below. Everything about what counts as a change has a sensible default (next section), so a bare { "url": "…" } is a complete, working watch.

What counts as a change

By default a watch detects any real change, and you don't configure anything to get that. Each check compares the page to its baseline two independent ways and alerts if either moves:

  • Visual — a real-Chrome before/after pixel diff of the whole page, returning a highlighted overlay of what moved. Detection is region-based: it looks for a changed region (a swapped image, a new banner, a moved price), so a small change on a very long page still trips — it is nota “what percent of the page changed” threshold that a price line on a 20,000px page would never reach.
  • Text — a diff of the page's full visible text(everything a human reads: pricing tables, nav, footer — not just the article body), reporting the exact added/removed lines. This is what reliably catches a $7.99 → $19.99 price change regardless of where it sits on the page.

Two knobs tune this, but most watches leave them at their defaults:

FieldWhat it does
diffModeboth (default) runs the visual and text diffs together and alerts on either. visual or text narrow it to a single signal — e.g. visual for a page whose wording changes constantly but whose layout matters, or textto monitor a PDF (which can't be pixel-diffed) or ignore purely visual churn.
thresholdA visual sensitivity selector (0–1, default 0.01). Low (the default) alerts on any change, including a small one on a long page. A value of 0.06 or higher switches to a blunt whole-page ratio gate — only a major visual change (a big fraction of the page) alerts. Handy for a page that redesigns often and you only want to hear about large shifts. It does not affect text detection.

Two combinations are rejected at create time: a text (or both) diff can't use geo-targeting (a geo render returns no extracted text), and PDF output can't be visually diffed (pair format: pdf with diffMode: text).

Three more optional create fields:

FieldWhat it does
nameOptional label (up to 120 chars) shown in the dashboard and run history.
pausedCreate the watch paused (default false). A paused watch captures no baseline and draws no credit until you resume it with PATCH /v1/watches/:id (paused: false). Note: paused watches still count toward your plan's watch limit.
aiSummaryOpt this watch into an AI-written“what changed” summary (Pro+). Every plan already gets a plain-English summary on each change (a templated line); on Pro and Enterprise, setting this to trueupgrades a text change's summary to a one-sentence AI-written description in the summary payload field, the email, and the dashboard.

Beyond the scoping knobs below, renderParams accepts the full standard capture param set — format (png/jpeg/webp/pdf), width/height, fullPage (defaults to true for watches, so the whole page is monitored), device, deviceScaleFactor, darkMode, blockAds, blockCookieBanners, hideSelectors, waitUntil, delay, timeout, and geo / geoCity / geoState (Pro+) — applied on every check. Inline-source params (url/html/markdown) and credential knobs (cookies/headers/css/js) are not accepted; the watch's target is the top-level url.

Scope what you watch

By default a watch diffs the whole rendered page. A few renderParams knobs narrow the visual diff and quiet the text diff so you only hear about changes you care about:

renderParams
{
  "url": "https://competitor.com/pricing",
  "diffMode": "visual",
  "renderParams": {
    "selector": ".pricing .pro-plan .price",
    "ignoreRegions": [{ "x": 0, "y": 0, "width": 1200, "height": 90 }]
  }
}
ParamWhat it does
selectorCapture and diff ONLY this CSS selector — watch just one price, banner, or section instead of the whole page. Visual-first (in text mode the extracted text is still the full page).
ignoreRegionsRectangles ({x, y, width, height} in pixels on the captured image) masked in both captures before the diff — silence dynamic zones like an ad slot or a clock so they never trip a change. Applies to the visual diff (visual and both modes).
ignoreTextSubstrings or /regex/ patterns stripped from both texts before the text diff — silence boilerplate like timestamps, view counts, or session tokens. Up to 50; applies to text and both modes.
minTextCharsMinimum number of added + removed characters for a text change to count — ignore tiny edits below the size you care about.
suppressWhilePresentWhile the page text contains any of these markers (e.g. “Out of stock”, “Loading”), the run is treated as unchanged — no alert, no baseline drift — until the marker clears. Up to 20.

If a site blocks monitoring

Rendex Watch fetches pages with a self-identifying User-Agent (the RendexWatch token — see the crawler page) so site owners can recognize it. If a site blocks that identity, set renderParams.uaMode to control how the monitor presents itself:

uaModeBehavior
autoDefault. Identify as RendexWatch, and if the page looks blocked, retry once with a standard browser identity.
identifyAlways identify as RendexWatch; never fall back. Most polite — best when you control the target site.
stealthAlways present a standard browser identity. Use only when a site blocks the identified monitor.

Rendex won't diff an error page against your baseline: if a check renders an HTTP 4xx/5xx, the run is recorded as a failure (and its credit refunded) instead of firing a false “changed” alert.

When a check fails

When a scheduled check can't complete, Rendex sends one “We couldn't check your page” email (a watch.error event) with a plain-language reason and a How to fix line. A few things soften the edges before you ever hear about it:

  • Transient blips are retried.A slow load, a network hiccup, or a one-off server 5xx is retried once within the same check before it counts as a failure — a single bad moment (say, during the site's own deploy) won't email you.
  • Alerts are edge-triggered. You get one error email when a watch starts failing, not one per check — and one watch.recoveredwhen it's healthy again. A day-long outage is a single pair of emails.
  • Failed checks are free. The credit is refunded, so a failing watch never drains your pool.

Each reason and its best workaround:

What the email saysWhat it meansBest fix
The site blocked automated accessThe page served a 403/429 or a bot challenge (e.g. Cloudflare) instead of content.Set uaMode to stealth (above). If it still fails, the site likely requires login or a CAPTCHA — monitor its API/RSS instead, or watch a different page. This is the only case where stealth mode helps.
The page was too large to capture and compareA very tall or high-resolution full-page capture exceeded the size a browser render can diff. Rendex auto-downscales large pages to fit, so you'll only see this on extreme cases (a very wide custom viewport, or an enormous page).Lower renderParams.width, set fullPage: false to watch just the viewport, or add a selector to watch one section. Any of these keeps the capture small.
The page took too long to loadThe page didn't finish loading within the timeout (already retried once).Raise renderParams.timeout (up to 60s), set bestAttempt: true to capture a partial render, or use a lighter waitUntil (e.g. domcontentloaded).
The element wasn't foundA selector this watch captures or waits for never appeared on the page.Confirm the selector still exists, loosen it, or remove it to watch the full page instead.
The site returned a server errorThe page responded with an HTTP 5xx — a problem on the site's side (already retried once).Usually temporary — the next check often recovers. If it persists, confirm the page loads in a normal browser.
We couldn't reach the pageDNS or a connection error — the URL didn't resolve or refused the connection.Confirm the URL is public and loads in a browser. Private, localhost, and non-resolving addresses can't be reached.

How you get alerted

A watch reaches you over two independent channels. Turn on either or both by setting a field when you create the watch (or later with PATCH /v1/watches/:id) — both fields are optional and accept null on update to switch a channel back off:

ChannelTurn it onPlanWhat arrives
EmailSet notifyEmail — or leave it unset and alerts go to your account email. For anti-abuse, notifyEmail must be your own account email; any other address is rejected with 400 VALIDATION_ERROR.Every plan (including Free)A formatted email from Rendex Watch <notifications@mail.rendex.dev> led by a crop of just what changed, a one-line plain-English summary of the change, before/after links, and a button to the run.
WebhookSet webhookUrl to an HTTPS endpoint.Starter and upAn HMAC-signed POST you verify and route into any pipeline (payload below). No code? Point it at a Zapier, Make, or n8n catch hook.

Both channels fire on the same three events — watch.changed, watch.recovered, and watch.error — and are edge-triggered: one alert when the page changes, one when a failing watch recovers, and one when it first starts failing, never one per check. With neither field set, a watch still records its run history; it just doesn't push a notification.

Change webhooks

On a change, Rendex POSTs a signed payload to your webhookUrl. Verify the x-rendex-signature header against your signing secret exactly as you do for async render jobs (see Webhooks).

watch.changed
{
  "event": "watch.changed",
  "status": "changed",
  "watchId": "b1de4f2a-9abc-4def-8901-2a3b4c5d6e7f",
  "runId": "c2ef5a3b-0d1e-4a2b-9c3d-4e5f6a7b8c9d",
  "url": "https://stripe.com/pricing",
  "changed": true,
  "diffScore": 0.0412,
  "diffPixels": 18240,
  "summary": "A new enterprise tier was added to the pricing table.",
  "textDiff": { "added": ["New enterprise tier"], "removed": [] },
  "beforeUrl": "https://api.rendex.dev/v1/images/…",
  "afterUrl":  "https://api.rendex.dev/v1/images/…",
  "diffOverlayUrl": "https://api.rendex.dev/v1/images/…",
  "cropUrl": "https://api.rendex.dev/v1/images/…",
  "changedRegion": { "x": 0.08, "y": 0.42, "width": 0.5, "height": 0.12 },
  "completedAt": "2026-06-15T12:00:00Z"
}
A Rendex Watch change alert showing the before capture, the after capture, and the highlighted diff overlay, with the diffScore and changed-region count from the watch.changed webhook payload.

The image URLs are short-lived signed links: the baseline (beforeUrl), the new capture (afterUrl), the highlighted full-page overlay (diffOverlayUrl), and cropUrl — a tight crop of just the changed region, ready to drop straight into Slack or a ticket. changedRegion gives that region as normalized (0–1) page coordinates. diffScore is the fraction of the page that changed (0–1), reported for reference — a changed alert fires on a meaningful region or a text change (see What counts as a change), so a genuine small change can carry a tiny diffScore and still be a real alert.

For a text change, textDiff carries the exact added/removed lines — a text-only workflow (webhook, n8n, Zapier) can post them with no image and no dashboard. And summary is a one-line plain-English “what changed” sentence on every change, every plan: for a text change it summarizes the added and removed lines; for a visual-only change with no text delta, it describes where and how much changed. Pro and Enterprise can opt a watch into an AI-written sentence (see aiSummary); Free and Starter get a clear templated line. So every change carries words, not just a percentage, whichever channel you use. Events are edge-triggered: a long outage sends one watch.error, and recovery sends one watch.recovered — not one per check.

Reading run history

GET /v1/watches/:id/runs
curl https://api.rendex.dev/v1/watches/b1de4f2a-9abc-4def-8901-2a3b4c5d6e7f/runs \
  -H "Authorization: Bearer rdx_your_key"
# → run history, newest first, each with signed before/after/overlay URLs

Each run reports changed, diffScore (the 0–1 change fraction), diffPixels, signed beforeUrl / afterUrl / diffOverlayUrl, textDiff, and creditsCharged (0 after a refund). These are the same field names the watch.changed webhook delivers.

Plan limits

PlanWatchesFastest intervalWebhooks
Free1DailyEmail only
Starter10Every 3 hoursYes
Pro50Every 30 minutesYes
Enterprise1,000Every 5 minutesYes

Your plan's watch cap (above) counts all your watches — active and paused. Separately, no single host can have more than 10 active watches, on any plan — an anti-abuse guardrail that stops a large plan from pointing dozens of watches at one site. It only comes into play on plans that allow more than 10 watches (Pro and Enterprise); on Free (1 watch) and Starter (10) your plan cap is already the lower, binding limit, so this never applies. A create, resume, or URL change that would exceed it returns 403 WATCH_HOST_LIMIT_REACHED — pause or delete a watch on that host, or watch a different one.

Each check draws one credit from your shared pool; a failed check is refunded. There is a hard cap, never an overage — see pricing.

If your shared credit pool runs out, a scheduled check pauses the watch rather than failing silently — the watch is never deleted, just deactivated. Resuming is manual: once your credits refill on your next billing cycle (or you upgrade), resume the watch from the dashboard or with a PATCH (paused: false) and checks continue.

Was this page helpful?