siteline Start free trial Log in

Search Console clicks are not analytics sessions

See your data

A Search Console click means a person clicked your listing on a Google result. A SiteLine visitor means a browser requested your site and the tracker ran, and the identity for that calendar day is a hash. A GA4 or Matomo session means a cookie or a timeout rule opened a visit. Those are different events, counted by different systems, on different clocks. They will not match. A report that puts them in one cell titled "organic" has already chosen the wrong unit.

You can still read them on the same page. Each number keeps its ledger. The click ledger answers what Google showed and what Google counted as a click. The visitor ledger answers what loaded on the site. The gap between a click count and a visitor count is not a remainder, and it is not lost attribution waiting to be subtracted.

The units

Google's Performance report defines the search side. A click is the number of times a user clicked your site from Google Search results. An impression is a time your site appeared. CTR is the click count divided by the impression count. Average position in the chart is the average position of the topmost result from the property; in the table it is the average position for that row's URL or grouping. The API returns CTR as a fraction from 0 to 1, so 0.04 is 4%, and it returns position already averaged for that row. You do not average those averages across rows and call it a weekly rank.

SiteLine's on-site unit is narrower than a session product. A visitor is a daily hash of a truncated IP, the user agent, and a daily salt. IPv4 is truncated to /24. IPv6 is truncated to /48. The salt rotates each day, so tomorrow's hash is a new visitor even when the same browser comes back. There is no analytics cookie stitching Tuesday to Wednesday. A pageview is a load where the script ran. Several pageviews can share one visitor-day. Several Google clicks can too.

Unit Who counts it When it fires Clock
Impression Google Your result was shown Typically published after 2–3 days, and revised after that
Click Google The result was clicked Same publish lag as impressions
CTR A ratio, not a stored event Clicks ÷ impressions for that slice Same as the clicks and impressions you divided
Position Google, already impression-weighted for the slice Mean rank of that row Same lag. Null on Discover and Google News, not zero
Pageview Your tracker The page loaded and the script ran Minutes
Visitor (SiteLine) Your tracker Daily hash of truncated IP + user agent + daily salt Minutes, and the hash resets the next day
Session (GA4 or Matomo) That product's tracker Cookie or timeout rules Minutes, and often only after a consent banner

Site totals and query rows are a second split inside Google's ledger, not a split between Google and your tracker. Anonymised queries are left out of the table and included in chart totals unless you filter by query. SiteLine stores up to 25,000 rows per day per report, which is one API page. Clicks on the site card come from site totals. Clicks on the query table are named queries that were imported. Those two Google numbers disagree with each other before a visitor count enters the conversation. The 1,000-row limit is that disagreement. Organic traffic versus Search Console is the version where an analytics channel name hides it.

A worked day, with the subtraction left undone

This is a worked illustration, not a measured site. One published Web day. One path. Numbers are round so the arithmetic can be checked by hand.

Google's page row for that URL, Web search:

Two named query rows for the same URL on the same day:

Row Clicks Impressions Row CTR Position
Query A 22 200 22 ÷ 200 = 0.11 1.5
Query B 11 500 11 ÷ 500 = 0.022 9.0
Sum of these two rows 33 700 33 ÷ 700 = 0.04714… see below

The page row says 40 clicks. The two named rows say 33. The 7-click difference stays inside Google's ledger: clicks with no query name in this illustration. It is the same shape as the privacy gap Google publishes in the performance-data deep dive, where four itemised queries sum to 450 clicks and the chart total is 550. Their illustration is privacy filtering only. Ours is the same kind of object: a chart (or page) total and a short named list. Neither difference is a visitor count.

Position, if you insist on one number for these two rows only:

5.25 treats a 200-impression query like a 500-impression query. 6.857… is the position of this pair. It is not the position of the page. The page has 1,000 impressions, and these rows only account for 700 of them. The other 300 impressions are not in the pair, so they cannot be in the pair's weighted position. Use the page row's position for the page. Use the weighted figure only when you have named the rows in the denominator.

CTR has the same trap:

Three ratios, three denominators. The one that matches the Performance report's definition for the page is 0.04. The 0.066 figure is an average of ratios, which the report does not compute.

Now the other ledger, same path, same calendar day, still an illustration:

