SEO
Written on 17/9/2026
Updated on 17/9/2026
3min

SEO KPIs: 20 tested one by one, 12 drive decisions, 8 don't

Thibaut Legrand
Thibaut Legrand
Co-founder - Vydera
Measurable SEO KPIs reporting Vydera
Table of contents

A report where every number has a source?

Vydera measures, documents the endpoint, and publishes what did not answer.

Talk to an expert

Key takeaways

  • We called the 20 KPIs of an SEO report one by one on their endpoint, on 27 August 2026, logging the status code. 12 answer and decide something, 8 do not
  • Search Console shows only 30.0% of vydera.com's clicks once you ask for the query dimension: 410 out of 1,366. Median of 42.3% across the account's 16 properties, from 4.9% to 80.2%
  • The CTR-by-position curve we meant to publish does not exist: 50 non-brand clicks across positions 1 to 20 in twelve months, of which 0 on the 10,693 combined impressions of positions 2, 3 and 4
  • The same code run on a control site decays cleanly, from 3.02% to 0.20% aggregate CTR between position 1 and position 10. The script is not at fault, the sample size is
  • Three KPIs did not answer: PageSpeed Insights returned 429, the CrUX API 403 without a key. That is a missing access on this workstation, not a missing KPI, and we publish it as such

The slide always arrives with the same question: what do we put in it? The usual answer is a KPI list copied off a blog. We did the opposite. We took the 20 indicators that make up a normal SEO report, and called each one on its endpoint on 27 August 2026, logging the status code.

The result fits in one sentence: 12 answer and decide something, 8 do not. And the number that changed how we read a report was on no list: Search Console shows only 30.0% of vydera.com's clicks once you ask for the query dimension. 410 clicks out of 1,366.

30%: what your query-level report cannot see

Two calls, same site, same twelve-month window. With no dimension: 1,366 clicks, 109,389 impressions. With the query dimension: 410 clicks, 43,249 impressions. That is 30.0% of clicks and 39.5% of impressions. Cross-checked by hand on six splits of the same window, the site total never moves.

This is not a small-site quirk. We ran the same check on the 16 Search Console properties of the account: the median share of clicks still visible at query level is 42.3%, with a minimum of 4.9% and a maximum of 80.2%. On half the properties, more than half the clicks disappear the moment you ask what they were made on.

One point we cannot get around: we measure that clicks disappear, and by how much, not the rule Google applies to remove them. That rule is not published, and nothing in these calls lets anyone reconstruct it.

Nor is it exclusive to the query dimension. On vydera.com, crossing the page dimension with device or country tips the total onto the same poorer table: 1,366 clicks with no dimension, 1,366 by date, 1,375 by page, then 419 as soon as device is added. Note 1,375 against 1,366: the page dimension returns 9 clicks and 5,045 impressions more than the site total. Google's own totals do not reconcile with each other, and it shows on twelve months of a 1,366-click site.

The 20, one by one, with their status code

Here is the grid. Each row carries the endpoint actually called, the code it returned, what the KPI decides, and its trap. The sorting rule is explicit: a KPI makes the 12 if it answers and if it decides something. Both conditions, not one.

The 12 split across three source families: seven come from searchAnalytics, three from the URL Inspection API, two from the GA4 Data API. None requires a paid tool.

The 8, and why they are not eight of the same thing

This is where most lists cheat, by throwing everything into one bucket. The 8 split into three families with nothing in common.

Three did not answer here

Field LCP, INP and CLS. PageSpeed Insights returned 429: daily quota exceeded, shared across all anonymous callers. The CrUX API called directly returned 403, with the message "Method doesn't allow unregistered callers". No API key is set on this workstation, and the API refuses an OAuth token.

This is not missing data, it is missing access. The distinction is not cosmetic: a site returning 403 is not a site that blocks, it is a site we failed to measure. What would unlock these three rows is known and takes one action, a CrUX API key enabled on the project. They stay in the table, marked not measured, rather than quietly dropped.

Three answer and decide nothing

These are the only ones that genuinely mislead, precisely because they display so cleanly.

  • Site average position: 6.89. A weighted average over unrelated queries. It moves when the site gains impressions deep in the SERP, so it degrades as visibility improves. No decision can rest on it.
  • Site-wide CTR: 1.25%. It blends brand and non-brand. On vydera.com the brand query shows 16.15% CTR at position 1, non-brand 0.16% at the same position: the average of the two describes neither.
  • Total impressions: 109,389. An impression at position 40 weighs as much as one at position 2. The number rises when a piece of content picks up long-tail impressions, which says nothing more than that.

