Checks the pages you list each day and emails you when the wording actually changes, with a note on what changed.
Uses
Claude
Gmail
Google Sheets
Jina AI
Tell me when a page I watch changes
Every day, each page listed in your watch list sheet is read, compared with the copy saved at the last check, and you are emailed only when the wording has genuinely changed, with a short note saying what is different.
The alert names the page, says what changed, quoting old and new wording where it can, and gives the address. Spacing is flattened before comparing and only the opening of the page is compared, so formatting churn and footers further down cannot trigger an alert. When the text does differ, the AI judges whether a person would care, so a changed timestamp or view count is not reported. A page that cannot be read or comes back with too little text is never treated as a change, and the saved copy is kept, so a blocked request cannot wipe out what the page really said. The first check of a page just saves a copy.
Before the first run you need a sheet with a tab of four columns, 'Page', 'URL', 'Last Checked' and 'Last Snapshot', and you provide only the first two.
watchListUrl to blank.Set watchListUrl to blank.watchListTab to Pages.Set watchListTab to Pages.alertEmail to blank.Set alertEmail to blank.excerptSize to 4000.Set excerptSize to 4000.minTextLength to 200.Set minTextLength to 200.maxPages to 25.Set maxPages to 25.watchListUrl. Save it as watchList.Read the rows of watchListUrl. Save it as watchList.pages to watchList.rows.Set pages to watchList.rows.checkedOn.Get the current date and time and save it as checkedOn.watchedPage in pages:For each watchedPage in pages:pageName to blank.Set pageName to blank.pageUrl to blank.Set pageUrl to blank.savedSnapshot to blank.Set savedSnapshot to blank.columnsFound to false.Set columnsFound to false.columnErrorIf a step below fails, save the error as columnErrorwatchedPage.Page and save it as pageName.Trim watchedPage.Page and save it as pageName.watchedPage.URL and save it as pageUrl.Trim watchedPage.URL and save it as pageUrl.watchedPage["Last Snapshot"] and save it as savedSnapshot.Trim watchedPage["Last Snapshot"] and save it as savedSnapshot.columnsFound to true.Change columnsFound to true.watchedPage.row_number as text - this tab needs the columns Page, URL, Last Checked and Last Snapshot, in that order. Check the header row.Show in the output: Skipped row watchedPage.row_number as text - this tab needs the columns Page, URL, Last Checked and Last Snapshot, in that order. Check the header row.columnsFound is noIf columnsFound is nopageUrl is emptyIf pageUrl is emptypageUrl doesn't start with httpIf pageUrl doesn't start with httppageName is emptyIf pageName is emptypageName to pageUrl.Change pageName to pageUrl.fetchedText to blank.Set fetchedText to blank.readErrorIf a step below fails, save the error as readErrorpageUrl with Jina. Save it as readResult.Read the page at pageUrl with Jina. Save it as readResult.readResult.content and save it as fetchedText.Trim readResult.content and save it as fetchedText.pageName this time. The saved copy was left untouched and no alert was sent: readError as text.Show in the output: Could not read pageName this time. The saved copy was left untouched and no alert was sent: readError as text.fetchedText is empty or the length of fetchedText is less than minTextLength…If fetchedText is empty or the length of fetchedText is less than minTextLength…pageName to be a real read, so the saved copy was left untouched and nothing was sent.Show in the output: Too little text came back for pageName to be a real read, so the saved copy was left untouched and nothing was sent.
with in fetchedText with
replaced by and save it as lineBreaksFlattened. with in lineBreaksFlattened with replaced by with replaced by and save it as spacingTidied.Replace with in lineBreaksFlattened with replaced by with replaced by and save it as spacingTidied.spacingTidied with replaced by with replaced by and save it as comparableText.Trim spacingTidied with replaced by with replaced by and save it as comparableText.comparableText to excerptSize multiplied by 1 characters and save it as currentSnapshot.Shorten comparableText to excerptSize multiplied by 1 characters and save it as currentSnapshot.savedSnapshot is emptyIf savedSnapshot is emptypageName. From now on you will hear about it only when the wording changes.Show in the output: Saved the first copy of pageName. From now on you will hear about it only when the wording changes.promptIntro to:Set promptIntro to:You are watching one web page for changes that a person would care about. Below are two plain-text copies of the same page: the copy saved at the last check, and the copy just fetched.
Ignore incidental differences entirely. These do NOT count as a change: adverts and promoted items, timestamps and 'last updated' lines, view or visitor counts, stock and seat counters, cookie or consent banners, navigation and footer links, the order of testimonials, reviews or news items, and any difference in spacing or formatting alone.
A change to the page's substance DOES count: wording, a price or fee,...promptSaved to Set promptSaved to
THE COPY SAVED AT THE LAST CHECK:
savedSnapshot.promptFetched to Set promptFetched to
THE COPY JUST FETCHED:
currentSnapshot.comparePrompt to promptIntro@promptSaved@promptFetchedSet comparePrompt to promptIntro@promptSaved@promptFetched
Answer in one of exactly two ways and write nothing else.
If nothing substantive differs, reply with the single line:
NO CHANGE
Otherwise reply with one line beginning 'CHANGED:' followed by at most 50 words saying precisely what is different, quoting the old and new wording where you can. For example: CHANGED: the standard plan price went from $19 to $29 per month.verdict to blank.Set verdict to blank.compareErrorIf a step below fails, save the error as compareErrorcomparePrompt. Save it as comparison.Ask Claude: comparePrompt. Save it as comparison.
Maximum response length: 300comparison.response_text and save it as verdict.Trim comparison.response_text and save it as verdict.pageName this time: compareError as text.Show in the output: Could not compare pageName this time: compareError as text.verdict starts with CHANGEDIf verdict starts with CHANGEDverdict with CHANGED: replaced by and save it as whatChanged.Trim verdict with CHANGED: replaced by and save it as whatChanged.emailSubject to Page changed: pageName.Set emailSubject to Page changed: pageName.bodyOpening to pageName has changed since the last check.Set bodyOpening to pageName has changed since the last check.
What changed:
.bodyDetail to whatChangedSet bodyDetail to whatChanged
.bodyWhere to Page: pageUrlSet bodyWhere to Page: pageUrl
Checked: checkedOn
.bodyClosing to The new wording is now the saved copy, so you will only hear from this workflow the next time this page changes..Set bodyClosing to The new wording is now the saved copy, so you will only hear from this workflow the next time this page changes..emailBody to bodyOpening plus bodyDetail plus bodyWhere plus bodyClosing.Set emailBody to bodyOpening plus bodyDetail plus bodyWhere plus bodyClosing.alertEmail.Send an email to alertEmail.
Subject: emailSubjectemailBodypageName changed only in ways that do not matter, so nothing was sent. The saved copy was refreshed.Show in the output: pageName changed only in ways that do not matter, so nothing was sent. The saved copy was refreshed.pageName - the saved copy was left exactly as it was, so the next run looks at this page again.Show in the output: No clear answer for pageName - the saved copy was left exactly as it was, so the next run looks at this page again.watchedPage.row_number of watchListTab in watchListUrl.Overwrite row watchedPage.row_number of watchListTab in watchListUrl.