Reads your sitemap daily, submits the pages that are new or changed for indexing, and records every submission in a sheet.

UsesGoogle 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.

ScheduleRun every day at 07:00.Run every day at 07:00.
ValuesSet sitemapUrl to blank.Set sitemapUrl to blank.
ValuesSet indexNowKey to blank.Set indexNowKey to blank.
ValuesSet spreadsheetUrl to blank.Set spreadsheetUrl to blank.
ValuesSet sheetTab to Index submissions.Set sheetTab to Index submissions.
ValuesSet maxPagesPerRun to 25.Set maxPagesPerRun to 25.
ValuesSet keyFileUrl to blank.Set keyFileUrl to blank.
ValuesSet indexNowEndpoint to https://api.indexnow.org/indexnow.Set indexNowEndpoint to https://api.indexnow.org/indexnow.
MathSet pageLimit to maxPagesPerRun multiplied by 1.Set pageLimit to maxPagesPerRun multiplied by 1.
Decisions & LoopsIf pageLimit is greater than or equal to 1 is noIf pageLimit is greater than or equal to 1 is no
Yes
Decisions & LoopsStop 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.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.
No
No steps
Decisions & LoopsIf pageLimit is greater than 500If pageLimit is greater than 500
Yes
Decisions & LoopsStop 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.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.
No
No steps
TextTrim indexNowKey and save it as submissionKey.Trim indexNowKey and save it as submissionKey.
ListsGet the first item of the last item of 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.
Decisions & LoopsIf siteHost is emptyIf siteHost is empty
Yes
Decisions & LoopsStop 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.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.
No
No steps
TextTrim keyFileUrl and save it as keyLocation.Trim keyFileUrl and save it as keyLocation.
Decisions & LoopsIf keyLocation is emptyIf keyLocation is empty
Yes
ValuesChange keyLocation to https://@siteHost/@submissionKey.txt.Change keyLocation to https://@siteHost/@submissionKey.txt.
No
No steps
ValuesSet sitemapText to blank.Set sitemapText to blank.
Decisions & LoopsIf a step below fails, save the error as sitemapReadErrorIf a step below fails, save the error as sitemapReadError
Try
HTTPFetch sitemapUrl and save it as sitemapResponse.Fetch sitemapUrl and save it as sitemapResponse.
TextTrim sitemapResponse.body and save it as sitemapText.Trim sitemapResponse.body and save it as sitemapText.
On error
Decisions & LoopsStop and report a problem: Could not read the sitemap in the 'sitemapUrl' step. 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.
Decisions & LoopsIf sitemapText contains <sitemapindexIf sitemapText contains <sitemapindex
Yes
Decisions & LoopsStop and report a problem: That address is a sitemap index - a list of other sitemaps - rather than a sitemap of pages. Point the 'sitemapUrl' step at one of the sitemaps it lists, or add this workflow once per sitemap.Stop and report a problem: That address is a sitemap index - a list of other sitemaps - rather than a sitemap of pages. Point the 'sitemapUrl' step at one of the sitemaps it lists, or add this workflow once per sitemap.
No
No steps
Decisions & LoopsIf sitemapText doesn't contain <loc>If sitemapText doesn't contain <loc>
Yes
Decisions & LoopsStop and report a problem: Nothing at the address in the 'sitemapUrl' step looks like a sitemap: it contains no <loc> entries. Check the address opens a sitemap in a browser.Stop and report a problem: Nothing at the address in the 'sitemapUrl' step looks like a sitemap: it contains no <loc> entries. Check the address opens a sitemap in a browser.
No
No steps
TextReplace &amp; with & in sitemapText and save it as sitemapText.Replace &amp; with & in sitemapText and save it as sitemapText.
ListsTake 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.
ValuesSet tabIsNew to false.Set tabIsNew to false.
Decisions & LoopsIf a step below fails, save the error as createTabErrorIf a step below fails, save the error as createTabError
Try
Google SheetsGoogle SheetsCreate the tab sheetTab in spreadsheetUrl.Create the tab sheetTab in spreadsheetUrl.
ValuesChange tabIsNew to true.Change tabIsNew to true.
On error
ValuesShow in the output: Recording this run on the existing '@sheetTab' tab.Show in the output: Recording this run on the existing '@sheetTab' tab.
Decisions & LoopsIf tabIsNew is yesIf tabIsNew is yes
Yes
Google SheetsGoogle SheetsAdd a row to sheetTab in spreadsheetUrl.Add a row to sheetTab in spreadsheetUrl.
No
No steps
ListsStart an empty list and save it as alreadySubmitted.Start an empty list and save it as alreadySubmitted.
Decisions & LoopsIf a step below fails, save the error as historyReadErrorIf a step below fails, save the error as historyReadError
Try
Google SheetsGoogle SheetsRead the rows of spreadsheetUrl. Save it as history.Read the rows of spreadsheetUrl. Save it as history.
Decisions & LoopsFor each loggedRow in history.rows:For each loggedRow in history.rows:
Values Set loggedKey to blank. Set loggedKey to blank.
Decisions & Loops If a step below fails, save the error as loggedRowError If a step below fails, save the error as loggedRowError
Try
Decisions & LoopsIf loggedRow["Page URL"] isn't blankIf loggedRow["Page URL"] isn't blank
Yes
ValuesChange loggedKey to loggedRow["Page URL"] @ loggedRow["Last modified"].Change loggedKey to loggedRow["Page URL"] @ loggedRow["Last modified"].
No
No steps
On error
ValuesShow in the output: 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..
Decisions & Loops If loggedKey isn't empty If loggedKey isn't empty
Yes
ListsAdd loggedKey to the end of alreadySubmitted and save it as alreadySubmitted.Add loggedKey to the end of alreadySubmitted and save it as alreadySubmitted.
No
No steps
On error
ValuesShow in the output: Could not read what has been submitted before, so every page in the sitemap is being treated as new. 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.
ListsStart an empty list and save it as pendingPages.Start an empty list and save it as pendingPages.
ListsStart an empty list and save it as pendingKeys.Start an empty list and save it as pendingKeys.
ValuesSet unchangedCount to 0.Set unchangedCount to 0.
Decisions & LoopsFor each sitemapEntry in sitemapEntries:For each sitemapEntry in sitemapEntries:
Decisions & LoopsIf the length of pendingPages is greater than or equal to pageLimitIf the length of pendingPages is greater than or equal to pageLimit
Yes
Decisions & LoopsStop repeating.Stop repeating.
No
No steps
Decisions & LoopsIf sitemapEntry doesn't contain </loc>If sitemapEntry doesn't contain </loc>
Yes
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
TextExtract the first web address from the first item of 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.
Decisions & LoopsIf pageUrl is emptyIf pageUrl is empty
Yes
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
ValuesSet lastModified to blank.Set lastModified to blank.
Decisions & LoopsIf sitemapEntry contains <lastmod>If sitemapEntry contains <lastmod>
Yes
TextTrim the first item of the last item of 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.
No
No steps
ValuesSet pageKey to pageUrl @ lastModified.Set pageKey to pageUrl @ lastModified.
Decisions & LoopsIf alreadySubmitted contains pageKeyIf alreadySubmitted contains pageKey
Yes
ValuesAdd 1 to unchangedCount.Add 1 to unchangedCount.
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
Decisions & LoopsIf pendingKeys contains pageKeyIf pendingKeys contains pageKey
Yes
Decisions & LoopsSkip to the next item.Skip to the next item.
No
No steps
ListsAdd a record of url and lastModified to the end of pendingPages and save it as pendingPages.Add a record of url and lastModified to the end of pendingPages and save it as pendingPages.
ListsAdd pageKey to the end of pendingKeys and save it as pendingKeys.Add pageKey to the end of pendingKeys and save it as pendingKeys.
Decisions & LoopsIf pendingPages is emptyIf pendingPages is empty
Yes
Decisions & LoopsFinish the run: Nothing to submit: all unchangedCount 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.
No
No steps
ListsStart an empty list and save it as submissionUrls.Start an empty list and save it as submissionUrls.
Decisions & LoopsFor each pendingPage in pendingPages:For each pendingPage in pendingPages:
ListsAdd 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.
ValuesSet submissionStatus to 0.Set submissionStatus to 0.
ValuesSet submissionAccepted to false.Set submissionAccepted to false.
Decisions & LoopsIf a step below fails, save the error as submissionErrorIf a step below fails, save the error as submissionError
Try
HTTPSend data to indexNowEndpoint and save it as submission.Send data to indexNowEndpoint and save it as submission.
ValuesChange submissionStatus to submission.status.Change submissionStatus to submission.status.
ValuesChange submissionAccepted to submission.ok.Change submissionAccepted to submission.ok.
On error
Decisions & LoopsStop 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.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.
Decisions & LoopsIf submissionAccepted is noIf submissionAccepted is no
Yes
Decisions & LoopsStop 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.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.
No
No steps
Dates & TimesGet today's date and save it as submittedOn.Get today's date and save it as submittedOn.
ValuesSet submissionResult to Accepted (HTTP submissionStatus).Set submissionResult to Accepted (HTTP submissionStatus).
Decisions & LoopsFor each submittedPage in pendingPages:For each submittedPage in pendingPages:
Decisions & LoopsIf a step below fails, save the error as recordErrorIf a step below fails, save the error as recordError
Try
Google SheetsGoogle SheetsAdd a row to sheetTab in spreadsheetUrl.Add a row to sheetTab in spreadsheetUrl.
On error
ValuesShow in the output: Submitted but could not record 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.
ValuesShow in the output: Submitted the length of 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.