Two are rented

Referring domains, and citations in AI answers. The first comes from a backlink vendor: on vydera.com, 33 referring domains, 114 backlinks, a proprietary rank of 164, first detected on 19 November 2025, for $0.024036 measured on the account balance before and after the call. The domain count can be verified one by one; the rank index is proprietary and unverifiable.

The second has no public API at all. The KPI exists, you rent it. On vydera.com it is measured nowhere: two active projects on the account, none on this domain. The measurement says zero projects, not zero citations.

The CTR-by-position curve we meant to publish, and which does not exist

The brief for this article promised a CTR-by-position curve computed on our own data, to replace the tables everyone has been copying for ten years. We computed it. It does not exist.

Non-brand, across positions 1 to 20 and twelve months, Search Console returns 50 clicks for 24,383 impressions on vydera.com. Applying a floor of 10 impressions per query-position pair, position 1 holds 78 pairs, 3,703 impressions and 6 clicks, an aggregate CTR of 0.16%, with only two queries having received a single click. Positions 2, 3 and 4 together return 0 clicks on 10,693 impressions.

So the honest sentence is not "the curve is flat". It is "the sample is too small to draw a curve". To check that, we ran a control site through exactly the same code, a hotel site whose Search Console property we own, 253,273 raw rows. Its curve decays cleanly, from 3.02% aggregate CTR at position 1 to 0.20% at position 10. The script is not at fault, the sample size is.

Three things to read in this component, and the third is the most useful.

  1. Median CTR per query is 0% at every one of the 20 positions, on both sites, position 1 included. Not a bug: on the control site at position 1, 21 queries out of 47 received a click, so the median really is 0. A median CTR curve is unreadable by construction.
  2. The statistic that carries the shape is the share of queries clicked, not the median. On the control site: 44.7% at position 1, 27.3% at position 3, 5.8% at position 10. That is the number to track in a report.
  3. Beyond position 11, vydera.com rests on fewer than 10 queries, and four positions are empty. Those cells are published empty and marked too small. We do not interpolate.

One detail explains part of the flatness. The non-brand position-1 impressions on vydera.com are carried by conversational questions about a competing tool: "meteoria aeo tool", 389 impressions and 0 clicks; a 168-character question in Spanish, 314 impressions and 0 clicks; "meteoria vs other aeo tools", 283 impressions and 0 clicks. These are query fan-out impressions landing on our AI visibility tools comparison. They are not to be judged on clicks, and they flatten position 1 of the curve.

Two caveats on the control site, published alongside the rest: it is a hotel, crushed by its own brand, 4,338 brand clicks out of 4,647. It validates the method, it does not represent a B2B site.

And one caveat on us, more awkward. On vydera.com, the "brand" half of the calculation is a single query: 1,721 impressions at position 1, 278 clicks, 16.15% CTR. Those 278 clicks are 278 of the whole site's 284 position-1 clicks. Any "our site's CTR curve" published without splitting brand out is one brand query wearing a costume.

Why no CTR curve drawn from Search Console is accurate

Even with volume, two deflations stack, and neither is correctable from the API.

The first is the query dimension, the 30% one. We measured its effect on CTR itself, property by property: the CTR readable at query level divided by the site's real CTR gives a factor of 0.71 median across 16 properties, ranging from 0.15 to 1.34, with twelve properties out of sixteen below 1.

Do not overread it: four sit above, up to 1.34. The direction of the distortion depends on the site. So nobody can write "Search Console always underestimates CTR". What can be written is that it distorts it, and that the distortion takes two calls to measure on your own property.

The second is the position purity filter. An API row carries an average position, not a whole one: 6.4 blends impressions at 6 with impressions at 7. To compute a curve you have to discard those rows. On vydera.com the filter removes 16.7% of the remaining clicks, 419 down to 349; on the control site, 22.6%, 6,021 down to 4,661. And it does not remove them at random: a row blends two positions when it carries many impressions, so the filter cuts the busy days first.

One last judgement call, owned: the floor of 10 impressions per pair. It is not a truth, it is a choice. We recomputed the curve at 1, 5, 10 and 30 minimum impressions. At 30, nothing survives beyond position 10, and positions 5 to 10 fall to four queries or fewer. The floor shapes the curve as much as the data does.

What we do not publish, and why

