Diagnose traffic losses
The tool says why the queries that lost the most clicks lost them. The class matters more than the size of the loss, because each cause calls for a different action. The tool finds the period where the series actually stepped, and it splits the change around that period. A rank drop in the middle of a window is smoothed by a window average, and the lost impressions then look like falling demand. case is the class. A means the page was outranked in place, so strengthen the page. A_prime means a different page of yours took the place, so merge the pages. B means the ranking held and the clicks fell, so rewrite the title, the description, or the schema. This is usually the cheapest fix. C means the impressions fell, the ranking held, and demand is ruled out. No search-console feed shows what else is on the results page, so an AI Overview or a feature block cannot be seen from this data. Call explain_query_serp on a C or B query. That tool fetches the results page live and compares it with the last one stored. Only about 100 queries, captured weekly, have a stored baseline. Outside that set, the first call creates one. D means the rank fell across the page-one boundary. The impressions followed the ranking, so this is a ranking loss. E means clicks fell on both engines and the position stayed flat, so this is demand. Two limits belong in any answer you build from this. An E row supports fewer people searching this exact phrasing. It cannot show fewer people searching the topic, because nothing here looks at neighbouring queries, so a reworded query looks the same. And a demand result is useful. It stops a content cycle being spent on a page that is fine. This tool names a cause, so the row limit matters more here than on the other search tools. A period Google cut looks like an impressions fall with nothing behind it. The cross-engine check does not catch that, because Bing's own feeds are a top-results list, and a query missing from both looks like the two engines agreeing. Google keeps about 5,000 rows per day for each single breakdown once a day is more than about three days old. On a large site the tool is reading the top queries or pages by impressions. Google no longer stores the rest. If that applies to this window, the response names the dates in unsupportedInputs. A missing row means it was outside that list. Google no longer has the row, so there is nothing further to fetch.
diagnose_traffic_lossesThe tool says why the queries that lost the most clicks lost them.
The class matters more than the size of the loss, because each cause calls for a different action. The tool finds the period where the series actually stepped, and it splits the change around that period. A rank drop in the middle of a window is smoothed by a window average, and the lost impressions then look like falling demand.
case is the class. A means the page was outranked in place, so strengthen the page. A_prime means a different page of yours took the place, so merge the pages. B means the ranking held and the clicks fell, so rewrite the title, the description, or the schema. This is usually the cheapest fix.
C means the impressions fell, the ranking held, and demand is ruled out. No search-console feed shows what else is on the results page, so an AI Overview or a feature block cannot be seen from this data. Call explain_query_serp on a C or B query. That tool fetches the results page live and compares it with the last one stored. Only about 100 queries, captured weekly, have a stored baseline. Outside that set, the first call creates one.
D means the rank fell across the page-one boundary. The impressions followed the ranking, so this is a ranking loss. E means clicks fell on both engines and the position stayed flat, so this is demand.
Two limits belong in any answer you build from this. An E row supports fewer people searching this exact phrasing. It cannot show fewer people searching the topic, because nothing here looks at neighbouring queries, so a reworded query looks the same. And a demand result is useful. It stops a content cycle being spent on a page that is fine.
This tool names a cause, so the row limit matters more here than on the other search tools. A period Google cut looks like an impressions fall with nothing behind it. The cross-engine check does not catch that, because Bing's own feeds are a top-results list, and a query missing from both looks like the two engines agreeing.
Google keeps about 5,000 rows per day for each single breakdown once a day is more than about three days old. On a large site the tool is reading the top queries or pages by impressions. Google no longer stores the rest.
If that applies to this window, the response names the dates in unsupportedInputs. A missing row means it was outside that list. Google no longer has the row, so there is nothing further to fetch.
How it decides
The tool uses changes in ranking, impressions, and clicks to classify each query's
traffic loss. Impressions count how often a search engine shows your page in its
results.
The tool follows these steps.
- Find the week or month when the metrics changed.
- Compare ranking, impressions, and clicks before and after that period.
- Check whether a different page of yours replaced the original page.
- Compare Google and Bing data to distinguish a Google-only loss from a loss on both search engines.
Comparing whole calendar windows can hide a ranking drop that happens partway
through a window. The resulting impression loss can then look like lower search
demand.
The decision tree shows how these checks separate the causes.
The comparison with Bing helps separate changes on Google's results page from
lower demand for the query. A loss on both search engines with a stable ranking
supports the demand classification. Search-console data does not show which
features appeared on the results page.
What each answer means
The table explains each classification and its suggested action.
| What you see | What it means | What to do |
|---|---|---|
| Ranking fell, and the same page still ranks | A competitor outranked your page. | Strengthen that page. |
| Ranking fell, and a different page of yours now ranks | Two of your pages compete for the same result. | Consolidate the pages. |
| Ranking fell beyond page one | The impression loss follows the ranking loss. It does not establish lower demand. | Treat this as a ranking problem. |
| Ranking and impressions stayed stable, but clicks fell | Fewer people clicked your result. Search-console data does not show whether a result feature or changed title caused the loss. | Review the title, description, and schema. Run explain_query_serp to inspect the results page. |
| Ranking stayed stable, but impressions fell on Google only | The classification points to a change on Google's results page rather than lower demand. | Run explain_query_serp to inspect the results page. |
| Ranking stayed stable, but traffic fell on both search engines | Fewer people searched this exact phrasing. | Check whether the loss repeats each year before rewriting the page or moving the effort elsewhere. |
Lower demand does not establish a page problem
A demand classification helps you avoid rewriting a page when the data does not
show a problem with that page.
The tool does not compare related queries. It cannot distinguish lower interest
in the topic from searches that use different words. The result supports fewer
searches for this exact phrasing, not fewer searches for the whole topic.
Parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
site | string | Yes | The site to look up, as a bare domain ("example.com") or a URL. The tool strips the scheme, www, path, and trailing slash. The site must belong to a workspace you own. Call list_sites first if you are not sure of the exact domain. |
granularity | "weekly" | "monthly" | No | Length of one period in the series: weekly or monthly. Daily is not offered. These tools ask whether a change held across periods, and a one-day period would measure weekday patterns instead. Defaults to "monthly". |
periods | integer | No | How many whole periods to read. Defaults to 13 monthly periods, the shortest series the seasonal-versus-secular test can run on; shorter windows still diagnose every other class and report seasonality "untested". Defaults to 13. |
to | string | No | Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for. |
minImpressions | integer | No | Minimum impressions over the whole window per query. Defaults to 50. |
excludeBranded | boolean | No | Exclude branded queries. On by default: branded queries carry far higher CTR at the same position, so leaving them in pulls the expected-CTR curve upward and under-reports the CTR class on exactly the non-branded queries that matter. Defaults to true. |
checkPageSwap | boolean | No | Run the page↔query join to separate class A from A′. Costs two extra lake scans and is skipped automatically when no row reached class A; turn it off to keep the call cheap and accept pageSwap.status: "unknown". Defaults to true. |
limit | integer | No | How many of the biggest losers to diagnose. Kept small, because each row is a paragraph of evidence. Defaults to 10. |
searchVertical | "web" | "image" | "video" | No | Which Google search type to read: web (the default), image, or video. Bing Webmaster Tools does not split search types on any traffic report, so image and video return Google only. The response records that Bing was left out, in unsupportedInputs. A missing Bing number then means Bing was not measured. Defaults to "web". |
Full input JSON SchemaTypes, defaults and validation rules
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string", "minLength": 1, "description": "The site to look up, as a bare domain (\"example.com\") or a URL. The tool strips the scheme, www, path, and trailing slash. The site must belong to a workspace you own. Call list_sites first if you are not sure of the exact domain." }, "granularity": { "default": "monthly", "description": "Length of one period in the series: weekly or monthly. Daily is not offered. These tools ask whether a change held across periods, and a one-day period would measure weekday patterns instead.", "type": "string", "enum": [ "weekly", "monthly" ] }, "periods": { "default": 13, "description": "How many whole periods to read. Defaults to 13 monthly periods, the shortest series the seasonal-versus-secular test can run on; shorter windows still diagnose every other class and report seasonality \"untested\".", "type": "integer", "minimum": 2, "maximum": 104 }, "to": { "description": "Last day of the current window (YYYY-MM-DD). Defaults to the most recent date either engine has data for.", "type": "string" }, "minImpressions": { "default": 50, "description": "Minimum impressions over the whole window per query.", "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "excludeBranded": { "default": true, "description": "Exclude branded queries. On by default: branded queries carry far higher CTR at the same position, so leaving them in pulls the expected-CTR curve upward and under-reports the CTR class on exactly the non-branded queries that matter.", "type": "boolean" }, "checkPageSwap": { "default": true, "description": "Run the page↔query join to separate class A from A′. Costs two extra lake scans and is skipped automatically when no row reached class A; turn it off to keep the call cheap and accept `pageSwap.status: \"unknown\"`.", "type": "boolean" }, "limit": { "default": 10, "description": "How many of the biggest losers to diagnose. Kept small, because each row is a paragraph of evidence.", "type": "integer", "exclusiveMinimum": 0, "maximum": 50 }, "searchVertical": { "default": "web", "description": "Which Google search type to read: web (the default), image, or video. Bing Webmaster Tools does not split search types on any traffic report, so image and video return Google only. The response records that Bing was left out, in unsupportedInputs. A missing Bing number then means Bing was not measured.", "type": "string", "enum": [ "web", "image", "video" ] } }, "required": [ "site" ]}Response
Full output JSON SchemaEvery field the response can contain
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "site": { "type": "string" }, "workspaceId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "searchVertical": { "type": "string", "enum": [ "web", "image", "video" ] }, "periodWindow": { "type": "object", "properties": { "granularity": { "type": "string" }, "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "window": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": [ "start", "end" ], "additionalProperties": false }, "periodStarts": { "type": "array", "items": { "type": "string" } }, "excludedPartialPeriod": { "anyOf": [ { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } }, "required": [ "start", "end" ], "additionalProperties": false }, { "type": "null" } ] } }, "required": [ "granularity", "periods", "window", "periodStarts", "excludedPartialPeriod" ], "additionalProperties": {} }, "rows": { "type": "array", "items": { "type": "object", "properties": { "query": { "type": "string" }, "primaryEngine": { "type": "string", "enum": [ "google", "bing" ] }, "case": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "caseLabel": { "type": "string" }, "confidence": { "type": "string" }, "recommendation": { "type": "string" }, "explanation": { "type": "string" }, "estimatedClicksLost": { "type": "number" }, "change": { "type": "object", "properties": { "changepoint": { "type": "object", "properties": { "periodStart": { "type": "string" }, "before": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} }, "after": { "type": "object", "properties": { "periods": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "clicks": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "impressions": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "ctr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "clicksPerPeriod": { "type": "number" }, "impressionsPerPeriod": { "type": "number" } }, "required": [ "periods", "clicks", "impressions", "position", "ctr", "clicksPerPeriod", "impressionsPerPeriod" ], "additionalProperties": {} } }, "required": [ "periodStart", "before", "after" ], "additionalProperties": {} }, "deltaClicksPerPeriod": { "type": "number" }, "deltaImpressionsPerPeriod": { "type": "number" }, "deltaPosition": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "deltaCtr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "decomposition": { "type": "object", "properties": { "impressionsTerm": { "type": "number" }, "ctrTerm": { "type": "number" } }, "required": [ "impressionsTerm", "ctrTerm" ], "additionalProperties": {} }, "series": { "type": "array", "items": { "type": "object", "properties": { "periodStart": { "type": "string" }, "clicks": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "impressions": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "position": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "periodStart", "clicks", "impressions", "position" ], "additionalProperties": {} } } }, "required": [ "changepoint", "deltaClicksPerPeriod", "deltaImpressionsPerPeriod", "deltaPosition", "deltaCtr", "decomposition", "series" ], "additionalProperties": {} }, "crossEngine": { "type": "object", "properties": { "control": { "type": "string" }, "otherEngine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "otherDeltaImpressionsPerPeriod": { "anyOf": [ { "type": "number" }, { "type": "null" } ] } }, "required": [ "control", "otherDeltaImpressionsPerPeriod" ], "additionalProperties": {} }, "expectedCtr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "actualCtr": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "pageSwap": { "type": "object", "properties": { "status": { "type": "string" }, "beforeTopPage": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "afterTopPage": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "status" ], "additionalProperties": {} }, "seasonality": { "type": "string" } }, "required": [ "query", "primaryEngine", "case", "caseLabel", "confidence", "recommendation", "explanation", "estimatedClicksLost", "change", "crossEngine", "expectedCtr", "actualCtr", "pageSwap", "seasonality" ], "additionalProperties": {} } }, "unsupportedInputs": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "engine": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "reason": { "type": "string" } }, "required": [ "field", "reason" ], "additionalProperties": {} } }, "lastSyncedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "site", "workspaceId", "searchVertical", "periodWindow", "rows", "unsupportedInputs" ], "additionalProperties": false}