Back to blog
guides13 min read

The Google Search Console API: Six Endpoints, One That Matters, and the Auth Step Everyone Misses

The Search Console API in full: OAuth versus service accounts, the searchAnalytics query that does the real work, the 25,000-row cap, and why there is no indexing endpoint.

Aditi ChaturvediSeptember 2, 2026
TL;DR

The Search Console API is small: six endpoints, and one of them is the reason anyone uses it. It serves the same 16 months as the interface, with the same anonymised queries missing, at 25 times the rows. What to know before you start:

  • The endpoint that matters: searchAnalytics.query. Clicks, impressions, CTR and position by query, page, country, device, date or search appearance, up to 25,000 rows per request and paged with startRow.
  • The auth step people miss: A service account is not enough on its own. You must also add its email address as a user on the property inside Search Console, manually, once per property.
  • There is no indexing endpoint: The Indexing API only accepts JobPosting and BroadcastEvent pages. For index status on a normal URL you want urlInspection.index.inspect, at 2,000 calls per day per property.
  • Two flags change your numbers: dataState decides whether fresh incomplete days are included, and aggregationType decides whether totals are counted per property or per page. Both explain most API-versus-UI mismatches.
  • It is not a warehouse: The API reads the same rolling 16 months and backfills nothing. If you want history, the BigQuery bulk export is the answer and the API is the wrong tool.

Start with sites.list. It is one call, it proves your credentials work, and it prints the exact siteUrl strings every other endpoint demands.

CrawlRaven reads this API on your behalf, so treat the last section as interested. The rest is the documentation I wanted when I first wired it up, including the auth failure that costs everyone an afternoon. Try CrawlRaven free: 1 site, no credit card →

The Search Console API has a reputation for being fiddly, and it is not. It has six endpoints, one of which does the real work, and a single authentication step that fails silently enough to cost people a whole afternoon.

This covers the surface, the auth, the first two calls, and the two flags that explain almost every "my numbers do not match the interface" question.

What the API is, and what it is not

It is the same Search Console data, read programmatically. That sentence sets both the appeal and the ceiling.

  • Same window. A rolling 16 months, with the oldest day dropping off every morning. The API cannot reach further back than the interface can.
  • Same privacy line. Anonymised queries have no row over the API either. More rows of the visible long tail is not the same as more queries.
  • Twenty-five times the rows. 25,000 per request against the interface's 1,000, paged. This is the actual reason to use it.
  • Schedulable. The interface needs a human. A cron job does not, which is what turns a report into a pipeline.

What it is not is a warehouse. It reads; it never stores. Any history beyond the window is something you built.

Every endpoint, in one table

The whole API fits on one screen, which is unusual for a Google product and makes it much less intimidating than the documentation implies.

The whole surface, and it is small

Every Search Console API endpoint

EndpointVerbWhat it returnsWhat to know
searchAnalytics.queryPOSTClicks, impressions, CTR and position by query, page, country, device, date or search appearanceThe reason to use the API at all. Up to 25,000 rows per request
sites.listGETEvery property this credential can read, with its permission levelYour first call. Confirms auth works and prints exact siteUrl strings
sites.get / add / deleteGET / PUT / DELETERead, create or remove a propertyAdding a property does not verify it. Verification is still manual
sitemaps.list / getGETSubmitted sitemaps with their status, warnings, errors and URL countsThe discovered-versus-submitted gap, without opening the interface
sitemaps.submit / deletePUT / DELETESubmit or remove a sitemap URLWorth wiring into a deploy. Resubmitting is cheap and idempotent
urlInspection.index.inspectPOSTIndex status, canonical, crawl date and rich-result verdicts for one URLSeparate host and quota: 2,000 per day and 600 per minute, per property

Note the split hosts. Search analytics, sites and sitemaps live under the Webmasters v3 base; URL inspection is on its own host with its own quota, and is the newest part of the surface.

Authentication: the step that trips people up

Everything is OAuth 2.0. What differs is who consents, and that choice determines how your integration dies later.

Pick before you write any code

Three ways to authenticate

OAuth, user consent
Best forA script you run, or a product a user signs in to
SetupCloud project, OAuth client, consent screen, then a refresh token
GotchaThe token is tied to a person. When they leave, it dies
Service account
Best forScheduled jobs and anything unattended
SetupCreate the account, then add its email as a user on the property
GotchaThat second step is manual and per property. It is the step people miss
A tool that holds the auth
Best forNot wanting to own a token lifecycle at all
SetupSign in once over OAuth
GotchaYou inherit the vendor's refresh cadence and their schema

