Website Text & DOM Change Monitor avatar

Website Text & DOM Change Monitor

Pricing

from $0.48 / 1,000 page snapshots

Go to Apify Store
Website Text & DOM Change Monitor

Website Text & DOM Change Monitor

Compare public static webpages with supplied previous HTML. Export normalized text, text/DOM hashes, added and removed lines, changed element paths and reusable snapshots for recurring audits.

Pricing

from $0.48 / 1,000 page snapshots

Rating

0.0

(0)

Developer

Automation Lab

Automation Lab

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Categories

Share

Compare public static HTML pages with HTML snapshots you supply. Website text DOM change monitoring returns normalized body text, content and structure hashes, added/removed lines, and inserted/deleted/changed element paths. Use it for recurring wording reviews, compliance audits and website content pipelines—not screenshot comparisons or browser automation.

The first observation establishes a baseline. You own cross-run storage: save each returned html, then supply it as previousHtml next time. The Actor never silently retrieves another user's history or shares a global baseline.

Who is it for?

  • Compliance teams reviewing public terms and policy wording.
  • Content operations comparing documentation and landing pages.
  • Analysts auditing static competitor content with source-attributed changes.
  • Developers building a snapshot archive in their own database.

Why use this Actor?

A changed hash tells you something changed; line and element records show where. This Actor compares the whole accepted page, including wording beyond the first matching field. Attribute-only DOM changes can be detected even when body text stays the same.

Unlike the related Website HTML & Text Change Monitor, this workflow accepts an explicit previous HTML document and reports positional DOM element differences. Unlike the Website Visual Change Monitor, it does not render or compare screenshots.

Get started

  1. Add a public static HTML URL in pages.
  2. Run the Actor and open the default dataset.
  3. Save the successful result's html and observedAt in your own storage.
  4. On the next run, pass that HTML as previousHtml for the same URL.
  5. Review status, textChanges, domChanges and both truncation flags.
{
"pages": [{ "url": "https://example.com" }],
"maxDiffEntries": 200
}

Input parameters

FieldMeaning
pagesRequired array of 1–20 unique URL objects. Sequential, no crawling.
pages[].urlAnonymous public HTTP(S) URL on standard ports; no credentials.
pages[].previousHtmlOptional prior HTML, at most 524288 UTF-8 bytes per page and 1048576 bytes combined per run. Omit for a baseline; "" means an explicitly empty prior snapshot.
maxDiffEntriesInteger 1–500, default 20, separately applied to each diff array on every page. No unlimited/zero mode.

Example comparison input:

{
"pages": [{
"url": "https://example.com",
"previousHtml": "<html><body><h1>Previous wording</h1></body></html>"
}],
"maxDiffEntries": 100
}

Previous HTML must belong to the same intended URL and observation policy. The Actor cannot verify the history you supply. It does not fetch Wayback snapshots or infer a previous version from a timestamp.

Output fields

