Google Search Console ACTION
Query Search Analytics
Query Google Search Console search analytics for one property and one date range: clicks, impressions, CTR and average position, optionally grouped by dimensions and filtered. This is the main traffic-reporting tool for a site.
Use for any single-date-range question about how a site performs in Google Search — top queries, top pages, country or device splits, daily or hourly trends, the CTR or average position of a term. Use Compare Search Analytics instead for period-over-period questions (month over month, year over year, "did the update hurt us") — it fetches both ranges and joins them for you. Use Inspect URLs for index status, canonicals and crawl state; this tool only reports traffic.
Returns the API response unchanged — rows (each { keys, clicks, impressions, ctr, position }, where keys lines up positionally with dimensions), responseAggregationType and metadata — plus row_count, has_more (the page came back full, so more rows probably exist), next_start_row (pass it back as startRow) and returned_totals (clicks and impressions summed over the RETURNED rows only).
Reading the numbers. ctr is a 0-1 fraction (0.1428 means 14.3%) and position is a 1-indexed float where LOWER is better. Both are impression-weighted, so never re-average them across rows — a plain mean is wrong. returned_totals is not the property total, least of all when grouping by query: Google omits anonymized (rare) queries, so the sum of query rows is materially LESS than the same range grouped by date. For a true total, query with no dimensions or with date. And check has_more before reporting a count or a "top N" — a truncated first page is not the whole answer.
Scoping to one page or segment. Set subdomainFilter — the filter VALUE, despite the legacy prop name — together with filterDimension and filterOperator. For "page X only" that is filterDimension: page, filterOperator: equals, value = the full URL. Selecting a matching URL-prefix property instead is NOT equivalent: a prefix property covers every page under it, so the numbers come back for the whole prefix. Use advancedDimensionFilters for multi-condition filters; it is ignored whenever subdomainFilter is set.
Freshness. The most recent 2-3 days are not final and are omitted under dataState: final (the default), so a range ending today comes back one to three rows short. When the user asks for "the last N days" and every day needs a figure, set dataState: all.
Discover has no query dimension (400 "Request for DISCOVER cannot be grouped by query"). When the user asks for Discover queries, do not stop to ask: report Discover pages instead (searchType: discover, dimensions: ["page"]) and say why.
Example. siteUrl="sc-domain:example.com", startDate="2025-09-01", endDate="2026-08-31", dimensions=["query"], rowLimit=10 returns rows such as { keys: ["example brand"], clicks: 41, impressions: 287, ctr: 0.1429, position: 2.4 } plus row_count: 10, has_more: true and next_start_row: 10.
There is no fields parameter — rows are already minimal, so rowLimit plus has_more/next_start_row is the payload lever. Quota: 1,200 queries per minute per site. See the documentation
- Action
- Read only
- OAuth
- SDK
- MCP
IMPLEMENTATION
Call this tool
Connect a user's Google Search Console account once, then configure and run Query Search Analytics from your backend or agent.
import { PipedreamClient } from "@pipedream/sdk"
const pd = new PipedreamClient({
projectId: process.env.PIPEDREAM_PROJECT_ID!,
clientId: process.env.PIPEDREAM_CLIENT_ID!,
clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
projectEnvironment: "production",
})
const result = await pd.actions.run({
id: "google_search_console-retrieve-site-performance-data",
externalUserId: "{external_user_id}", // any stable ID for this user in your system
configuredProps: {
google_search_console: { authProvisionId: "apn_xxxxxxx" },
siteUrl: "Property (siteUrl)",
startDate: "Start Date (YYYY-MM-DD)",
},
})
console.log(result)curl -X POST https://api.pipedream.com/v1/connect/{project_id}/actions/run \
-H "Content-Type: application/json" \
-H "X-PD-Environment: production" \
-H "Authorization: Bearer {access_token}" \
-d '{
"external_user_id": "{external_user_id}",
"id": "google_search_console-retrieve-site-performance-data",
"configured_props": {
"google_search_console": { "authProvisionId": "apn_xxxxxxx" },
"siteUrl": "Property (siteUrl)",
"startDate": "Start Date (YYYY-MM-DD)"
}
}'// accessToken: mint a short-lived token with the Connect SDK — see the MCP guide
const transport = new StreamableHTTPClientTransport(
new URL("https://remote.mcp.pipedream.net/v3"),
{
requestInit: {
headers: {
Authorization: `Bearer ${accessToken}`,
"x-pd-project-id": "{project_id}",
"x-pd-environment": "production",
"x-pd-external-user-id": "{external_user_id}", // any stable ID for this user in your system
"x-pd-app-slug": "google_search_console",
},
},
},
)
const mcp = new Client({ name: "my-agent", version: "1.0.0" })
await mcp.connect(transport)
const { tools } = await mcp.listTools()
// listTools() hands your model this tool's input schema, so it can
// fill the arguments itself:
const result = await mcp.callTool({
name: "google_search_console-retrieve-site-performance-data",
arguments: {
siteUrl: "Property (siteUrl)",
startDate: "Start Date (YYYY-MM-DD)",
},
})SCHEMA
Inputs
Pipedream supplies the connected account. Your application provides the operation-specific values below. Dynamic inputs are resolved against that user's account.
| Property | Type | Description |
|---|---|---|
siteUrl Property (siteUrl) | string | Exact property identifier from List Sites — sc-domain:example.com for a domain property, or a URL-prefix such as https://www.example.com/ (trailing slash; scheme and subdomain must match exactly, or the call 403s). Copy it verbatim, never construct it. For traffic questions prefer the domain property when one exists: it covers all subdomains and protocols. Required |
startDate Start Date (YYYY-MM-DD) | string | First day of the range, inclusive, YYYY-MM-DD Pacific Time — e.g. 2025-09-01. Search Console keeps 16 months; an earlier date returns a 400. The most recent 2-3 days are not final and are omitted unless dataState is all. Required |
endDate End Date (YYYY-MM-DD) | string | Last day of the range, inclusive, YYYY-MM-DD Pacific Time — e.g. 2026-08-31. Reporting lags 2-3 days, so an end date of today returns nothing for the final days unless dataState is all. Required |
dimensions Dimensions | string[] | How to group the rows; each row's keys lines up positionally with this list, so ["query","device"] yields keys: ["example brand","MOBILE"]. Leave empty for a single totals row. Combination rules, each a 400 if broken: searchAppearance cannot be combined with ANY other dimension (fetch the appearance types alone, then filter by one with filterDimension: searchAppearance); hour cannot be combined with date, requires dataState: hourly_all, and covers at most 10 days; Discover (searchType: discover) has no query dimension. date returns one row per calendar day, zero-impression days included — so count rows with impressions > 0, not rows, for active days. Optional |
searchType Search Type | string | Which Google surface to report on. web (default) is normal Google Search; discover is the Discover feed (no query dimension); googleNews is news.google.com, news is the News tab of Google Search. Sent to the API as type. Optional |
aggregationType Aggregation Type | string | How Google aggregates the metrics. auto (default) lets Google choose — by page when grouping by page, by property otherwise. byPage aggregates by URI, byProperty across the whole property, byNewsShowcasePanel is for News Showcase. byProperty is rejected whenever a page dimension or page filter is present: 400 "'BY_PROPERTY' is not a valid aggregation type in the context of the request." The property-level average position is the byProperty number and does NOT equal the average of the byPage rows. Optional |
rowLimit Max Rows | integer | Rows to return in this call. Defaults to 50; API maximum 25000, but keep it at 200 or below — 100 rows is ≈13k characters and 400 rows exceeds the output cap and is spilled to a file you cannot read. Size it to the task: 5-10 for a "top query" answer, 200 for a sweep, then page with startRow: next_start_row while has_more is true (there is no page token). The last page is the one returning fewer rows than rowLimit; an empty final page is normal. Optional |
startRow Start Row | integer | Zero-based index of the first row to return. Omit for the first page, then pass the next_start_row value from the previous response to page forward. Optional |
subdomainFilter Filter Value | string | The value to filter on, for ANY dimension — not just subdomains (Query Search Analytics keeps the legacy key subdomainFilter). Combined with filterDimension and filterOperator into one filter, e.g. filterDimension: page, filterOperator: contains, value https://www.example.com/blog/. page expressions match the FULL URL, scheme and host included, not a path. Setting this makes advancedDimensionFilters ignored. Optional |
filterDimension Filter Dimension | string | Dimension the single-filter shortcut applies to; default page. Filtering does not require grouping by the same dimension — you can filter by page while grouping by query. page expressions match the FULL URL, scheme and host included, not a path. Optional |
filterOperator Filter Operator | string | How the filter value is compared. String comparison is case-insensitive. includingRegex/excludingRegex use RE2 syntax (no lookahead/lookbehind). Default contains. Optional |
advancedDimensionFilters Advanced Dimension Filters | string | JSON for multi-condition filtering, used only when the single-filter shortcut is empty. Accepts either a bare array of filters, which is ANDed into one group — e.g. [{"dimension":"country","operator":"equals","expression":"usa"},{"dimension":"device","operator":"equals","expression":"MOBILE"}] — or the raw API dimensionFilterGroups array, e.g. [{"groupType":"and","filters":[{"dimension":"page","operator":"contains","expression":"/blog/"}]}]. The API only supports groupType: "and"; there is no OR. Regex operators use RE2. Optional |
dataState Data State | string | Which data to include. final (default) returns only finalized data, which lags roughly 2-3 days. all also includes the most recent, not-yet-final days and adds metadata.firstIncompleteDate — use it when the user explicitly wants recent or partial numbers. hourly_all is required by the hour dimension. Optional |
REFERENCE
Tool details
Behavior hints are published with the component in the Pipedream registry and surface as MCP tool annotations, so an agent can reason about a tool before it calls it.
- Registry key
- google_search_console-retrieve-site-performance-data
- Version
- 1.1.0
- App
- Google Search Console
- Authentication
- OAuth
- Read-only
- Yes
- Destructive
- No
- Open world
- Yes
- Source
- View on GitHub ↗