Two scopes exist, and you should ask for the smaller one:

  • https://www.googleapis.com/auth/webmasters grants read and write. You need it to submit sitemaps or manage properties.
  • https://www.googleapis.com/auth/webmasters.readonly grants read only, and is what a reporting integration should request. People read consent screens.

The service account gotcha

This is the afternoon. Creating a service account in Google Cloud and enabling the API grants it exactly nothing in Search Console, and the failure looks like a permissions bug rather than a missing step.

  1. Create the service account in your Google Cloud project.
  2. Copy its email address, which ends in .iam.gserviceaccount.com.
  3. Open Search Console, pick the property, then Settings → Users and permissions.
  4. Add that email as a user, exactly as you would add a colleague. Full for write access, restricted for read.
  5. Repeat for every property. There is no bulk step.

Until step four, every call returns a 403 that reads like an auth problem, because technically it is one: the credential is valid and has no access to anything. Who can perform step four is covered in users and permissions, and the answer is owners only.

The quickstart: your first two calls

Make sites.list your first request every time. It is cheap, it proves the credential works, and it prints the exact siteUrl strings every other endpoint insists on.

GET https://www.googleapis.com/webmasters/v3/sites

The response names your properties in the exact string format every other endpoint demands. Two shapes:

  • sc-domain:example.com for a domain property.
  • https://example.com/ for a URL-prefix property, trailing slash included.

Copy them verbatim and URL-encode them wherever they sit in a path. Then the call you came for:

POST https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Aexample.com/searchAnalytics/query

{
  "startDate": "2026-08-01",
  "endDate": "2026-08-28",
  "dimensions": ["query", "page"],
  "rowLimit": 25000,
  "startRow": 0,
  "dataState": "final"
}

That is the whole quickstart. Everything else is choosing dimensions, filtering, and paging.

searchAnalytics.query in detail

One endpoint, and most of the API's value. The body is small and each field changes the shape of what comes back.

  • dimensions: any of query, page, country, device, date, searchAppearance. Combine them and each row is a unique combination, which multiplies row counts fast.
  • type: which surface you are asking about. Web, image, video, news and discover are separate datasets, not filters on one.
  • dimensionFilterGroups: the API equivalent of interface filters, including regex. The free GSC regex generator writes patterns that work in both.
  • rowLimit and startRow: paging. Request 25,000, then increment startRow by 25,000 until a response returns fewer rows than you asked for.

There is no total-count field, so the short response is the end-of-results signal. A loop that waits for an empty page instead will make one wasted request every time, which is harmless but tells you the author never read the response shape.

Why your API numbers do not match the interface

This is the most common support question about the API and it is almost never a bug. Two flags explain nearly all of it.

  • dataState. It defaults to final, which excludes the most recent days while they are still settling. Set it to all to include fresh, incomplete data, and expect those last days to keep moving.
  • aggregationType. Whether totals are counted by property or by page. Adding the page dimension changes the aggregation, and therefore changes the totals, which surprises people who expected filtering to be lossless.
  • Anonymised queries. The moment you add the query dimension, hidden queries drop out. Your query rows will always sum to less than your unfiltered totals.

Rule of thumb: compare like with like. Rows to rows, totals to totals, one dataState throughout. A mismatch between an API pull and a screenshot usually means the two used different settings, not that one of them is wrong. The same discipline applies when you cross into GA4, where the gap is structural rather than configurable.

Index status, and the Indexing API misconception

People come looking for an endpoint that gets pages indexed. It exists, it is a different product, and it almost certainly does not apply to you.

  • The Indexing API is narrow. Google accepts it only for pages carrying JobPosting or BroadcastEvent structured data. Calling it for ordinary content does nothing, whatever a plugin claims.
  • URL Inspection is what you want. urlInspection.index.inspect returns Google's index verdict for a URL, the canonical it chose, the last crawl date and rich-result verdicts.
  • Its quota is real. 2,000 calls per day and 600 per minute, per property. That is generous for spot checks and small for a nightly sweep of a large site.

For bulk indexability across a site, inspection is the wrong shape entirely: you want a crawl, and the free page indexability checker covers the common case. What the statuses mean once you have them is in the page indexing report guide.

Sitemaps: the endpoint worth wiring into a deploy

