Google Search Console
Query your own live Google Search Console data (queries, pages, countries, devices, sitemaps, URL inspection) through the RankParse API.
/v1/gsc/search-analyticsFreeAPI key + connected Google accountThe /v1/gsc/* routes proxy the Google Search Console API for properties you have connected to RankParse. Every call is a live request to Google for one property, so the data is Google-only and exactly as fresh as Search Console itself. Google only returns finalized data, so the most recent day or two are typically missing.
Three separate data products, three separate endpoint families. Do not mix them up:
- This page: live, Google-only Search Console data (queries, pages, countries, devices, sitemaps, URL inspection).
- Rank tracking: stored, scheduled snapshots of Google, Bing and Yandex keyword rankings side by side.
- Google Ads keyword research: search volume and competition for keywords. It never returns rankings.
Before you call
Connect Google Search Console
In the dashboard open Integrations, choose Google Search Console and click Connect. Sign in with a Google account that has access to the properties you want to query. You can connect more than one Google account; each property is read through the account that can see it.
List your properties
Call GET /v1/gsc/properties and copy a siteUrl exactly as it is returned. The site parameter on every other route must match it character for character (https://example.com/ with the trailing slash for a URL-prefix property, or sc-domain:example.com for a domain property).
Query it
Call GET /v1/gsc/search-analytics with site, startDate and endDate.
Authentication and cost
- Send your API key in the
X-API-Keyheader, as on every other endpoint. See Authentication. - The Google connection belongs to your workspace, not to the key. Any key in the workspace uses it.
- All
/v1/gsc/*routes cost 0 credits. They are limited only by the shared rate limit of 60 requests per minute per key (see Rate limiting) and by Google's own quotas. - The query routes (
search-analytics,sitemaps,url-inspection) require a team role (owner, admin or member). A client-role member gets403 capability_missing. - Responses are returned as plain objects. They are not wrapped in the usual
data/credits_usedenvelope.
GET /v1/gsc/status
/v1/gsc/statusFreeAPI keyReports whether a Google account is connected. It makes no call to Google and is not rate limited.
{
"connected": true,
"google_email": "you@example.com",
"connected_at": 1756112400000,
"connections": [
{ "id": "conn_abc123", "label": "you@example.com", "status": "active", "property_count": 4 }
]
}| Field | Type | Description |
|---|---|---|
connected | boolean | true when at least one Google account is connected |
google_email | string | Account email. Omitted when not connected |
connected_at | integer | Unix time in milliseconds. Omitted when not connected |
connections | array | Every non-revoked Google connection of the workspace |
connections[].status | string | active, reauth_required (reconnect it in the dashboard) or revoked |
connections[].property_count | integer | Properties that connection can see |
GET /v1/gsc/properties
/v1/gsc/propertiesFreeAPI keyLists the Search Console properties visible to your connected Google account or accounts. When several accounts are connected, a property visible to more than one is listed once, at the highest permission level.
curl "https://api.rankparse.com/v1/gsc/properties" \
-H "X-API-Key: rp_your_key"import requests
res = requests.get(
"https://api.rankparse.com/v1/gsc/properties",
headers={"X-API-Key": "rp_your_key"},
)
print(res.json()["properties"])const res = await fetch("https://api.rankparse.com/v1/gsc/properties", {
headers: { "X-API-Key": "rp_your_key" },
});
const { properties } = await res.json();{
"properties": [
{ "siteUrl": "sc-domain:example.com", "permissionLevel": "siteOwner" },
{ "siteUrl": "https://example.com/", "permissionLevel": "siteFullUser" }
]
}permissionLevel is Google's value for your access to the property (for example siteOwner, siteFullUser, siteRestrictedUser, siteUnverifiedUser).
GET /v1/gsc/search-analytics
/v1/gsc/search-analyticsFreeAPI key + connected Google accountRuns a Search Console performance query: clicks, impressions, click-through rate and average position, grouped by the dimensions you choose.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
site | string | Yes | Property URL exactly as returned by /v1/gsc/properties | |
startDate | string | Yes | First day, YYYY-MM-DD | |
endDate | string | Yes | Last day, YYYY-MM-DD | |
dimensions | string | No | query | Comma-separated list. Allowed: query, page, country, device, date, searchAppearance. Unknown names are dropped; if none are valid the call fails with invalid_dimensions |
searchType | string | No | web | One of web, image, video, news, googleNews, discover |
filterPage | string | No | Keep rows whose page URL equals this exactly | |
filterPageContains | string | No | Keep rows whose page URL contains this text (for segments such as /blog/). Cannot be combined with filterPage | |
filterQuery | string | No | Keep rows whose query equals this exactly | |
minPosition | number | No | Keep rows with average position at or above this | |
maxPosition | number | No | Keep rows with average position at or below this | |
maxCtr | number | No | Keep rows with CTR at or below this, as a fraction from 0 to 1 (0.03 means 3%) | |
minImpressions | number | No | Keep rows with at least this many impressions | |
rowLimit | integer | No | 100 | Rows to return. Capped at 1000 |
Notes on the parameters:
- RankParse checks only that dates look like
YYYY-MM-DD. Google applies its own range rules (for example its limited history window), and a request Google rejects comes back as502 gsc_api_errorcarrying Google's message. filterPageandfilterQuerycan be used together. Both must match.- The order of
keysin each row follows the order ofdimensionsthat were accepted. minPosition,maxPosition,maxCtrandminImpressionsare applied by RankParse after Google responds. When any of them is set, RankParse fetches Google's top 1000 rows (Google orders by clicks, descending), filters them, then trims torowLimit. A filter can therefore only find rows inside that top 1000. For a large property, narrow the date range or addfilterPageContainsfirst.
Example
Queries on positions 8 to 20 over the last month, the "quick win" zone:
curl -G "https://api.rankparse.com/v1/gsc/search-analytics" \
-H "X-API-Key: rp_your_key" \
--data-urlencode "site=https://example.com/" \
--data-urlencode "startDate=2026-09-01" \
--data-urlencode "endDate=2026-09-30" \
--data-urlencode "dimensions=query" \
--data-urlencode "minPosition=8" \
--data-urlencode "maxPosition=20" \
--data-urlencode "rowLimit=50"import requests
res = requests.get(
"https://api.rankparse.com/v1/gsc/search-analytics",
headers={"X-API-Key": "rp_your_key"},
params={
"site": "https://example.com/",
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"dimensions": "query",
"minPosition": 8,
"maxPosition": 20,
"rowLimit": 50,
},
)
for row in res.json()["rows"]:
print(row["keys"][0], row["position"], row["impressions"])const params = new URLSearchParams({
site: "https://example.com/",
startDate: "2026-09-01",
endDate: "2026-09-30",
dimensions: "query",
minPosition: "8",
maxPosition: "20",
rowLimit: "50",
});
const res = await fetch(`https://api.rankparse.com/v1/gsc/search-analytics?${params}`, {
headers: { "X-API-Key": "rp_your_key" },
});
const { rows } = await res.json();Response
{
"site": "https://example.com/",
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"dimensions": ["query"],
"searchType": "web",
"rows": [
{
"keys": ["buy widgets online"],
"clicks": 14,
"impressions": 1320,
"ctr": 0.0106,
"position": 9.4
}
],
"responseAggregationType": "byProperty"
}Response fields
| Field | Type | Description |
|---|---|---|
site, startDate, endDate | string | Echo of the request |
dimensions | string[] | The dimensions that were accepted, in order |
searchType | string | The search type used |
filterPage, filterPageContains, filterQuery | string | Echoed back only when you sent them |
rows | array | Result rows, at most rowLimit |
rows[].keys | string[] | One value per dimension, in dimensions order (for date, a YYYY-MM-DD string) |
rows[].clicks | number | Clicks from Google Search |
rows[].impressions | number | Impressions in Google Search |
rows[].ctr | number | Click-through rate as a fraction from 0 to 1 |
rows[].position | number | Average position for the row. A provider average, not a live SERP rank |
responseAggregationType | string | How Google aggregated the rows (byProperty, byPage, auto). Omitted if Google does not send it |
An empty result is a 200 with "rows": [].
Common recipes
| Goal | Request |
|---|---|
| Top queries | dimensions=query |
| Top pages | dimensions=page |
| Queries for one page | dimensions=query&filterPage=https://example.com/blog/post |
| 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 |
| Low CTR, high impressions | dimensions=page&minImpressions=500&maxCtr=0.05 |
| One section of the site | dimensions=page&filterPageContains=/blog/ |
| Countries or devices | dimensions=country or dimensions=device |
| Rich result and search features | dimensions=searchAppearance |
GET /v1/gsc/sitemaps
/v1/gsc/sitemapsFreeAPI key + connected Google accountLists the sitemaps submitted for a property, with Google's processing status.
| Parameter | Type | Required | Description |
|---|---|---|---|
site | string | Yes | Property URL exactly as returned by /v1/gsc/properties |
curl -G "https://api.rankparse.com/v1/gsc/sitemaps" \
-H "X-API-Key: rp_your_key" \
--data-urlencode "site=https://example.com/"{
"site": "https://example.com/",
"sitemaps": [
{
"path": "https://example.com/sitemap.xml",
"lastSubmitted": "2026-08-01T10:12:44.000Z",
"isPending": false,
"isSitemapsIndex": false,
"type": "sitemap",
"lastDownloaded": "2026-09-28T04:30:12.000Z",
"warnings": 0,
"errors": 0,
"contents": [{ "type": "web", "submitted": 412, "indexed": 398 }]
}
]
}Each sitemap object is Google's own record. path is always present; the other fields (lastSubmitted, isPending, isSitemapsIndex, type, lastDownloaded, warnings, errors, contents) appear when Google reports them. A property with no submitted sitemaps returns "sitemaps": [].
GET /v1/gsc/url-inspection
/v1/gsc/url-inspectionFreeAPI key + connected Google accountReturns Google's index status for one URL, using the Search Console URL Inspection API.
| Parameter | Type | Required | Description |
|---|---|---|---|
site | string | Yes | Property URL exactly as returned by /v1/gsc/properties. The inspected URL must belong to it |
url | string | Yes | Full URL to inspect, for example https://example.com/blog/post |
curl -G "https://api.rankparse.com/v1/gsc/url-inspection" \
-H "X-API-Key: rp_your_key" \
--data-urlencode "site=https://example.com/" \
--data-urlencode "url=https://example.com/blog/post"import requests
res = requests.get(
"https://api.rankparse.com/v1/gsc/url-inspection",
headers={"X-API-Key": "rp_your_key"},
params={"site": "https://example.com/", "url": "https://example.com/blog/post"},
)
status = res.json()["inspectionResult"]["indexStatusResult"]
print(status["verdict"], status["coverageState"])const params = new URLSearchParams({
site: "https://example.com/",
url: "https://example.com/blog/post",
});
const res = await fetch(`https://api.rankparse.com/v1/gsc/url-inspection?${params}`, {
headers: { "X-API-Key": "rp_your_key" },
});
const { inspectionResult } = await res.json();{
"site": "https://example.com/",
"url": "https://example.com/blog/post",
"inspectionResult": {
"inspectionResultLink": "https://search.google.com/search-console/inspect?resource_id=https://example.com/&id=abc",
"indexStatusResult": {
"verdict": "PASS",
"coverageState": "Submitted and indexed",
"robotsTxtState": "ALLOWED",
"indexingState": "INDEXING_ALLOWED",
"lastCrawlTime": "2026-09-27T06:14:00Z",
"pageFetchState": "SUCCESSFUL",
"googleCanonical": "https://example.com/blog/post",
"userCanonical": "https://example.com/blog/post",
"sitemap": ["https://example.com/sitemap.xml"],
"crawledAs": "MOBILE"
},
"mobileUsabilityResult": { "verdict": "PASS" }
}
}The inspectionResult object is Google's. indexStatusResult carries verdict, coverageState, robotsTxtState, indexingState, pageFetchState and, when known, lastCrawlTime, googleCanonical, userCanonical, sitemap, referringUrls and crawledAs. ampResult, mobileUsabilityResult and richResultsResult are present when Google evaluates them for the page. Google applies its own URL Inspection quota per property; exceeding it returns 429 gsc_quota_exceeded.
Errors
All errors use the standard shape { "error", "code", "message" }. See Error codes.
| Status | Code | Meaning |
|---|---|---|
400 | gsc_not_connected | No Google Search Console account is connected. Connect it under Dashboard, Integrations |
400 | missing_site | site is missing (search-analytics, sitemaps) |
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 |
400 | invalid_param | minPosition, maxPosition, maxCtr or minImpressions is not a number |
400 | missing_params | site or url is missing (url-inspection) |
401 | invalid_api_key | Missing, invalid or revoked API key |
401 | gsc_token_expired | The Google connection expired or was revoked. Reconnect under Dashboard, Integrations |
403 | capability_missing | Your workspace role cannot run live provider queries |
403 | gsc_access_denied | Google denied access to this property. Check the exact site value and that the property is verified for the connected account |
404 | gsc_property_not_found | The property is not visible to any connected Google account, or Google returned 404 for it |
429 | rate_limited | More than 60 requests per minute for this key |
429 | gsc_quota_exceeded | Google's Search Console API quota was exceeded. Retry later |
502 | gsc_token_refresh_failed | RankParse could not refresh the Google access token. Retry shortly |
502 | gsc_api_error | Any other upstream failure, including requests Google rejects. The message carries the upstream detail |
503 | gsc_not_configured | The Search Console integration is not configured on the server |
MCP tools
These tools wrap the routes above. They are available on the API-key MCP endpoint (mcp.rankparse.com) only. The secure OAuth connector (connect.rankparse.com) does not include any Search Console tool. All are read-only and cost 0 credits. See MCP Server.
| Tool | Purpose |
|---|---|
get_gsc_properties | List connected Search Console properties |
get_gsc_top_queries | Top queries by clicks |
get_gsc_top_pages | Top pages by clicks |
get_gsc_page_queries | Queries that drive traffic to one page |
get_gsc_query_pages | Pages that rank for one query |
get_gsc_date_trends | Daily clicks, impressions, CTR and position for a range |
get_gsc_country_breakdown | Traffic by country |
get_gsc_device_breakdown | Traffic by device type |
get_gsc_sitemaps | Submitted sitemaps and their status |
get_gsc_url_inspection | Index status of one URL |
get_gsc_query_page_pairs | Query and page combinations, for spotting cannibalization |
get_gsc_page_trend | Daily trend for one page |
get_gsc_query_trend | Daily trend for one query |
get_gsc_opportunity_queries | Queries at positions 8 to 20 (defaults), the quick-win zone |
get_gsc_low_ctr_queries | High-impression queries with low CTR (defaults: at least 100 impressions, CTR at most 0.05) |
get_gsc_low_ctr_pages | High-impression pages with low CTR (defaults: at least 500 impressions, CTR at most 0.05) |
get_gsc_page_segment | Top pages inside a URL segment such as /blog/ |
get_gsc_search_appearance | Traffic split by search appearance (rich results, video, and so on) |