backlinkindexersoftware.comSoftware and API handbook

Google Sheets indexing workflow

Published

A Google Sheets indexing workflow keeps URLs in one column and uses Apps Script with UrlFetchApp to send them to an index-check API. A second function, run on a time trigger, fetches the finished report and writes indexed, not indexed or check failed beside each URL. The API key stays in Script Properties.

When a spreadsheet is the right tool

Many link builders already track placements in Google Sheets: one row per live URL, with columns for the client, the vendor and the date the link went up. Adding an index status column to that sheet avoids copying lists between tools. Apps Script, the JavaScript runtime built into Google Workspace, can call any HTTPS API, so the sheet can request checks and record results without an add-on.

This recipe uses the public API of IndexChex, the publisher of this handbook, described on the IndexChex software and API page. The same structure works for any index checker with a create-job and fetch-report pair of endpoints; the general pattern is covered under backlink indexer APIs.

Sheet layout

Create a tab named URLs. Row 1 holds headers. Column A holds one URL per row from row 2 down; column B is where the script writes the result. Other columns are left alone, so client names, prices and notes can sit to the right.

Storing the key

In the Apps Script editor (Extensions, then Apps Script), open Project Settings and add a script property named INDEXCHEX_API_KEY with the key from your account's API settings. The key is sent as the full value of the Authorization header, with no prefix. Script Properties keep the key out of the grid, out of version history and out of copies made by collaborators. The API key security page covers rotation and scoping.

The script

const BASE = 'https://indexchex.com/api/v1';
const SHEET = 'URLs';

function api_(method, path, body) {
  const key = PropertiesService.getScriptProperties().getProperty('INDEXCHEX_API_KEY');
  const options = {
    method: method,
    headers: { Authorization: key, Accept: 'application/json' },
    muteHttpExceptions: true,
  };
  if (body) {
    options.contentType = 'application/json';
    options.payload = JSON.stringify(body);
  }
  const res = UrlFetchApp.fetch(BASE + path, options);
  return { code: res.getResponseCode(), data: JSON.parse(res.getContentText()) };
}

function urlRange_() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET);
  const rows = sheet.getLastRow() - 1;
  if (rows < 1) throw new Error('No URLs below the header row.');
  return sheet.getRange(2, 1, rows, 2);
}

function startCheck() {
  const urls = urlRange_().getValues()
    .map(row => String(row[0]).trim())
    .filter(url => url !== '');
  const res = api_('post', '/index-check/jobs', { urls: urls, name: 'Sheet: ' + SHEET });
  if (res.code !== 202) throw new Error(res.data.message);
  PropertiesService.getDocumentProperties()
    .setProperty('CHECK_JOB_ID', String(res.data.job_id));
}

function collectReport() {
  const props = PropertiesService.getDocumentProperties();
  const jobId = props.getProperty('CHECK_JOB_ID');
  if (!jobId) return;

  const res = api_('get', '/index-check/jobs/' + jobId + '/report');
  if (res.code === 422) return; // not finished yet
  if (res.code !== 200) throw new Error(res.data.message);

  const result = {};
  res.data.indexed_links.forEach(url => { result[url] = 'Indexed'; });
  res.data.unindexed_links.forEach(url => { result[url] = 'Not indexed'; });
  res.data.failed_links.forEach(url => { result[url] = 'Check failed'; });

  const range = urlRange_();
  range.setValues(range.getValues().map(row => {
    const url = String(row[0]).trim();
    return [row[0], result[url] || row[1]];
  }));
  props.deleteProperty('CHECK_JOB_ID');
}

startCheck reads column A, drops blanks and creates one check job. A successful create returns HTTP 202 with a job_id, which the script keeps in Document Properties so the second function knows what to fetch. collectReport asks for the report; until the job is complete the API answers 422, and the function simply exits. Once the report arrives it maps each URL to one of three labels and writes them into column B in the original row order.

Running it

  1. Run startCheck once from the editor and approve the authorisation prompt (the script needs spreadsheet and external request scopes).
  2. In Triggers, add a time-driven trigger for collectReport, every 10 or 15 minutes.
  3. When column B fills, the stored job ID is cleared and later trigger runs do nothing until startCheck is run again.

A time trigger is better than a loop with Utilities.sleep, because Apps Script stops long-running executions. Polling every few minutes is gentle on the per-minute request limit; the polling guide explains how to choose an interval.

Reading the three labels

LabelMeaningNext step
IndexedGoogle currently shows the URL in search resultsNothing; re-check later if the link matters
Not indexedThe check completed and the URL was not foundInspect the page, then consider a submission
Check failedThe lookup could not completeRe-check; it is not evidence that the URL is unindexed

Not indexed URLs can be sent for crawling from the same sheet by calling the index-submit jobs endpoint with the URLs from those rows, or from the IndexChex dashboard, where check results can be moved into a new submission in one click. Indexing itself remains Google's decision; a submission gets the page crawled, not ranked or kept.

Variations

  • Several tabs. Store one job ID per tab name instead of a single property.
  • Submit with a re-check. Use the submission endpoint with checker set to a number of days (1 to 5), then read the re-check report later. The Python script shows that flow end to end.
  • Client hand-off. Copy the filled columns into a summary tab; client reporting suggests table layouts.
  • No code. The same calls can run from an automation platform; see Zapier and Make workflows.

Field names and response codes are listed in the API reference. For a broader view of where scripts like this sit among indexing tools, see what backlink indexer software is.

FAQ

Why not put the API key in a hidden cell?

Anyone with view access to the spreadsheet can read every cell, hidden or not, and copies of the file carry the cells with them. Script Properties are attached to the script project and are not shown in the grid.

Can the sheet submit URLs for crawling instead of checking them?

Yes. Change the path to the index-submit jobs endpoint and the body to the submission fields. Submissions cost 1 credit per URL in standard mode, so keep the trigger manual rather than timed to avoid repeat submissions.

What happens if the report is requested before the job finishes?

The report endpoint answers HTTP 422 with a message saying the report is not ready. The script treats that as a signal to try again on the next trigger run.

How many URLs can one sheet send?

One check job accepts up to 10,000 URLs. Larger lists should be split into several jobs, each with its own stored job ID.

Terms used on this page

Sources

  1. IndexChex public API reference
  2. Backlink indexer APIs

Cite this entry

IndexChex. (2026, October 8). Google Sheets indexing workflow. backlinkindexersoftware.com. https://backlinkindexersoftware.com/google-sheets-indexing-workflow/