backlinkindexersoftware.comSoftware and API handbook

Client reporting from indexing data

Published

Client reporting from indexing data means converting raw check reports into a per-URL CSV and a short summary table: URLs submitted, indexed, not indexed and checks that failed, grouped by client or domain. Good reports separate failed checks from not indexed results and state that crawling, not indexing, is what a submission buys.

What clients actually need

Agencies and link builders often send clients a spreadsheet of live links. Adding an index status to that sheet answers the question clients ask most: are the links I paid for visible in Google? A raw API report is not a good answer on its own. It lists URLs in three arrays, has no client or campaign context, and uses field names that mean nothing to a business owner. A useful report has two layers: a per-URL table for anyone who wants detail, and a summary table that fits in an email.

The examples here use reports from the IndexChex index checker, described on this site's IndexChex software and API page, but the layout works with any checker that returns per-URL results.

Getting the data

There are two sources, and both produce the same three outcomes:

  • API report. GET /v1/index-check/jobs/{id}/report returns indexed_links, unindexed_links and failed_links, plus total_urls, indexed_count, not_indexed_count, failed_count and completed_at. The Python indexing script already saves this to a two-column CSV.
  • Dashboard export. Index-check results can be exported to CSV from the job page, for teams that prefer not to use the API.

Each check costs 1 credit per URL, and one job accepts up to 10,000 URLs, so a whole month of placements for several clients usually fits in one job. Splitting by client afterwards is cheaper in effort than running separate jobs.

Joining results to client data

The report knows URLs; your link log knows clients, campaigns, anchors and placement dates. The script below joins a link log CSV (columns url, client, campaign) with one or more result CSVs (columns url, result) and writes two files: a per-URL report and a per-client summary.

#!/usr/bin/env python3
"""Usage: client_report.py links.csv results1.csv [results2.csv ...]"""
import csv
import sys
from collections import Counter, defaultdict
from urllib.parse import urlsplit

LABELS = ("indexed", "not indexed", "check failed")

links_file, *result_files = sys.argv[1:]

result = {}
for path in result_files:
    with open(path, newline="") as f:
        for row in csv.DictReader(f):
            result[row["url"].strip()] = row["result"]

rows, totals = [], defaultdict(Counter)
with open(links_file, newline="") as f:
    for link in csv.DictReader(f):
        url = link["url"].strip()
        status = result.get(url, "not checked")
        rows.append({"client": link["client"], "campaign": link["campaign"],
                     "domain": urlsplit(url).hostname, "url": url, "result": status})
        totals[link["client"]][status] += 1

with open("report_urls.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=["client", "campaign", "domain", "url", "result"])
    writer.writeheader()
    writer.writerows(sorted(rows, key=lambda r: (r["client"], r["result"], r["url"])))

with open("report_summary.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["client", "links", *LABELS, "not checked", "indexed share of completed checks"])
    for client, c in sorted(totals.items()):
        completed = c["indexed"] + c["not indexed"]
        share = round(100 * c["indexed"] / completed, 1) if completed else ""
        writer.writerow([client, sum(c.values()), *(c[l] for l in LABELS), c["not checked"], share])

URLs that appear in the link log but not in any result file are labelled not checked, which catches placements that were never sent to the checker. The indexed share is calculated over completed checks only, so failed lookups do not drag the figure down or inflate it.

The summary table

A summary for a client email might look like this (illustrative numbers):

ClientLinksIndexedNot indexedCheck failedNot checked
Client A4031720
Client B2518601

Keep the labels in plain language. "Check failed" should be explained once in a footnote: the lookup did not complete, the URL will be re-checked, and it is not a sign the link is unindexed.

Honest framing

A report is also a statement about what the client bought, so wording matters:

  • Crawling versus indexing. A submission gets a URL crawled by Googlebot. Whether Google indexes it is Google's decision, based on the page's quality and duplication. IndexChex guarantees the crawl, not the index entry, and its credits are not refunded because a URL was not indexed.
  • Timing. A check run the day after submission will show fewer indexed URLs than one run five days later. State the check date beside the numbers, and prefer a scheduled re-check 3 to 5 days after submission.
  • Published averages are not targets. IndexChex publishes weekly measurements on recurring URL sets; the methodology page explains what they cover. A single client batch can land above or below those averages.
  • Not indexed is actionable. List those URLs with next steps: check the source page for noindex or robots.txt blocks, consider whether the page is thin, then resubmit if it looks sound. From the dashboard, not indexed URLs can be moved into a new submission in one click.

Automating the pipeline

The full loop can run unattended: links logged in a sheet, submitted with a re-check by the Python script or a Zapier or Make workflow, results written back by the Google Sheets workflow, and this script run monthly for the roll-up. For WordPress-hosted client sites, a publish hook can cover the client's own new posts as well as the links built to them.

For the wider picture of tools that handle submission and checking, see what backlink indexer software is; for request formats and response fields, see backlink indexer APIs.

FAQ

Should failed checks count as not indexed in a client report?

No. A failed check means the lookup did not complete, so the URL's status is unknown. Report failed checks in their own column and re-check them before the report is final.

What indexation rate should a report promise?

None. Report what the check found for that batch. IndexChex guarantees Googlebot crawling, not indexing, and its published rates are averages from weekly measurements, not targets for a given client.

Can clients see the raw report?

Yes. The per-URL CSV is suitable to share as-is. Keep vendor costs and internal notes in separate columns that are dropped before export.

How often should reports be produced?

Match the client's link-building cadence. A common pattern is one report per placement batch, built from a check run a few days after submission, plus a monthly roll-up.

Terms used on this page

Sources

  1. IndexChex public API reference
  2. How IndexChex published figures are measured

Cite this entry

IndexChex. (2026, October 8). Client reporting from indexing data. backlinkindexersoftware.com. https://backlinkindexersoftware.com/client-reporting/