Track performance and adjust
Tools, key metrics and strategic adjustments for your programmatic pages.

Read a programmatic site by group of pages. A single city page getting four clicks tells you nothing, while the same number across 400 pages of one template tells you whether the template works. So the useful setup is one table that joins each URL to its template, its rollout wave and its data, and a short list of metrics that each trigger a decision.
I’ll stay with the same example: a coworking directory with 2,000 city pages, released in waves as described in the chapter on production and indexing.
Follow the funnel from indexed page to business result
Each metric below exists because a bad number points at a specific fix.
| Stage | Metric | Source | A weak number points to |
|---|---|---|---|
| Indexing | Share of submitted pages indexed, per wave | Search Console, Page indexing | Template quality, discovery |
| Visibility | Impressions, queries per page | Search Console | Wrong targeting, too little authority |
| Ranking | Average position by query group | Search Console | Content depth, internal links |
| Attractiveness | CTR at comparable positions | Search Console | Title and meta templates |
| Engagement | Engaged sessions, average engagement time | GA4 | Search intent, UX |
| Business | Key events, value per visitor, retention | GA4, CRM, product data | Offer, calls to action, lead quality |
A few of these need careful reading. Average position is weighted by impressions and blends every query a page appears for, so compare it within a group of similar queries. GA4 has replaced the old time on page with engagement time, and its bounce rate is now simply the opposite of engagement rate. What it calls conversions are now key events.
Clicks and impressions from Google’s AI features are counted inside Search Console’s web totals. So a falling CTR on stable positions can mean an AI answer now satisfies the query, with nothing wrong on your page. So I read CTR by query type. My working assumption is that factual queries are the most exposed, and that queries needing a list, a comparison or a price from your data keep a better reason to click.
The bottom of the table is the part people skip, and it is the only part your business feels. The results I care about are business numbers: 107 signups in a typical week on Derrick, and a steady 35 to 50 leads per month on Deux.io. To measure that, pass the landing page through to your CRM or product database at signup. Otherwise retention and lifetime value stay invisible at page level.
Know what each tool answers
| Tool | Question it answers |
|---|---|
| Google Search Console | Is the page indexed, for which queries does it appear, and where |
| Google Analytics 4 | What visitors do once they arrive |
| Looker Studio | What the whole picture looks like, shared with the team |
| Screaming Frog | What is broken on the site: 404s, orphans, tags, internal links |
| Ahrefs, Semrush | How backlinks and competitors’ rankings are moving |
| Hotjar, Microsoft Clarity | Where people get stuck on a template |
None of this is real time, and for a programmatic site that doesn’t matter. Search Console data arrives with a delay, and search results move slowly enough that a weekly read is plenty.
The Search Console limit that does matter at this scale is the 1,000 rows in the interface. Use the API, or better, the bulk data export to BigQuery, which is built for sites with many pages.
Referrals from AI assistants deserve their own channel in GA4. A custom channel group using this condition on session source does the job, and it will undercount, because part of that traffic arrives with no referrer at all:
^(chatgpt\.com|perplexity\.ai|copilot\.microsoft\.com|gemini\.google\.com|claude\.ai)$
Join search data to your own data
Export your page table to BigQuery with the full URL, then query it against the Search Console export. Replace project with your own project ID, and adjust dataset names to your setup.
SELECT
p.template,
p.wave,
SUM(g.impressions) AS impressions,
SUM(g.clicks) AS clicks,
SAFE_DIVIDE(SUM(g.clicks), SUM(g.impressions)) AS ctr,
SAFE_DIVIDE(SUM(g.sum_position), SUM(g.impressions)) + 1 AS avg_position
FROM `project.searchconsole.searchdata_url_impression` AS g
JOIN `project.pseo.page` AS p
ON g.url = p.url
WHERE g.data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 28 DAY)
AND g.search_type = 'WEB'
GROUP BY p.template, p.wave
ORDER BY p.template, p.wave;
Add any field from your database to that grouping and you start answering questions no SEO tool can. Do cities with ten or more listings earn a better CTR than cities with three? Do pages with a price summary rank higher? If they do, you know what to invest in, and you have evidence for raising your publication threshold. Plug the result into Looker Studio and you have your dashboard.
Turn symptoms into changes
| What you see | Likely cause | What to try |
|---|---|---|
| Good position, low CTR | Generic title template | Bring real data into the title: count, price, freshness |
| Stuck on page two | Not enough depth or internal authority | Add data blocks, add links from stronger pages |
| A wave stays unindexed | Pages too similar or too thin | Enrich them, or raise the threshold and prune |
| Impressions on queries you never targeted | An unmet need | Add a field, a section or a new template |
| Clicks, no engagement | The page doesn’t match the intent | Check the template in Clarity, rework the top of the page |
| Engagement, no conversions | Weak or misplaced call to action | Test the offer on that template |
Fast-growing queries and long-tail variations surface in the same export. They are your cheapest new pages, because the template already exists.
For the second row, add links from pages that have authority to pages close to breaking through, using the rules from the chapter on internal linking. Regroup pages into coherent clusters when the query data shows that people think about them together.
New formats are worth testing too: comparisons, interactive tools, FAQ blocks. Add FAQs because they answer real questions and because AI answers can reuse them. The FAQ rich result is restricted to authoritative government and health sites, so it won’t appear for a directory.
Updates should follow real changes in the data. Moving a date on a page that hasn’t changed isn’t freshness, and it damages the trust Google gives your lastmod values.
And if a template keeps producing pages that earn neither clicks nor engagement after several rounds of work, remove those pages. They are the profile the scaled content abuse policy describes, and they drag down your read of everything else.
Test on a wave before changing everything
Editing a template changes thousands of pages at once, which makes it impossible to know what caused what. Apply the change to one wave or half of a template, keep the rest as your control group, record the date, and compare both groups over the same 28-day windows so that seasonality affects them equally. Then roll out what worked.
Keep the rhythm simple:
- every week, scan for anomalies in indexing, clicks and server errors
- every month, review results by template and wave and pick one change to test
- every quarter, prune weak pages, review competitors and refresh your data
Most of this can run without you once it is set up. The chapter on automating SEO audits covers how to catch regressions across thousands of pages.
Your next step: connect the Search Console bulk export, load your page table next to it, and run the query above. Keep the first dashboard to one chart of clicks per wave, then choose the one template change you will test this month.