FieldMeaning
url, finalUrlRequested and final public URL.
observedAtUTC timestamp for this observation. Keep your own prior timestamp alongside stored HTML.
statusbaseline, unchanged, changed, or error.
htmlOriginal current HTML for your next comparison.
currentTextNormalized body text with block-separated lines.
textHash, domHashSHA-256 of normalized current text and element-path representation.
previousTextHash, previousDomHashPrior hashes; null on baseline.
textChanges`{type: "added"
domChanges{type, path, previous, current} records for inserted, deleted or changed elements.
textTruncated, domTruncatedTrue when the corresponding diff is incomplete.
httpStatus, bytesSuccessful response status and fetched byte count.
errorPage error message; successful snapshot fields are absent on error rows.

Baseline and unchanged results have empty change arrays. Every successful observation is a useful page result, including unchanged pages. Error rows are diagnostic and have no per-item event charge.

Output example

A real first-observation excerpt from an example-domain control (HTML, hashes and transport fields omitted here for readability):

{
"url": "https://example.com",
"status": "baseline",
"currentText": "This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.",
"textChanges": [],
"domChanges": [],
"textTruncated": false,
"domTruncated": false
}

Use the prefilled Books to Scrape URL for the public catalogue demonstration. Source content can change; always inspect the current results.

How comparison works

Scripts, styles, noscript and template contents are removed from both snapshots. Body whitespace is normalized; inline wording stays together and common block elements establish lines. This is text from static HTML, not browser-computed visibility: CSS-hidden text can remain. There is no OCR, JavaScript execution, locale translation or semantic rewriting.

DOM comparison includes normalized element paths, sorted attributes and direct text. Paths such as /html[1]/body[1]/main[1]/p[2] use same-tag sibling positions. They are not stable node IDs or executable CSS selectors. An insertion can shift subsequent paths and produce multiple changed records. Attribute values, including class and style, are significant; comments and attribute order are not. Head title wording can change the DOM hash without changing body text.

A full accepted snapshot is hashed even if output differences are truncated. After trimming equal leading/trailing lines, changed regions of at most 400 lines use a minimal line diff with a 100 ms computation cap. Larger regions use a bounded replacement hunk (all old lines removed, all new lines added), which can include unchanged interior lines rather than guaranteeing minimal edits. A computation timeout sets textTruncated true and details can be empty. A hash mismatch still reports changed.

Limits and supported sources

  • Maximum 20 submitted pages per run, no links followed.
  • Maximum 524288 bytes for each current or previous HTML document; combined prior HTML is capped at 1048576 bytes per run. Split larger archives into smaller batches.
  • Maximum 5000 normalized elements, 64 nesting levels and 5000 normalized text lines per snapshot.
  • At most three HTTP redirects, with the same safety checks at each hop.
  • Approximately 10-second page deadline; individual requests have at most 5 seconds and DNS lookup at most 2 seconds.
  • One retry for transient network reset, temporary DNS failure, HTTP 429 or 5xx; no retries for login/challenge/404 responses.
  • Only UTF-8/ASCII uncompressed HTML responses are supported; no files, compressed archives or arbitrary media.

Over-limit snapshots fail instead of pretending a partial hash represents the full page. There is no browser, login, custom proxy, cookie jar, screenshot, automatic alert, arbitrary dynamic-site guarantee or built-in historical archive. JavaScript-only/empty bodies fail; challenge pages and access restrictions must not be interpreted as authoritative business content. Validate source relevance downstream.

Public URL safety

Private, loopback, link-local, reserved, multicast and private DNS destinations are blocked. Every DNS answer must be public; the connection uses a pinned validated address while retaining the requested TLS hostname. Redirects are revalidated before connecting. Only HTTP/HTTPS on standard ports is accepted, without URL credentials. There are no subresource requests.

This prevents using the Actor as an internal-network fetcher. Do not supply confidential pages or put credentials in URL queries. Your requested URLs and fetched public content remain in your run dataset as part of the product output.

How much does it cost to compare public webpages?

Billing uses one start event per run and one item event per successful page snapshot. Baseline, changed and unchanged pages are charged equally; failed pages have no item event. Hashes, text and DOM details are included, not separately charged.

Start fee: $0.00005 per run. Successful page snapshot prices:

Store spend tierPrice per page
FREE$0.00092
BRONZE$0.00080
SILVER$0.000624
GOLD$0.00048
PLATINUM$0.00048
DIAMOND$0.00048

BRONZE examples including the start fee: one page $0.00085; five pages $0.00405; twenty pages $0.01605. At FREE tier, the estimated total for one successful observation including initialization is $0.00097. Spend tiers follow your qualifying aggregate monthly Apify Store spending, not a private per-Actor volume discount. Charges can stop collection at your configured spending limit; error rows or partial runs are not a promise of a complete batch. Estimates are not guaranteed invoices.

Integrations and recurring workflows

Schedule runs with Apify and use your own orchestration to load previous HTML from a database into each new input. Save successful current snapshots after each observation. Do not replace your last good baseline with an error row.

Send change arrays to a spreadsheet or compliance review queue. Use an Apify webhook or your integration platform after completion if you want notifications; the Actor itself sends no alerts. Compare hash values before loading large HTML into downstream language models. Keep a retention policy for your own archive.

API usage

Start once and retain the returned run ID:

curl -X POST \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H 'Content-Type: application/json' \
'https://api.apify.com/v2/acts/automation-lab~website-text-dom-change-monitor/runs' \
-d '{"pages":[{"url":"https://example.com"}]}'

JavaScript with the official Apify client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/website-text-dom-change-monitor')
.call({ pages: [{ url: 'https://example.com' }] });
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 20 });

Python:

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/website-text-dom-change-monitor').call(
run_input={'pages': [{'url': 'https://example.com'}]})
items = client.dataset(run['defaultDatasetId']).list_items(limit=20).items

Check terminal status and pagination before treating an export as complete.

Use through MCP

For Claude Code:

claude mcp add --transport http apify \
'https://mcp.apify.com?tools=automation-lab/website-text-dom-change-monitor'

Claude Desktop, Cursor and VS Code configuration:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=automation-lab/website-text-dom-change-monitor"
}
}
}

Configure authorized authentication in your client. Discover actual tools and input schemas using scoped tools/list; selection also adds run/data/abort tools, not exactly one tool. Example prompt: “Fetch example.com, establish a baseline and return its hashes; do not invent a prior snapshot.”

For an authorized invocation, start once and retain the run/status/storage IDs. call-actor waits at most 45 seconds (default 30). If pending, poll get-actor-run for that same run with 2, 4, 8 seconds then capped 10-second backoff and a 120-second total deadline. Set client transport timeout above the server wait plus margin. A timeout is not permission to restart; recover the known run or report uncertainty. At deadline report pending with the run ID. Failed runs may have partial rows, not a successful full result.

After success, use get-dataset-items with explicit fields, limit 20 and offset 0. Example consumer budget: at most 100 source rows and 64 KiB serialized UTF-8 result content total, whichever comes first. Enforce bytes host-side before model injection; omit/summarize oversized HTML with disclosure. If the client cannot intercept oversized responses, do not promise a hard byte guarantee. Track source offsets, report returned count, continuation offset and truncation. Store full exports outside model context. These consumer bounds do not change Actor collection limits. No universal token-saving claim is made.

Troubleshooting and FAQ

Why is the first run a baseline? No previousHtml was supplied. Save html and provide it next time. Scheduling alone does not create persistent history.

Why is text unchanged but status changed? Attributes, tags, structure or head title text changed. Inspect domChanges.

Why did many paths change? Positional paths shift when same-tag siblings are inserted/deleted. Review nearby paths together, not as stable identity events.

Why did a URL fail? Check HTTP status, HTML size, encoding, public-address restrictions and whether static content exists. This Actor cannot solve logins or browser challenges. Partial failures produce error rows; if every page fails, the run fails. Start charges can still apply.

Can I send huge documents or request unlimited differences? No. Reduce source scope outside the Actor or use another tool. The caps are strict and documented; truncation flags must be respected.

Legality and responsible use

Only monitor pages you are authorized to access. Respect website terms, robots policies, rate limits, copyright and applicable privacy law. Sequential bounded fetches are not permission to bypass access controls. This Actor is a technical comparison tool, not legal advice or a compliance certification.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days. Previous HTML is redacted from diagnostic reports. Do not put secrets in arbitrary HTML input fields. The runtime uses no AI, paid data provider or proxy. Apify hosts inputs, output datasets and logs under your account's retention settings; delete these through Apify when no longer needed. There is no Actor-global archive, cache or session store. Public targets receive ordinary HTTP requests. GlitchTip receives only scrubbed failure diagnostics, retained for 30 days. Store and delete your own snapshot archives according to your obligations.

For help, open an issue from this Actor's Apify Store page with a safe input and run link. Never include passwords or confidential snapshots.