Get in touch

Measuring programmatic SEO: the five metrics that matter

Updated August 19, 2026 · 9 min read · Search intent : informational · Indexing

Page-level reporting is useless on a generated site. A single page with forty impressions a month tells you nothing. The unit of analysis has to be the batch — pages sharing a template and a publication wave — and every metric below is defined at that level.

Cohort reporting on a generated content batch
Cohort reporting on a generated content batch

Define cohorts before you measure anything

A cohort is the intersection of a template and a publication wave. Two dimensions, both known at generation time and both worth storing alongside each page.

cohort_id = template_name + "_" + publication_wave
example:  "alternatives_2026-W28"

Everything downstream — indexation, impressions, clicks — is aggregated by cohort. Without this key, a template regression introduced in week 30 is invisible inside sitewide averages.

Cohorts map directly onto sitemap segmentation. Isolating each wave in its own sitemap gives you coverage data per cohort for free — see sitemap segmentation.

The five metrics

1. Indexation rate

indexed_urls / submitted_urls   per cohort, at 30, 60 and 90 days

The 30-day figure measures discovery. The 90-day figure measures perceived value. A cohort that is 60% indexed at 30 days and still 60% at 90 days has a content problem, not a crawl problem — the distinction is unpacked in crawl budget.

2. Impression concentration

share of cohort impressions held by the top 10% of pages

Above 80%, the batch is effectively ten good pages plus filler. That is the clearest quantitative signal that the quality gate was too permissive.

3. Query yield

distinct queries with impressions / indexed pages

A healthy programmatic page attracts three to fifteen distinct queries. Below one, pages are ranking for nothing beyond their exact target — usually a sign the content covers the query but not its neighbourhood.

4. Click depth distribution

Not available from Search Console; it comes from your own crawl. Track the share of the cohort at depth 1, 2, 3 and 4+. A cohort drifting deeper over time means the hubs are saturating and need splitting.

5. Position band movement

share of cohort pages in positions 1-3, 4-10, 11-20, 21+

Average position across a batch is a meaningless number — it mixes a page at 2 with a page at 80. Band distribution shows where the mass actually sits and whether it is moving.

Pulling it programmatically

The Search Console API returns performance data by page and query with a date range. Two constraints shape any implementation: results are capped per request and must be paginated, and data is filtered for privacy, so low-volume queries are omitted entirely.

request = {
  "startDate": "2026-05-01",
  "endDate":   "2026-07-31",
  "dimensions": ["page", "query"],
  "rowLimit": 25000,
  "startRow": 0
}
# paginate until fewer rows than rowLimit are returned
# then join `page` against your cohort table

The privacy filtering matters more than it looks on a long tail batch: a substantial share of impressions comes from queries too rare to be reported. Query-level totals will therefore always undercount page-level totals. Compare like with like.

Detecting template regressions

Every template change is a deployment that can break a thousand pages at once. Three checks run against the cohort after each change:

  1. Structural diff. Heading counts, word counts and internal link counts before and after. An unintended drop shows up immediately.
  2. Markup validity. Structured data parsed and required fields verified across a sample of the batch, per structured data validation.
  3. Impression slope. A cohort whose impressions diverge from its siblings within three weeks of a deployment points at that deployment.

A reporting cadence that survives contact with reality

The quarterly review is the one that gets skipped, and it is the one that keeps the site's average quality from drifting down as batches accumulate.

Frequently asked questions

Why is average position misleading on a generated batch?

Because it averages a page at position 2 with a page at position 80 into a number that describes neither. Distribution across position bands is far more actionable.

Why do query totals not match page totals in Search Console?

Queries that are too rare are filtered out for privacy reasons. On a long tail batch these account for a substantial share of impressions, so query-level sums always undercount.

How soon after publishing should a cohort be judged?

Not before 30 days for discovery and not before 90 days for perceived value. Earlier readings on a young domain lead to reversing decisions that would have worked.