Reads your sitemap daily, submits the pages that are new or changed for indexing, and records every submission in a sheet.
Uses
Google Sheets
Tell search engines about your new pages
Every day at 07:00, your sitemap is read, the pages that are new or changed since the last run are picked out, and they are submitted for indexing through IndexNow, the shared protocol behind Bing, Yandex, Naver, Seznam and Yep. Google does not take part in IndexNow and still finds pages by crawling your sitemap, so this speeds up the other engines rather than all of them.
Your Google Sheet is the memory. An 'Index submissions' tab is created on the first run and each submitted page becomes one row: the date, the page address, its last modified date, where it was sent and the result. A page is only sent again when its last modified date in the sitemap changes, so nothing is submitted twice for the same version. Pages are submitted before anything is recorded, so a failure part way through means the next run offers the same pages again rather than losing them.
You need an IndexNow key saved in a text file at the root of your site, and a sitemap of pages rather than a sitemap index.
sitemapUrl to blank.Set sitemapUrl to blank.indexNowKey to blank.Set indexNowKey to blank.spreadsheetUrl to blank.Set spreadsheetUrl to blank.sheetTab to Index submissions.Set sheetTab to Index submissions.maxPagesPerRun to 25.Set maxPagesPerRun to 25.keyFileUrl to blank.Set keyFileUrl to blank.indexNowEndpoint to https://api.indexnow.org/indexnow.Set indexNowEndpoint to https://api.indexnow.org/indexnow.pageLimit to maxPagesPerRun multiplied by 1.Set pageLimit to maxPagesPerRun multiplied by 1.pageLimit is greater than or equal to 1 is noIf pageLimit is greater than or equal to 1 is nomaxPagesPerRun.Stop and report a problem: Set the 'maxPagesPerRun' step to how many pages may be submitted in one run, as a whole number - for example 25. It is currently: maxPagesPerRun.pageLimit is greater than 500If pageLimit is greater than 500maxPagesPerRun.Stop and report a problem: The 'maxPagesPerRun' step is set higher than 500. Search engines throttle and then ignore bulk submissions, so keep it at 500 or fewer. It is currently: maxPagesPerRun.indexNowKey and save it as submissionKey.Trim indexNowKey and save it as submissionKey.sitemapUrl split by :// split by / and save it as siteHost.Get the first item of the last item of sitemapUrl split by :// split by / and save it as siteHost.siteHost is emptyIf siteHost is emptysitemapUrl.Stop and report a problem: Could not work out which site the 'sitemapUrl' step belongs to. It should look like https://example.com/sitemap.xml, not: sitemapUrl.keyFileUrl and save it as keyLocation.Trim keyFileUrl and save it as keyLocation.keyLocation is emptyIf keyLocation is emptykeyLocation to https://@siteHost/@submissionKey.txt.Change keyLocation to https://@siteHost/@submissionKey.txt.sitemapText to blank.Set sitemapText to blank.sitemapReadErrorIf a step below fails, save the error as sitemapReadErrorsitemapUrl and save it as sitemapResponse.Fetch sitemapUrl and save it as sitemapResponse.sitemapResponse.body and save it as sitemapText.Trim sitemapResponse.body and save it as sitemapText.sitemapReadError.message with sitemapUrl replaced by the sitemap address.Stop and report a problem: Could not read the sitemap in the 'sitemapUrl' step. sitemapReadError.message with sitemapUrl replaced by the sitemap address.sitemapText contains <sitemapindexIf sitemapText contains <sitemapindexsitemapText doesn't contain <loc>If sitemapText doesn't contain <loc>& with & in sitemapText and save it as sitemapText.Replace & with & in sitemapText and save it as sitemapText.sitemapText split by <loc> from item 2 on and save it as sitemapEntries.Take sitemapText split by <loc> from item 2 on and save it as sitemapEntries.tabIsNew to false.Set tabIsNew to false.createTabErrorIf a step below fails, save the error as createTabErrorsheetTab in spreadsheetUrl.Create the tab sheetTab in spreadsheetUrl.tabIsNew to true.Change tabIsNew to true.tabIsNew is yesIf tabIsNew is yessheetTab in spreadsheetUrl.Add a row to sheetTab in spreadsheetUrl.alreadySubmitted.Start an empty list and save it as alreadySubmitted.historyReadErrorIf a step below fails, save the error as historyReadErrorspreadsheetUrl. Save it as history.Read the rows of spreadsheetUrl. Save it as history.loggedRow in history.rows:For each loggedRow in history.rows:loggedKey to blank. Set loggedKey to blank.loggedRowError If a step below fails, save the error as loggedRowErrorloggedRow["Page URL"] isn't blankIf loggedRow["Page URL"] isn't blankloggedKey to loggedRow["Page URL"] @ loggedRow["Last modified"].Change loggedKey to loggedRow["Page URL"] @ loggedRow["Last modified"].Skipping a record row that has no 'Page URL' and 'Last modified' value..Show in the output: Skipping a record row that has no 'Page URL' and 'Last modified' value..loggedKey isn't empty If loggedKey isn't emptyloggedKey to the end of alreadySubmitted and save it as alreadySubmitted.Add loggedKey to the end of alreadySubmitted and save it as alreadySubmitted.historyReadError.message.Show in the output: Could not read what has been submitted before, so every page in the sitemap is being treated as new. historyReadError.message.pendingPages.Start an empty list and save it as pendingPages.pendingKeys.Start an empty list and save it as pendingKeys.unchangedCount to 0.Set unchangedCount to 0.sitemapEntry in sitemapEntries:For each sitemapEntry in sitemapEntries:pendingPages is greater than or equal to pageLimitIf the length of pendingPages is greater than or equal to pageLimitsitemapEntry doesn't contain </loc>If sitemapEntry doesn't contain </loc>sitemapEntry split by </loc> and save it as pageUrl.Extract the first web address from the first item of sitemapEntry split by </loc> and save it as pageUrl.pageUrl is emptyIf pageUrl is emptylastModified to blank.Set lastModified to blank.sitemapEntry contains <lastmod>If sitemapEntry contains <lastmod>sitemapEntry split by <lastmod> split by </lastmod> and save it as lastModified.Trim the first item of the last item of sitemapEntry split by <lastmod> split by </lastmod> and save it as lastModified.pageKey to pageUrl @ lastModified.Set pageKey to pageUrl @ lastModified.alreadySubmitted contains pageKeyIf alreadySubmitted contains pageKeyunchangedCount.Add 1 to unchangedCount.pendingKeys contains pageKeyIf pendingKeys contains pageKeypendingPages and save it as pendingPages.Add a record of url and lastModified to the end of pendingPages and save it as pendingPages.pageKey to the end of pendingKeys and save it as pendingKeys.Add pageKey to the end of pendingKeys and save it as pendingKeys.pendingPages is emptyIf pendingPages is emptyunchangedCount page(s) in the sitemap have already been submitted at their current last-modified date.Finish the run: Nothing to submit: all unchangedCount page(s) in the sitemap have already been submitted at their current last-modified date.submissionUrls.Start an empty list and save it as submissionUrls.pendingPage in pendingPages:For each pendingPage in pendingPages:pendingPage.url to the end of submissionUrls and save it as submissionUrls.Add pendingPage.url to the end of submissionUrls and save it as submissionUrls.submissionStatus to 0.Set submissionStatus to 0.submissionAccepted to false.Set submissionAccepted to false.submissionErrorIf a step below fails, save the error as submissionErrorindexNowEndpoint and save it as submission.Send data to indexNowEndpoint and save it as submission.submissionStatus to submission.status.Change submissionStatus to submission.status.submissionAccepted to submission.ok.Change submissionAccepted to submission.ok.submissionError.message with indexNowEndpoint replaced by the indexing endpoint Nothing was recorded, so the next run will offer these pages again.Stop and report a problem: Could not reach the search engine indexing endpoint. submissionError.message with indexNowEndpoint replaced by the indexing endpoint Nothing was recorded, so the next run will offer these pages again.submissionAccepted is noIf submissionAccepted is nosubmissionStatus). Check that keyLocation opens in a browser and contains exactly the key from the 'indexNowKey' step. Nothing was recorded, so the next run will offer these pages again.Stop and report a problem: The indexing endpoint refused the submission (HTTP submissionStatus). Check that keyLocation opens in a browser and contains exactly the key from the 'indexNowKey' step. Nothing was recorded, so the next run will offer these pages again.submittedOn.Get today's date and save it as submittedOn.submissionResult to Accepted (HTTP submissionStatus).Set submissionResult to Accepted (HTTP submissionStatus).submittedPage in pendingPages:For each submittedPage in pendingPages:recordErrorIf a step below fails, save the error as recordErrorsheetTab in spreadsheetUrl.Add a row to sheetTab in spreadsheetUrl.submittedPage.url, so the next run will offer it again. recordError.message.Show in the output: Submitted but could not record submittedPage.url, so the next run will offer it again. recordError.message.pendingPages new or changed page(s) for indexing and left unchangedCount unchanged page(s) alone.Show in the output: Submitted the length of pendingPages new or changed page(s) for indexing and left unchangedCount unchanged page(s) alone.