The sitemaps endpoints get overlooked because submitting one by hand takes ten seconds. Doing it automatically is still worth the ten minutes.

  • sitemaps.submit takes the property and the full sitemap URL. It is idempotent, so resubmitting on every deploy is safe and cheap.
  • sitemaps.list returns status, warnings, errors and counts for everything submitted. The submitted-versus-discovered gap is a finding on its own.
  • Alert on the gap, not the submission. A sitemap that submits cleanly and discovers a third of its URLs is the failure worth catching in CI.

Validate the file before you submit it rather than after Google complains: the free sitemap validator checks it against the protocol, and the sitemap checker live-checks the URLs inside it.

Limits, quotas and what the API will not give you

The honest list, so you find out here rather than three days into a build.

  • No link data. The Links report has no endpoint. If you want the links report programmatically, you cannot have it.
  • No Core Web Vitals. Not exposed. The CrUX API is a separate product with a different dataset.
  • No manual actions or security issues. Both are interface and email only, which matters if you hoped to monitor a client roster programmatically.
  • No enhancement reports. Structured-data validity is not available through this API.
  • Quota you will not hit normally. Per-project and per-user limits are generous for reporting and easy to exhaust with a retry loop that has no backoff. Handle 429 properly from the start.
Opinion· Aditi's take

The gap between "I pulled search analytics" and "I have a pipeline" is much wider than it looks on day one. The first script takes an afternoon. Paging, retries, token refresh, schema drift, a property someone renamed, and the daily job that fails silently for three weeks are the actual project.

Build it when the pull is genuinely specific to how your team works. Do not build it to avoid paying for a tool, because the maintenance is the price and it arrives monthly rather than once.

API, interface or bulk export

Three ways to read the same data, and the choice is about depth against setup rather than about quality.

Same data, three depths

Interface, API and bulk export

InterfaceAPIBigQuery export
Rows per pull1,00025,000, pagedAll of them
History16 months16 monthsFrom setup day, forever
Anonymised queriesHiddenHiddenOne aggregate row per day
Setup costNoneAn afternoonA Cloud project
Running costFreeFreeGoogle Cloud billing
Needs codeNoYesSQL

Most teams that get this right run two of the three: the BigQuery bulk export as the archive, because it is the only route that beats the 16-month window, and the API for the specific questions they ask weekly. The interface stays for the reports the API does not expose at all.

If the goal is a language model reading your Search Console data rather than a dashboard, the API is one layer below where you want to be: the Search Console MCP routes cover what sits on top of it.

Key Takeaways

  • Six endpoints, one that matters: searchAnalytics.query does the work. sites, sitemaps and urlInspection round it out, and the whole surface fits on one screen.
  • Start with sites.list: One call that proves your credentials and prints the exact siteUrl strings, including the sc-domain: prefix, that every other endpoint demands.
  • The service account needs a second step: Creating it in Google Cloud grants nothing. Add its email as a user on the property in Search Console, manually, once per property, or every call returns 403.
  • 25,000 rows, paged with startRow: Against 1,000 in the interface. There is no total count, so a response with fewer rows than you requested is your end-of-results signal.
  • Two flags explain the mismatches: dataState decides whether fresh incomplete days are in. aggregationType decides property versus page counting. Check both before reporting a bug.
  • There is no indexing endpoint for normal pages: The Indexing API takes JobPosting and BroadcastEvent only. For index status use urlInspection.index.inspect, capped at 2,000 per day per property.
  • It reads, it never stores: Same rolling 16 months, same anonymised queries withheld, no backfill. For history you want the BigQuery bulk export, and it starts from the day you switch it on.

Primary sources

Frequently asked questions

What is the Google Search Console API?

It is the programmatic interface to your Search Console property. Six endpoints cover search analytics, property listing and management, sitemap submission and status, and URL inspection. It serves the same data as the interface, from the same rolling 16-month window, with the same anonymised queries withheld, but returns far more rows per request and can be scheduled.

How do I authenticate with the Search Console API?

OAuth 2.0, in one of two shapes. User consent, where a person signs in and you store a refresh token, suits scripts and products. A service account suits unattended jobs. Either way you create a Google Cloud project, enable the Search Console API, and request the scope https://www.googleapis.com/auth/webmasters, or the readonly variant if you never write.

Why does my service account get a 403 from the Search Console API?

Almost always because you skipped the second step. Creating a service account in Google Cloud grants it nothing in Search Console. You must open the property, go to Settings then Users and permissions, and add the service account's email address as a user, exactly as you would add a colleague. This is manual and needed once per property.

