Tracks where your pages rank each week, catches the ones slipping, and says what to change on each.
Uses
Claude
Google Sheets
Serper
Slack
Catch the pages slipping in Google each week
siteDomain to blank.Set siteDomain to blank.slackChannelId to blank.Set slackChannelId to blank.historySheetUrl to blank.Set historySheetUrl to blank.trackedPagesTab to Tracked pages.Set trackedPagesTab to Tracked pages.historyTabName to Rank history.Set historyTabName to Rank history.searchCountry to us.Set searchCountry to us.searchLanguage to en.Set searchLanguage to en.resultsToScan to 50.Set resultsToScan to 50.positionDropAlert to 3.Set positionDropAlert to 3.maxPagesToDiagnose to 5.Set maxPagesToDiagnose to 5.trackedPages to an empty list.Set trackedPages to an empty list.trackedReadErrorIf a step below fails, save the error as trackedReadErrorTry
historySheetUrl. Save it as trackedSheet.Read the rows of historySheetUrl. Save it as trackedSheet.trackedSheet.rows where item.URL isn't empty and item.Query isn't empty and save it as trackedPages.Keep each item in trackedSheet.rows where item.URL isn't empty and item.Query isn't empty and save it as trackedPages.On error
trackedTabErrorIf a step below fails, save the error as trackedTabErrorTry
trackedPagesTab in historySheetUrl.Create the tab trackedPagesTab in historySheetUrl.trackedPagesTab in historySheetUrl.Add a row to trackedPagesTab in historySheetUrl.On error
trackedReadError.message.Stop and report a problem: Could not read the '@trackedPagesTab' tab of your sheet. Check the link in 'historySheetUrl' and that the connected Google account can open and edit the sheet, then run this again. Reason: trackedReadError.message.trackedPages is emptyIf trackedPages is emptyYes
No
No steps
runDate.Get today's date and save it as runDate.historyRows to an empty list.Set historyRows to an empty list.historyHasHeader to false.Set historyHasHeader to false.historyReadErrorIf a step below fails, save the error as historyReadErrorTry
historySheetUrl. Save it as storedHistory.Read the rows of historySheetUrl. Save it as storedHistory.historyRows to storedHistory.rows.Change historyRows to storedHistory.rows.historyHasHeader to the length of storedHistory.headers is greater than 0.Change historyHasHeader to the length of storedHistory.headers is greater than 0.On error
createTabErrorIf a step below fails, save the error as createTabErrorTry
historyTabName in historySheetUrl.Create the tab historyTabName in historySheetUrl.On error
historyReadError.message.Stop and report a problem: Could not read the '@historyTabName' tab of your history sheet, so this week could not be compared with last week and nothing was written or posted. Check the link in 'historySheetUrl' and that the connected Google account can open the sheet, then run this again. Reason: historyReadError.message.historyHasHeader is noIf historyHasHeader is noYes
headerWriteErrorIf a step below fails, save the error as headerWriteErrorTry
historyTabName in historySheetUrl.Add a row to historyTabName in historySheetUrl.On error
No
No steps
rows and today and save it as previousReadings.Run Python code on rows and today and save it as previousReadings.serpSnapshots to an empty list.Set serpSnapshots to an empty list.searchFailures to an empty list.Set searchFailures to an empty list.trackedPage in trackedPages:For each trackedPage in trackedPages:searchErrorIf a step below fails, save the error as searchErrorTry
trackedPage.Query. Save it as searchResult.Search Google for trackedPage.Query. Save it as searchResult.serpSnapshots and save it as serpSnapshots.Add a record of keyword, url, and serp to the end of serpSnapshots and save it as serpSnapshots.On error
searchError.message to the end of searchFailures and save it as searchFailures.Add Could not check '@trackedPage.Query': searchError.message to the end of searchFailures and save it as searchFailures.snapshots, previous, site_domain, run_date, results_scanned, position_drop_alert, max_to_diagnose, sheet_url, and search_failures and save it as decayReport.Run Python code on snapshots, previous, site_domain, run_date, results_scanned, position_drop_alert, max_to_diagnose, sheet_url, and search_failures and save it as decayReport.problems to decayReport.unchecked.Set problems to decayReport.unchecked.historyRow in decayReport.rows:For each historyRow in decayReport.rows:historyWriteErrorIf a step below fails, save the error as historyWriteErrorTry
historyTabName in historySheetUrl.Add a row to historyTabName in historySheetUrl.On error
historyWriteError.message to the end of problems and save it as problems.Add A row of this week's readings could not be written to the history sheet: historyWriteError.message to the end of problems and save it as problems.summaryTimestamp to nothing.Set summaryTimestamp to nothing.summaryErrorIf a step below fails, save the error as summaryErrorTry
slackChannelId. Save it as postedSummary.Send a message to slackChannelId. Save it as postedSummary.decayReport.summarysummaryTimestamp to postedSummary.ts.Change summaryTimestamp to postedSummary.ts.On error
summaryError.message to the end of problems and save it as problems.Add The summary could not be posted to Slack: summaryError.message to the end of problems and save it as problems.finding in decayReport.findings:For each finding in decayReport.findings:pageSummary to - The page itself was not read this run..Set pageSummary to - The page itself was not read this run..pageFetchErrorIf a step below fails, save the error as pageFetchErrorTry
finding.url and save it as pageResponse.Fetch finding.url and save it as pageResponse.status, final_url, page_url, and html and save it as pageSummary.Run Python code on status, final_url, page_url, and html and save it as pageSummary.On error
pageSummary to - The page could not be read this run (pageFetchError.message), so judge it from the search data alone.Change pageSummary to - The page could not be read this run (pageFetchError.message), so judge it from the search data alone.adviceErrorIf a step below fails, save the error as adviceErrorTry
Do this lines. Name the subtopic, the competitor, the question or the number you are reacting to; a line that would read the same on any page is not worth writing. Do not suggest buying links, spinning up more pages for the same query, or anything you would not want a search engineer to read. Where the results page has started answering the query itself, treat the goal as being the source that answer cites and readers still click, not as ranking higher.
Here is the data.
finding.brief
THE PAGE AS IT STANDS TODAY:
pageSummary
decayReport.context'. Save it as advice.
Maximum response length: 900advice.response_text and save it as recommendation.Trim advice.response_text and save it as recommendation.recommendation isn't emptyIf recommendation isn't emptyYes
slackChannelId.Reply in a thread in slackChannelId.
Reply To: summaryTimestampfinding.heading
recommendationNo
problems and save it as problems.Add No recommendation came back for '@finding.keyword', so nothing was posted for it. to the end of problems and save it as problems.On error
adviceError.message to the end of problems and save it as problems.Add Could not write up '@finding.keyword': adviceError.message to the end of problems and save it as problems.problems isn't emptyIf problems isn't emptyYes
problems with a new line and save it as problemList.Join problems with a new line and save it as problemList.problemList.No
No steps