The brief also promised an alert threshold per KPI. We are not publishing them. None of these measurements can set a threshold, and a threshold invented in an article is worse than an article without one: it ends up in somebody's report. A useful threshold is derived from twelve months of history on the site concerned, not from an industry average. The "what it decides" column replaces the promise, and that column is measured.

Three more numbers we get asked for and cannot produce:

  • The share of impressions served inside an AI overview. The searchAppearance dimension returns 0 rows on vydera.com and 5 on a large control property: JOB_DETAILS, JOB_LISTING, VIDEO, TRANSLATED_RESULT, PRODUCT_SNIPPETS. The dimension works, and none of its values separates an AI overview impression from a blue-link one.
  • AI crawler visits. The site runs on Webflow, which exposes no access log and no logging endpoint in its v2 API. Out of reach on this hosting, full stop.
  • A competitor's estimated organic traffic. No API measures it. The tools that display it model it from a panel and an average CTR curve, therefore from exactly the number this measurement shows to be unavailable.

One last awkward line, about our own site: organic conversions answer 200 and return zero. The control run without a channel filter gives 0 key events across 5,418 sessions on all channels. This is not SEO failing to convert, it is a GA4 property with no key event configured. The most important KPI of the set is out of reach until that setup exists, and the missing setup is ours.

So, the slide

Six lines, in this order.

  1. Non-brand clicks, never total clicks.
  2. Distinct queries returning at least one click, to see whether the capture surface is widening.
  3. Clicks per page, to know what to update next month.
  4. Indexation rate, because a page missing from the index will never appear in the first three lines. The full diagnosis sits in our 7-cause decision tree.
  5. Organic sessions and conversions from GA4, after checking that a key event actually exists.
  6. The share of clicks visible at query level, once, as a footnote. It says how much to trust the five lines above it.

What is not on it: average position, site-wide CTR, total impressions, domain authority. And no CTR-by-position curve until the site has the sample size to draw one.

Running the measurement yourself

The most profitable check takes two API calls and two minutes. Query searchAnalytics over twelve months with no dimension, then the same window with the query dimension, and divide. The ratio is your share of clicks visible at query level. Below 50%, any query-level report describes less than half your site, and it is better written into the slide than discovered in the meeting.

The rest follows the same logic. The URL Inspection API allows 2,000 inspections per day per property, the GA4 Data API answers in a few hundred milliseconds, and a consistency check costs one more call: brand plus non-brand must give back exactly the whole, click for click and impression for impression. On both properties tested here it does, and that is what makes the rest of the file believable.

For the links that feed indexation, see our internal linking method. And if you want this reporting to exist without assembling it yourself, that is the job of our performance tracking.

  • Which SEO KPIs should I present to leadership?

    Six are enough: non-brand clicks, distinct queries returning at least one click, clicks per page, indexation rate, organic sessions and conversions, plus the share of clicks visible at query level as a footnote. The four to drop from the slide: average position, site-wide CTR, total impressions, domain authority.

  • Why does Search Console average position mean nothing?

    It is a weighted average computed over unrelated queries. It moves as soon as the site gains impressions deep in the SERP, so it degrades as visibility improves. On vydera.com it reads 6.89 over twelve months, and no decision can rest on it.

  • Why does my Search Console CTR not match my real CTR?

    Because the query dimension shows only part of the clicks. On vydera.com, just 30.0% of clicks remain visible once that dimension is requested. Across 16 properties, CTR readable at query level is 0.71 times the site's real CTR at the median, ranging from 0.15 to 1.34. The direction of the distortion depends on the site: four properties out of sixteen sit above 1.

  • Can I compute my own CTR-by-position curve?

    Technically yes, the API answers 200. But you need enough clicks. On vydera.com, 50 non-brand clicks spread across 20 positions in twelve months support nothing: positions 2, 3 and 4 together show 0 clicks on 10,693 impressions. A control site run through the same code decays cleanly, from 3.02% to 0.20% between position 1 and position 10.

  • How do I measure the clicks Search Console does not show?

    Two searchAnalytics calls over the same window: one with no dimension, one with the query dimension. The ratio of the two click totals is your share visible at query level. Across the 16 properties tested, the median is 42.3%, with a minimum of 4.9% and a maximum of 80.2%.

  • Can citations in AI answers be measured?

    Not through a public API: none exposes them. The KPI exists and is rented from a specialised vendor. On vydera.com it was measured nowhere as of 27 August 2026: two active projects on the account, none on this domain. Read that as zero projects, not zero citations.


Thibaut Legrand
Thibaut Legrand
Co-founder - Vydera