What is the row limit on the Search Console API?

25,000 rows per request for searchAnalytics.query, against 1,000 in the interface export. Page through larger result sets with startRow, incrementing by your rowLimit until a response comes back with fewer rows than you asked for. That is the signal you have reached the end; there is no total count to check against.

Is there a Google Search Console API for indexing pages?

Not for general web pages. Google's Indexing API only accepts pages with JobPosting or BroadcastEvent structured data, and using it for ordinary content does nothing. To read whether a URL is indexed, use urlInspection.index.inspect, which returns Google's index verdict, the canonical it chose and the last crawl date, at 2,000 calls per day per property.

Why do my API numbers differ from the Search Console interface?

Usually one of two flags. dataState defaults to final, which excludes the most recent incomplete days that the interface may show; setting it to all includes them. And aggregationType changes whether totals are counted by property or by page, which shifts every figure once the page dimension is involved. Check both before assuming a bug.

Does the Search Console API show anonymised queries?

No. The API applies the same privacy line as the interface: queries typed by very few people have no row over the API either, and query filters exclude them the same way. You get more rows of the visible long tail, not access to the hidden part. The BigQuery bulk export is the only place their aggregate size appears.

Can the Search Console API give me more than 16 months of data?

No. The API reads the same rolling 16-month window as the interface and backfills nothing, so it cannot recover history you did not capture. If you want a longer record, you either pull on a schedule and store the results yourself from today onward, or set up the BigQuery bulk export, which has the same start-from-today constraint.

What scopes does the Search Console API need?

Two exist. https://www.googleapis.com/auth/webmasters grants read and write, which you need for submitting sitemaps or managing properties. https://www.googleapis.com/auth/webmasters.readonly grants read only and is what almost every reporting integration should request. Ask for the narrower one unless you genuinely write, because users notice what a consent screen asks for.

Is the Search Console API free?

Yes. There is no charge for calls and no paid tier, in keeping with the rest of Search Console. What you get instead is quota: per-project and per-user limits that normal reporting will not reach, and that a retry loop without backoff will reach quickly. The URL Inspection API has its own published caps of 2,000 per day and 600 per minute per property.

Can I submit a sitemap through the API?

Yes, with sitemaps.submit, which takes the property and the full sitemap URL and needs the read-write scope. It is idempotent, so resubmitting an existing sitemap is harmless, which makes it a sensible last step in a deploy pipeline. sitemaps.list returns the status, warnings, errors and URL counts for everything already submitted.

Should I use the API or the BigQuery bulk export?

The API when you want specific slices on demand and can live inside 16 months. The bulk export when you want everything, including the long tail the API truncates and an aggregate row for anonymised queries, and when you are building history. Many teams run both: the export for the archive, the API for the queries they ask weekly.

Aditi Chaturvedi
About the Author

Aditi Chaturvedi

15+ years of growing SaaS websites through SEO | Author, 200-Point Audit Checklist

Aditi has spent 15+ years helping SaaS companies scale organic traffic through technical SEO and content strategy. She is the author of the CrawlRaven 200-Point Audit checklist used by agencies and in-house teams to systematically improve search performance.

google search console apisearch analytics apisearch console api oauthgsc apiurl inspection apisearch console api quickstartsites.listgoogle search console api indexing

Reader is technical, has understood the API surface and its 16-month ceiling, and is deciding whether to build and maintain a pipeline or connect something that already did.

You can build this. The question is whether it earns the maintenance.

GSC + GA4 + a 200-point crawl, joined into one ranked plan. From $49 at launch, one time.

A search analytics pull is an afternoon. Keeping it running, paging it correctly, joining it to GA4, and crawling the site to explain what it shows is the actual project. CrawlRaven connects over OAuth, syncs daily, and returns the join as a ranked plan rather than a table. Free plan covers 1 site, no credit card.

CrawlRaven connects Google Search Console and GA4, runs 200+ technical SEO checks, and joins all three into one prioritized fix list, so you know what is broken, what it is costing you, and what to fix first.

✓ No credit card required·200+ checks·GSC + GA4 + full-site crawl
Free plan — no credit card

Stop exporting. Start shipping.

Connect Search Console, import your Ahrefs or Semrush lists, and get one ranked plan. Start free with one site, or grab a limited lifetime deal from $39, only 10 licenses left.

3
Data sources joined
200+
Point audit checks
1
Ranked plan out