SiteVerdict — Search Console & GA4 Analyst
Pricing
Pay per event
SiteVerdict — Search Console & GA4 Analyst
Find out why Google traffic dropped. Compares two periods in Search Console + GA4 with your own read-only Google access: ranking lost vs CTR drop vs demand vs deindexed (URL Inspection), quick wins, cannibalisation, tracking/consent red flags, top 10 actions.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Aman Thakran
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
9 days ago
Last modified
Categories
Share
Find out why your Google traffic dropped, and what to do first, in about a minute.
This Actor reads your Google Search Console and Google Analytics 4 data through Google's official APIs, using your own read-only access. It compares two periods and writes up the result the way an SEO analyst would:
- Why clicks fell, page by page. Each losing page gets a cause: ranking lost, CTR dropped, demand fell or page deindexed. Deindexing is confirmed with Google's URL Inspection API.
- Quick wins. Queries ranking 5–20 with real impressions, and pages whose CTR is far below what their position should earn.
- Keyword cannibalisation. Queries where two or more of your URLs split the impressions.
- Sitemap and index coverage summary.
- GA4 performance by channel, landing page, device and country, joined to Search Console landing pages. It also estimates the revenue at stake on the pages that lost clicks.
- Tracking and consent red flags. For example, GA4 organic sessions collapse while Google clicks hold steady, and the Actor finds the day it broke.
- Top 10 actions, a plain-English summary, evidence tables for every finding, and a shareable HTML report.
Everything is deterministic and rule-based: no AI guessing and no invented numbers. Every finding shows the data behind it.
Try it free first. With Google access = Sample data, it runs on a built-in fictional shop that has every problem planted, so you can see the full report before connecting your own account.
Who it is for
- SEO consultants and agencies. Diagnose a client's traffic drop and get a prioritised fix list. You can run it on a schedule for every client property.
- In-house marketers. Answer "why did organic drop?" before the Monday meeting.
- Developers and analysts. Get a clean JSON of GSC + GA4 deltas for dashboards, or for AI agents through the SiteVerdict MCP server.
What you get
| Output | Where |
|---|---|
| One dataset item per property: summary, headline metrics, drop causes, top 10 actions, all findings with evidence tables, URL Inspection results, sitemap summary, data notes | Dataset (views: Overview, Top 10 actions, All findings, Traffic-drop causes) |
| HTML report covering all properties | Key-value store REPORT |
| HTML report per property | report-<property> |
| Full tables (all losing/gaining pages and queries, countries, devices, daily series, GA4 breakdowns, the GSC↔GA4 join) | data-<property> (JSON) |
| Short machine summary | OUTPUT |
How causes are decided (transparent rules)
Only the biggest losing pages are classified: up to 25, ordered by clicks lost.
| Cause | Rule | Confidence |
|---|---|---|
| Page deindexed / not indexed | URL Inspection verdict is not PASS, indexing is blocked (noindex), or Google chose a different canonical | confirmed |
| Impressions collapsed by 90% or more and the URL was not inspected | suspected | |
| Ranking lost | Average position worsened (by at least 1, or 25% of the old position, in the top 10; by at least 3 below that) | likely |
| CTR dropped | CTR fell 20% or more while position and impressions held | likely |
| Demand fell | Impressions fell 20% or more at a stable position. Query-level demand drops are listed too | likely |
Each page also shows how much of its loss came from impressions and how much from CTR.
Tracking and consent sanity checks
Search Console counts clicks on Google's side, so your tags don't affect it. GA4 depends on your tag and your consent banner. Comparing the two catches measurement problems:
- GA4 organic sessions per Google click fall 30% or more between periods. Possible broken tag, consent change or GTM publish.
- The day it happened: the 7-day window where sessions per click collapsed while Google clicks stayed within ±20%.
- Landing pages with 30 or more Google clicks but zero GA4 organic sessions (tag missing on that template).
- High shares of "(not set)" landing pages or the "Unassigned" channel. A Direct share jump while organic falls.
- Key events or revenue collapsing while sessions hold. GA4 Realtime showing 0 users on a busy site.
To confirm a consent issue, run SiteVerdict — Consent Mode v2 & Tracking Auditor on the site. It loads the page with no click, after Reject and after Accept, and decodes Consent Mode v2.
The CTR curve is a benchmark
Opportunity sizes use an approximate industry-average CTR by position, compiled from public CTR studies. Real SERPs with ads, AI Overviews, featured snippets or shopping units earn less. The report also shows your own site's median CTR per position next to it, and you can supply your own curve (ctrCurve).
Setup: connect your own Google data (about 10 minutes, once)
You need a Google account that can already see the site in Search Console and GA4. The Actor only ever asks for read-only scopes (webmasters.readonly, analytics.readonly).
Option A: Service account (recommended; nothing expires)
- Create or choose a Google Cloud project. Go to https://console.cloud.google.com/ and use the project picker, then New project. The free tier is fine and no billing is needed for these APIs.
- Enable the two APIs. Go to APIs & Services > Library. Search for "Google Search Console API" and click Enable. Then search for "Google Analytics Data API" and click Enable.
- Create the service account. Go to IAM & Admin > Service accounts > + Create service account. Name it, for example,
siteverdict-reader, then click Create and continue > Done. It needs no project roles. - Create a key. Open the service account, then go to Keys > Add key > Create new key > JSON > Create. A
.jsonfile downloads. Copy itsclient_email(looks likesiteverdict-reader@your-project.iam.gserviceaccount.com). - Give it Search Console access. In Search Console, select the property, then go to Settings > Users and permissions > Add user. Paste the
client_emailand choose Restricted (read-only is enough), then Add. Repeat for each property. - Give it GA4 access. In GA4 go to Admin > (Property column) Property access management > + > Add users. Paste the
client_email, choose the role Viewer and untick "Notify new users". Click Add. - Find the GA4 Property ID. Go to GA4 Admin > Property details. It is the number, for example
123456789, not theG-…measurement ID. - Run the Actor. Set Google access = Service account and paste the whole JSON file into Service-account JSON key. Under Properties, add for example:
[{ "label": "My shop", "gscSiteUrl": "sc-domain:example.com", "ga4PropertyId": "123456789" }]
gscSiteUrl accepts sc-domain:example.com (Domain property), https://www.example.com/ (URL-prefix property) or just example.com. The Actor matches it against the properties the service account can see and lists them if it can't find yours.
Option B: OAuth client and refresh token (use your own Google login)
- In Google Cloud, enable the two APIs (steps 1–2 above).
- Set up the consent screen. Go to APIs & Services > OAuth consent screen: choose External, fill the app name and your email, and add yourself as a test user. To stop refresh tokens expiring after 7 days, click Publish app ("In production"). For your own use you can click through the "unverified app" warning.
- Create the client. Go to APIs & Services > Credentials > + Create credentials > OAuth client ID > Web application. Add the authorised redirect URI
https://developers.google.com/oauthplaygroundand copy the client ID and client secret. - Get a refresh token from the OAuth Playground:
- Open https://developers.google.com/oauthplayground. Click the gear icon, tick Use your own OAuth credentials and paste the client ID and secret.
- In Step 1, enter the scopes
https://www.googleapis.com/auth/webmasters.readonly https://www.googleapis.com/auth/analytics.readonlyand click Authorize APIs. Sign in. - In Step 2, click Exchange authorization code for tokens and copy the Refresh token.
- Run the Actor with Google access = OAuth and paste the client ID, client secret and refresh token.
Security and privacy
- All credential fields are secret inputs. Apify stores them encrypted, and the Actor uses them in memory only to get short-lived access tokens. They are never logged or written to the dataset, key-value store or report.
- Read-only scopes only. The Actor cannot change anything in Search Console or GA4.
- You can revoke access at any time. Delete the service-account key in Google Cloud, or remove the user in Search Console / GA4.
- Only aggregated metrics are requested: no user IDs, client IDs or user-level data. Search Console queries are aggregated and anonymised by Google. If a query ever contains a personal name, treat the output as you would your Search Console export (UK GDPR applies to your own use).
Input
| Field | Default | Notes |
|---|---|---|
authMethod | sample | sample, serviceAccount or oauth |
serviceAccountJson / oauthClientId / oauthClientSecret / oauthRefreshToken | – | Secret |
properties | – | [{label, gscSiteUrl, ga4PropertyId}], up to 50 per run. Either id may be omitted |
comparison | previous_period | Also year_over_year (364 days back, weekday-aligned) or custom |
periodDays | 28 | 7–180 |
dataLagDays | 3 | Search Console data is final after 2–3 days |
currentStartDate…previousEndDate | – | For custom |
searchType | web | Also image, video, news, discover or googleNews |
brandTerms | [] | Brand queries are excluded from opportunities and split out |
minImpressions | 100 | Threshold for opportunities and cannibalisation |
maxUrlInspections | 10 | Losing pages inspected per property (billed per URL) |
includeGa4Realtime | true | One runRealtimeReport call |
dataState | final | all includes fresh data |
maxRowsPerQuery | 25,000 | Per report, paged |
maxTableRows | 25 | Rows per evidence table |
ctrCurve | built-in | Your own CTR by position |
Example output (abridged)
{"label": "My shop","status": "ok","gsc": { "clicks": { "previous": 11380, "current": 7845, "changePct": -31.1 }, "avgPosition": { "previous": 6.3, "current": 6.8, "changePct": 7.9 } },"ga4": { "sessions": { "previous": 18023, "current": 10815, "changePct": -40 }, "organicSessions": { "previous": 9230, "current": 4228, "changePct": -54.2 }, "currency": "GBP" },"dropCauses": [{ "cause": "page_deindexed", "causeLabel": "Page deindexed / not indexed", "pages": 1, "clicksLost": 875 },{ "cause": "ranking_lost", "causeLabel": "Ranking lost", "pages": 1, "clicksLost": 965 }],"topActions": [{ "rank": 1, "priority": "critical", "action": "Check GA4 tracking before changing any content: GA4 organic sessions changed -54.2% while Google clicks changed -31.1%. ..." },{ "rank": 2, "priority": "critical", "action": "Restore indexing on 1 page(s) (top: https://www.example.com/hiking-boots/) - ..." }],"findings": [ { "id": "deindexed-pages", "severity": "critical", "title": "...", "evidence": { "columns": ["Page", "..."], "rows": [["https://www.example.com/hiking-boots/", 912, 37, "..."]] } } ],"reportUrl": "https://api.apify.com/v2/key-value-stores/.../records/report-my-shop"}
The full sample run is in docs/sample-output.json and docs/sample-report.html.
Pricing
Pay per event:
property-analysed: once per property per run. It is only charged after real data has been fetched; errors and empty results are free.url-inspected: per successful URL Inspection call.
Sample mode is free apart from the Apify start event. The Actor stops before it would exceed your run's maximum total charge: inspections are skipped and remaining properties are not started. See PRICING.md for the proposed prices.
Limits and quotas (Google, checked 11 Oct 2026)
Calls run on your Google Cloud project's quota, not a shared one.
| API | Limit | What this Actor uses |
|---|---|---|
| Search Analytics | 1,200 queries/min per site and per user; short-term load quota in 10-minute windows. At most 50,000 rows per day per search type are exposed; rowLimit max 25,000 | About 13 queries per property plus paging |
| URL Inspection | 2,000/day and 600/min per site | maxUrlInspections (default 10), at most 400/min |
| GA4 Data API (standard property) | 200,000 tokens/day, 40,000/hour, 14,000 per project per property per hour, 10 concurrent requests | 14 runReports (≤7 in parallel) plus 1 realtime |
Sources: https://developers.google.com/webmaster-tools/limits · https://developers.google.com/webmaster-tools/v1/how-tos/all-your-data · https://developers.google.com/analytics/devguides/reporting/data/v1/quotas
Other limits:
- Search Console hides rare queries ("anonymised"). The report shows the share of clicks with no visible query.
- GA4 may threshold, sample or group rows into "(other)". These cases are flagged in
notes. - The GSC↔GA4 join matches on URL path. GA4 has no hostname in
landingPage, so multi-host Domain properties are joined on the host with the most clicks. - GA4 "Organic Search" includes Bing and other engines, so treat sessions per click as a sanity ratio.
- Sitemap "indexed" counts are deprecated in the API. Coverage uses pages with impressions plus URL Inspection samples as a proxy.
FAQ
Does it need my Google password? No. It uses a service-account key or an OAuth refresh token that you create and can revoke.
Will it change anything in my accounts? No. It uses read-only scopes only.
Why does it say it "cannot see" my property? The service-account email has not been added in Search Console (Settings > Users and permissions), or you typed a different property form. The error lists the properties it can see.
It says the API is not enabled. Enable "Google Search Console API" and "Google Analytics Data API" in the same Cloud project as the key, wait a few minutes, then rerun.
My OAuth token stopped working after a week. Apps left in "Testing" status get refresh tokens that expire after 7 days. Publish the consent screen, or use a service account.
Is this AI-generated advice? No. All text comes from fixed templates filled with your numbers, and the rules are documented above.
Compliance and sources
- Data comes only from Google's official APIs with credentials you supply: Search Console API (searchanalytics.query, sitemaps.list, urlInspection.index.inspect) and Google Analytics Data API v1beta (runReport, runRealtimeReport). There is no scraping.
- The CTR benchmark is an approximation of public CTR studies and is labelled as such in every output.
- This is diagnostic analysis, not legal advice (including on consent/PECR/UK GDPR compliance) and not a guarantee of rankings or revenue.
- Where results are stored: the default dataset and key-value store of the run (the analysis rows, the raw API data per property as
data-…records, HTML reports andOUTPUT), inside your own Apify account. These default storages are unnamed. How long Apify keeps them is set by Apify and your plan, not by SiteVerdict (Apify data retention); you can delete them sooner in Apify Console. The Actor keeps no named store. - Data licence: your Search Console and GA4 data stays yours; it is fetched under Google's API terms with your own credentials.
- What SiteVerdict keeps: nothing. The Actor runs in your Apify account; data goes only between Google's APIs and your Apify storage, never to SiteVerdict.
Made by SiteVerdict (BharatFare Travel and Technologies Ltd, UK).