40, 28, and 51 are three counts of three events. Leave them in three cells. Subtracting visitors from clicks produces a number that is not lost clicks, not untracked sessions, and not a fudge factor to reuse next week. The next section lists causes that can all be true on the same day. They do not combine into one remainder, so assigning each cause a share of the click count would be a second fiction.

A smaller illustration shows the units diverging by construction, still not a customer. One browser, one day, one /24, one user agent: the person clicks a result, hits back, and clicks again. Google counts two clicks. The daily hash counts one visitor. The script records three pageviews because a later internal link loaded as well. Two, one, and three are all correct. None of them is the error term of the others.

Causes that stay in their own column

Expect the ledgers to disagree. When you investigate, pick one landing path, one search type, and one complete Search Console day, then look at one cause at a time.

Publish lag. The API how-to says data is typically available after 2–3 days. On Wednesday, Web clicks are often complete through Monday. The tracker already has Tuesday and Wednesday. Align the range to days Search Console has published before you put the numbers in the same sentence. Fresh rows still move after they appear; SiteLine re-imports the last 7 days every 4 hours so the click side can catch up. Comparing a moving Monday with a finished Monday is its own discrepancy. It is not a visitor gap.

Anonymised queries. Google drops queries that were not issued by more than a few dozen users over a two-to-three month period. The clicks stay in the chart totals unless you filter by query. They never become rows. This explains a query-sum versus a site total. It does not explain a site total versus a visitor-day. Once you are looking at site-total clicks, anonymisation has already been included.

The 1,000-row UI export, and the 25,000-row stored page. Both cut named rows. Both leave chart totals alone. Summing an export and calling the sum "clicks" undercounts the Google ledger before analytics is involved. Site totals are the click headline. The long table is the named tail you can see, up to 25,000 rows per day per report, which is one API page and not Google's second page.

Discover and Google News. These search types have no query. Nobody typed one, and the API rejects the query dimension for them. Position is null, not zero. A spreadsheet download can turn a not-available mark (~ or -) into a numeric zero; Google's export help says that conversion happens in the file. A zero in that cell is a download artifact. It is not rank 0, and it is not "above position 1." The News tab in Google Search is a different type from Google News the product: the News tab can have queries and a position. Mixing Discover clicks into a Web click total, or into an analytics "organic" channel, compares a feed with a results page. Keep the types apart.

Consent-gated GA4. A GA4 session exists when the tag fired. A banner that blocks the tag until consent removes sessions Google has already counted as clicks. Search Console does not ask your banner. SiteLine's cookieless tracker does not use that consent gate either, and it still misses requests where the script never ran: ad blockers, a template that omitted the snippet, an in-app browser that dropped it. Cookieless removes one filter. It does not intercept the click on Google's page. Install is /docs/script.

The click never became a paint. The tab closed. The server was slow. An interstitial sat in front of the script. Google counted the click on the result. Your tracker counts a load. Both records can be right.

Redirects and canonicals. Google attributes the click to the URL it showed. The tracker records the URL that rendered. HTTP to HTTPS, a trailing slash, or a hop from /blog/post to /blog/post/ puts the click on one path and the visitor-day on the other. Join on the URL that actually loaded, or group both URLs on purpose. A raw string match will show a click with no visit and a visit with no click, which is the join failing, not demand disappearing.

Several clicks, one visitor-day. Back-button, a second result on the same /24, the same user agent, the same salt. Two clicks, one hash. The hash is supposed to collapse those. That collapse is the definition of the visitor, not a tracking bug.

One click, many pageviews. The landing load and every later load that day share the visitor hash. The on-site number that sits next to a Search Console page row is landings on that path, or pageviews of that path, labelled as such. It is not "all pageviews in the session," because SiteLine does not stitch a session across the day beyond the hash, and it does not stitch across days at all.

Search Console's own 16-month window. A click from month 17 is absent on Google's side even when the tracker still has the visit, or the reverse if you installed the tracker late. That is a missing day, not a ratio between clicks and visitors. The 16-month limit is the calendar version.

None of these is fixed by multiplying clicks by 0.8. If the weekly note needs a reconciliation, write the cause you actually checked. A single variance percentage blends lag, anonymisation, consent, redirects, and the hash into a number nobody can act on.

How to put them on the same page

