Query search analytics
Run a live Search Console performance query, with clicks, impressions, CTR and average position grouped by the dimensions you choose.
https://api.rankparse.com/v1/gsc/search-analyticsFreeAPI key + connected Google accountRuns a Search Console performance query for one property, live against Google: clicks, impressions, click-through rate and average position, grouped by query, page, country, device, date or search appearance. It covers any query Google reports, not only the ones you track. For stored, multi-engine snapshots of your tracked queries, use Rank tracking instead.
Query parameters
sitestringrequiredThe property as returned by List properties, for example https://example.com/ or sc-domain:example.com. A bare host such as example.com also works: see The site parameter. Missing returns 400 missing_site.
startDatestringrequiredFirst day, YYYY-MM-DD. Missing returns 400 missing_dates; any other format returns 400 invalid_date. The same applies to endDate.
endDatestringrequiredLast day, YYYY-MM-DD. RankParse checks only the shape of the dates. Google applies its own range rules (such as its history window), and a request Google rejects comes back as 502 gsc_api_error with Google's message.
dimensionsstringdefault queryComma-separated list of query, page, country, device, date and searchAppearance. Unknown names are dropped silently; if none is valid the call returns 400 invalid_dimensions. The order you give is the order of keys in each row.
searchTypestringdefault webweb, image, video, news, googleNews or discover. Anything else returns 400 invalid_search_type. Google does not offer every dimension for discover and googleNews (query is not available there).
filterPagestringKeep rows whose page URL equals this exactly.
filterPageContainsstringKeep rows whose page URL contains this text, for a segment such as /blog/. Sending it with filterPage returns 400 conflicting_filters.
filterQuerystringKeep rows whose query equals this exactly. Can be combined with either page filter; all must match.
filterQueryContainsstringKeep rows whose query contains this text. Sending it with filterQuery returns 400 conflicting_filters.
countrystringKeep traffic from one country: an ISO 3166-1 alpha-3 code such as USA or GBR. A two-letter code such as US is accepted for common countries. Anything else returns 400 invalid_country.
devicestringKeep traffic from one device type: desktop, mobile or tablet. Anything else returns 400 invalid_device.
filterQueryRegexstringKeep rows whose query matches this RE2 regular expression, 1 to 500 characters. For example ^(?:\S+\s+){7,}\S+$ keeps queries of 8 or more words. Sending it with minQueryWords returns 400 conflicting_filters; a length outside 1 to 500 returns 400 invalid_param; a pattern that is clearly not valid RE2 (unbalanced parentheses, an unclosed [, a quantifier with nothing to repeat, lookahead, lookbehind or a backreference) returns 400 invalid_filter_regex before Google is called.
minQueryWordsintegerKeep queries of at least this many words, from 2 to 50. The same filter as the regular expression above, built for you: 8 means 8 or more words. Anything else returns 400 invalid_param.
minPositionnumberKeep rows with an average position at or above this number. Not a number returns 400 invalid_param.
maxPositionnumberKeep rows with an average position at or below this number.
maxCtrnumberKeep rows with a CTR at or below this, as a fraction from 0 to 1 (0.03 means 3%).
minImpressionsnumberKeep rows with at least this many impressions. With compareTo, a row passes when either period meets it.
minClicksnumberKeep rows with at least this many clicks (same rule with compareTo).
startRowintegerdefault 0Offset into Google's rows (ordered by clicks), to read past the first page. Use the next_start_row from a response that says has_more. A negative or non-integer value returns 400 invalid_param, and so does any value above 0 together with compareTo.
compareTostringprevious_period also reads the period of the same length that ends the day before startDate and adds previous, delta and status to every row. Anything else returns 400 invalid_compare_to. It cannot be combined with startRow above 0 or with the date dimension (both return 400 invalid_param). See Comparing periods.
sortstringOrder the rows by clicks, impressions, ctr or position; with compareTo also by clicks_delta, impressions_delta, ctr_delta or position_delta. The default order is Google's (clicks, descending). Anything else returns 400 invalid_sort.
orderstringasc or desc. The default is largest first, except position, where the best (lowest) comes first. For the biggest losers use sort=clicks_delta&order=asc. Anything else returns 400 invalid_order.
fieldsstringComma-separated subset of keys, clicks, impressions, ctr, position, previous, delta and status. Each row then carries only those fields, which keeps a large response small. An unknown name returns 400 invalid_fields.
rowLimitintegerdefault 100Rows to return. Values above 1000 are lowered to 1000; a missing, zero or non-numeric value uses 100.
Response
sitestringThe property you queried.
startDatestringEcho of the request.
endDatestringEcho of the request.
dimensionsarray of stringsThe dimensions that were accepted, in order.
filterPagestringEchoed only when you sent it. The same goes for filterPageContains, filterQuery and minQueryWords.
filterQueryRegexstringEchoed when you sent filterQueryRegex, or minQueryWords, in which case it is the regular expression that was built from it.
searchTypestringThe search type used.
requested_sitestringPresent only when RankParse matched your site to a different property; site then holds the property it used.
countrystringEchoed only when you sent country, as the alpha-3 code that was used. The same goes for device.
row_countintegerRows in this response.
total_matchedintegerRows that passed every filter before rowLimit trimmed them, out of the rows read from Google.
has_morebooleantrue when Google returned a full page of rows, so more may exist. Filters, sorting and comparison work on that page only.
next_start_rowintegerPresent when has_more is true: pass it as startRow to read the next page.
previous_periodobjectWith compareTo: the startDate and endDate of the period compared against.
comparison_may_be_incompletebooleanWith compareTo: true when either period hit the 1000-row read, so a row marked new or lost may only be outside the rows read.
rowsarrayResult rows, at most rowLimit, in Google's order (by clicks, descending). Empty when Google has no data for the request.
Show propertiesHide properties
keysarray of stringsOne value per dimension, in dimensions order. For date, a YYYY-MM-DD string.
clicksnumberClicks from Google Search.
impressionsnumberImpressions in Google Search.
ctrnumberClick-through rate, a fraction from 0 to 1.
positionnumberAverage position for the row. A Google-reported average, not a live SERP rank.
previousobject | nullWith compareTo: that period's clicks, impressions, ctr and position, or null for a new row.
deltaobjectWith compareTo: the change, where positive is better. clicks and impressions are current minus previous, ctr likewise, and position is previous minus current, so a positive position means the row moved up. position is null when the row is in only one period.
statusstringWith compareTo: both, new (only in the current period) or lost (only in the previous one; its current values are 0).
responseAggregationTypestringHow Google aggregated the rows (byProperty, byPage or auto). Omitted when Google does not send it.
Behavior and limits
- Credits: 0. Rate limit 60 requests per minute per key, plus Google's own quota.
- Finalized data only: RankParse asks Google for final data, so the last day or two are usually missing.
- Position, CTR and impression filters run after Google answers. When any of
minPosition,maxPosition,maxCtrorminImpressionsis set, RankParse fetches Google's top 1000 rows, filters them, then trims torowLimit. A filter can only find rows inside that top 1000, so for a large property narrow the date range or addfilterPageContainsfirst. - Sorting, comparison and paging work on one page of Google's rows. Sorting by anything other than clicks, or setting
compareTo, also reads the top 1000 rows. UsestartRowto move that page, except withcompareTo, which always reads the first 1000 rows of each period. - Query regex filters run inside Google.
filterQueryRegexandminQueryWordsnarrow the rows before the top 1000 are taken, so they are the way to find long, specific searches on a large property. RE2 has no lookahead or backreferences. Google answers many malformed patterns with no rows instead of an error, and RankParse catches only the clearly invalid ones, so an empty result from a regular expression you wrote can mean a mistake in the pattern as well as no matches. - Multiple Google accounts: the property is read through the account that can see it. See The site parameter.
Comparing periods
compareTo=previous_period answers "what changed?" in one call. For startDate=2026-10-01&endDate=2026-10-28 it also reads 2026-09-03 to 2026-09-30, joins the two on the row keys, and adds a delta to each row. Rows that exist in only one period are kept and marked new or lost, so a page that dropped out entirely still shows.
GET /v1/gsc/search-analytics?site=example.com&dimensions=page&startDate=2026-10-01&endDate=2026-10-28
&country=US&device=mobile&compareTo=previous_period&sort=clicks_delta&order=asc&minClicks=20&rowLimit=10Common recipes
| Goal | Parameters |
|---|---|
| Top queries | dimensions=query |
| Top pages | dimensions=page |
| Queries for one page | dimensions=query&filterPage=https://example.com/blog/post |
| Biggest losers against the previous period | dimensions=page&compareTo=previous_period&sort=clicks_delta&order=asc |
| One segment, one market | filterPageContains=/free-tools/&country=US&device=mobile |
| Pages ranking for one query | dimensions=page&filterQuery=best seo tools |
| Daily trend | dimensions=date (use rowLimit=500 or more for long ranges) |
| Cannibalization check | dimensions=query,page |
| Quick wins (positions 8 to 20) | dimensions=query&minPosition=8&maxPosition=20 |
| Low CTR, high impressions | dimensions=page&minImpressions=500&maxCtr=0.05 |
| One section of the website | dimensions=page&filterPageContains=/blog/ |
| Long, specific searches (8 or more words) with few clicks | dimensions=query,page&minQueryWords=8&minImpressions=50&maxCtr=0.03 |
| Countries or devices | dimensions=country or dimensions=device |
| Rich results and search features | dimensions=searchAppearance |
Errors
Plus the shared Search Console errors (see the overview).
| HTTP | Code | When |
|---|---|---|
| 400 | missing_site | site is missing. |
| 400 | missing_dates | startDate or endDate is missing. |
| 400 | invalid_date | A date is not in YYYY-MM-DD shape. |
| 400 | invalid_dimensions | None of the requested dimensions is valid. |
| 400 | invalid_search_type | searchType is not one of the allowed values. |
| 400 | conflicting_filters | filterPage and filterPageContains were both sent, or filterQueryRegex and minQueryWords were both sent. |
| 400 | invalid_param | minPosition, maxPosition, maxCtr or minImpressions is not a number, filterQueryRegex is empty or over 500 characters, or minQueryWords is not a whole number from 2 to 50, or compareTo is combined with a startRow above 0 or with the date dimension. |
| 400 | invalid_filter_regex | filterQueryRegex is not valid RE2. RankParse refuses the clearly invalid patterns itself without calling Google, and also returns this if Google rejects one. |
| 400 | gsc_not_connected | No Google Search Console account is connected. |
| 404 | gsc_property_not_found | No connected Google account can see site. |
| 429 | gsc_quota_exceeded | Google's quota was exceeded. Retry later. |
| 502 | gsc_api_error | Google rejected the request or failed. The message carries the detail. |
Related
Every MCP tool below calls this route with fixed dimensions and filters, and each takes an optional searchType (default web). All need site, startDate and endDate.
| MCP tool | What it sends |
|---|---|
get_gsc_top_queries | dimensions=query, filterQueryRegex (optional), rowLimit (default 25) |
get_gsc_top_pages | dimensions=page, rowLimit (default 25) |
get_gsc_page_queries | dimensions=query, filterPage from page, rowLimit (default 25) |
get_gsc_query_pages | dimensions=page, filterQuery from query, rowLimit (default 25) |
get_gsc_date_trends | dimensions=date, rowLimit=500 |
get_gsc_country_breakdown | dimensions=country, rowLimit (default 25) |
get_gsc_device_breakdown | dimensions=device, rowLimit=10 |
get_gsc_query_page_pairs | dimensions=query,page, filterQueryRegex (optional), rowLimit (default 100) |
get_gsc_page_trend | dimensions=date, filterPage from page, rowLimit=500 |
get_gsc_query_trend | dimensions=date, filterQuery from query, rowLimit=500 |
get_gsc_opportunity_queries | dimensions=query, minPosition (default 8), maxPosition (default 20), rowLimit (default 50) |
get_gsc_long_queries | dimensions=query,page, minQueryWords from minWords (default 8), minImpressions (default 50), maxCtr (default 0.03), rowLimit (default 50). It reads at most 1000 query and page rows ordered by clicks before the impressions and CTR filters apply |
get_gsc_low_ctr_queries | dimensions=query, minImpressions (default 100), maxCtr (default 0.05), rowLimit (default 50) |
get_gsc_low_ctr_pages | dimensions=page, minImpressions (default 500), maxCtr (default 0.05), rowLimit (default 25) |
get_gsc_page_segment | dimensions=page, filterPageContains from urlPattern, rowLimit (default 25) |
get_gsc_search_appearance | dimensions=searchAppearance, rowLimit=50 |