Website Text & DOM Change Monitor
Pricing
from $0.48 / 1,000 page snapshots
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
Maintained by CommunityActor 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
- Add a public static HTML URL in
pages. - Run the Actor and open the default dataset.
- Save the successful result's
htmlandobservedAtin your own storage. - On the next run, pass that HTML as
previousHtmlfor the same URL. - Review
status,textChanges,domChangesand both truncation flags.
{"pages": [{ "url": "https://example.com" }],"maxDiffEntries": 200}
Input parameters
| Field | Meaning |
|---|---|
pages | Required array of 1–20 unique URL objects. Sequential, no crawling. |
pages[].url | Anonymous public HTTP(S) URL on standard ports; no credentials. |
pages[].previousHtml | Optional 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. |
maxDiffEntries | Integer 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
| Field | Meaning |
|---|---|
url, finalUrl | Requested and final public URL. |
observedAt | UTC timestamp for this observation. Keep your own prior timestamp alongside stored HTML. |
status | baseline, unchanged, changed, or error. |
html | Original current HTML for your next comparison. |
currentText | Normalized body text with block-separated lines. |
textHash, domHash | SHA-256 of normalized current text and element-path representation. |
previousTextHash, previousDomHash | Prior hashes; null on baseline. |
textChanges | `{type: "added" |
domChanges | {type, path, previous, current} records for inserted, deleted or changed elements. |
textTruncated, domTruncated | True when the corresponding diff is incomplete. |
httpStatus, bytes | Successful response status and fetched byte count. |
error | Page 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 tier | Price 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 osfrom apify_client import ApifyClientclient = 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.
Related Actors
- Website HTML & Text Change Monitor: persistent snapshot-oriented monitoring workflow.
- Website Visual Change Monitor: screenshot-oriented comparisons.
- Public Webpage HTML Downloader: HTML acquisition rather than caller-supplied structural diffs.