Use four blocks, each labelled with its ledger.

  1. Demand. Search Console site totals for one search type, usually Web: clicks, impressions, CTR as clicks ÷ impressions, impression-weighted position. End the range at the last published day (data_through). These numbers come from the totals request, not from a sum of query rows.
  2. Named queries and pages. The breakdown you imported, footnoted as incomplete. Up to 25,000 rows per day per report. Countries, devices, and search appearance are smaller lists; Google says those non-URL, non-query tables are fully exported on its side. Query and page tables are the ones the UI cuts at 1,000 representative rows.
  3. Behaviour. Cookieless pageviews and visitor-days for the same paths and the same calendar dates, plus whatever goals you defined on those paths. This block is the hash ledger. It can be higher or lower than the click block. Report both.
  4. The sentence. One clause for what Google counted, one clause for what the tracker recorded, one clause for the cause you checked if they diverge. No third column called variance.

MCP exposes both ledgers to an agent. The Search Console tools carry data_from, data_through, and a note that breakdown rows do not sum to site totals. The analytics tools are the visitor side. An agent that subtracts one payload from the other is repeating the spreadsheet mistake in prose. Tell it to quote each source.

The click import is webmasters.readonly. It cannot edit Search Console. History on that side is the backfill Google still held at connect, then each later day while the subscription continues, with the last 7 days refreshed because Google revises them. The visitor side starts when the script is on the site. Neither side backfills the other.

What a client page should show first

Show Search Console site-total clicks for demand. Show on-site goals, if you have them, for what happened after the load. Visitor-days sit in the middle as a reach number for that day: how many distinct daily hashes hit the path. Sessions from a cookie product are a third on-site definition and will disagree with the hash even when both tags fired. If a client asks for one chart, give them two charts with the units in the titles.

A landing-page conversion rate is goals divided by landed pageviews, or by visitor-days, and you have to say which. It is not CTR. A title can earn a 0.12 CTR on Google and a form on that URL can convert almost none of the loads. Average neither ratio across pages. A high CTR row with 40 impressions is a different decision from a 0.04 CTR row with 50,000 impressions.

Plans that include the import and the tracker start at $4.99, $19.99, and $99.99 a month. Search Console rows are not billable analytics events. Start at /register. The snippet is /docs/script.

If the dashboard's first row is "GSC clicks," the second is "sessions," and the third is "variance %," delete the third row. Write the sentence instead.

FAQ

Which number should I show a client first?

Site-total Web clicks for the published range, then the on-site goal count for the same dates and paths. Visitor-days are the reach figure between those two, labelled as daily hashes. If they want one number, they still get two. A single "organic" cell is how the ledgers get subtracted by accident.

Why is a SiteLine visitor count different from GA4 sessions on the same week?

The units differ before any Search Console number is involved. SiteLine's visitor is one hash per truncated IP, user agent, and daily salt, and it resets the next day. A GA4 session follows Google's cookie and timeout rules and often fires only after consent. Bot filtering and a missing snippet move the counts further. Calibrate each product to the question it answers. Do not scale one to the other with a constant.

Can I use Search Console CTR as a landing-page conversion rate?

No. CTR is clicks ÷ impressions on Google. A conversion rate is goals ÷ loads on your site. They share a URL only after you have decided which URL Google ranked and which URL rendered. Keep the ratios in their own columns.

Should I subtract visitors from clicks to find untracked traffic?

No. Do not subtract visitors from clicks. The difference is not a pile of lost people. Lag, anonymised queries, Discover, consent, redirects, repeat clicks, and the daily hash are separate causes. Check one cause on one path and one published day.

Why is position empty on Discover?

Discover and Google News are feeds, not ranked result pages. There is no query, and position is null. It is not zero. A CSV can write 0 where the report showed a dash; ignore that 0. Web, image, video, and the News tab still have a position.

Why do my query clicks miss the chart total when my analytics is fine?

That miss is inside Search Console. Anonymised queries are in the chart and absent from the table, and a UI export adds a further cut to 1,000 representative rows. Analytics being "fine" does not fill in query strings Google withheld. Quote the site total for the click count, and quote the table as the named rows you can see.

Does the two-day lag mean yesterday's visitors are wrong?

Yesterday's visitors are yesterday's hashes. Yesterday's clicks may not be published yet, or they may be fresh and still revising. Hold the click comparison until data_through includes the day. The visitor number does not need to wait, and it does not need to be edited when the clicks arrive.