Search Console API: four services, two hostnames, and the row limit that makes it worth using
The Search Console API is free and returns the same numbers as the interface, with one difference worth the setup: 25,000 rows per request instead of a thousand. It is four services on two hostnames, authenticated with OAuth rather than an API key, and its quotas are charged by query shape rather than by call count.

The Search Console API is free, and it returns the same numbers you already see in Search Console — with one difference that decides whether you need it: a single request can return 25,000 rows instead of the thousand you can read on screen, and the same call can be scheduled. It is four services, not one, and two of them live on different hostnames, which is the first thing that trips people up.
Before you start
You need a Search Console property you already own, and you need to be able to add an email address to it — either your own, or a service account's. The API does not give you access to anyone's data; it reads the properties your account has. If the property is not verified yet, reading the performance report as five shapes is the cheaper first step, because the API will only hand you the same report in a less friendly form.
You do not need to be a developer to finish this chapter, but you do need to run one command that prints JSON. Everything below is done in a browser and a terminal.
Why the Search Console API is four services on two hostnames
The reference index lists four services, and they were added at different times, which is why the URLs disagree with each other. Search Analytics, Sitemaps and Sites all sit under a path that still carries the product's older name, webmasters/v3. URL Inspection was added later and lives on its own host, searchconsole.googleapis.com, with a different version number.
| Service | What it answers | Endpoint |
|---|---|---|
| Search Analytics | Which searches, pages and countries produced impressions and clicks | POST www.googleapis.com/webmasters/v3/sites/siteUrl/searchAnalytics/query |
| URL Inspection | What Google's index currently holds for one URL | POST searchconsole.googleapis.com/v1/urlInspection/index:inspect |
| Sitemaps | Which sitemaps are submitted, and their status | www.googleapis.com/webmasters/v3/sites/siteUrl/sitemaps |
| Sites | Which properties the account can see | www.googleapis.com/webmasters/v3/sites |
Two details in that table cause most of the first-day errors. The property identifier inside the URL is either the exact URL prefix you verified, including the trailing slash, or the domain form sc-domain:example.com — and the two are not interchangeable. And the search analytics method is a POST even though it only reads: the filters and the date range go in a request body, not in a URL string you can paste into a browser.
The API is not a second Search Console. It is the same data with a different ceiling — and the ceiling is the only reason to learn it.
Do it in this order
Four steps, each with something you can check before moving on. The whole sequence takes about twenty minutes the first time and nothing after that.
- Turn the API on for a project. In the Google Cloud console, create a project, then enable the Search Console API on it. Done when the API appears as enabled in that project's list.
- Create credentials. For a one-off script, an OAuth client of the desktop type is the shortest path, because it can open a browser window and store a refresh token. For anything scheduled on a server, create a service account instead and download its JSON key. Done when you hold either a client secret or a key file on disk.
- Grant it access to the property. Open Search Console, go to the property's settings, and add the service account's email address as a user. Done when the address appears in the property's user list with full or restricted permission. Skipping this step produces a 403 that looks like a bug in your code.
- Make one call and read the shape of the answer. Ask for one week of data grouped by page, with no filters. Done when you get rows with
keys,clicks,impressions,ctrandposition.
pip install google-auth google-auth-oauthlib requests
python3 -c "
import json, requests
from google.oauth2.credentials import Credentials
creds = Credentials.from_authorized_user_file('token.json',
['https://www.googleapis.com/auth/webmasters.readonly'])
site = 'sc-domain:example.com'
body = {'startDate': '2026-09-07', 'endDate': '2026-09-13',
'dimensions': ['page'], 'rowLimit': 25000, 'dataState': 'final'}
url = 'https://www.googleapis.com/webmasters/v3/sites/' + site + '/searchAnalytics/query'
r = requests.post(url, headers={'Authorization': 'Bearer ' + creds.token}, json=body)
rows = r.json().get('rows', [])
print(len(rows), 'rows')
for row in rows[:5]:
print(round(row['clicks'], 1), row['keys'][0])
"
The deliverable: a routing table, and the two ceilings it has to live inside
Keep this table next to the one above. It is the part that saves time, because the three ways of pushing or reading data are constantly confused with each other, and each one has a different set of rules about who may use it.
| Job | What to use | Who may use it |
|---|---|---|
| Read impressions and clicks in bulk | Search Analytics | Any verified property |
| Check one URL's index status at scale | URL Inspection service | Any verified property |
| Ask Google to recrawl a page | The URL Inspection tool in the interface | Any verified property |
| Push a URL the moment it changes | IndexNow | Any site; Bing and Yandex read it |
| Push a job posting the moment it changes | Indexing API | Job postings and live streams only |
| List or resubmit sitemaps | Sitemaps service | Any verified property |
Note the two rows in the middle, because they are the ones people get wrong. The Indexing API is not a general-purpose push button; if you are not publishing job postings or live streams, it is documented as not applying to you, and the Indexing API chapter covers what to use instead. There is also no documented API method for the URL Inspection tool's live test — the service returns the status of the version already in the index.
The two ceilings are easier to work with than they look, because they fail loudly. Every quota failure returns the same message, so you cannot tell them apart from the error alone; you have to know which one you are likely to be hitting.
| Ceiling | Limit | Measured over |
|---|---|---|
| Search Analytics, per property | 1,200 | Calls per minute |
| Search Analytics, per user | 1,200 | Calls per minute |
| Search Analytics, per project | 30,000,000 | Calls per day |
| URL Inspection, per property | 2,000 | Calls per day |
| URL Inspection, per property | 600 | Calls per minute |
| All other resources, per user | 20 | Calls per second |
There is a second kind of limit underneath the call counts, and it is the one that actually bites: a request is charged by how much work it represents, not by how many times you send it. Grouping or filtering by page and by search term in the same request is documented as the most expensive shape, and a long date range costs more than a short one. A script that asks for two years of data grouped by page and search term will exhaust its budget long before it exhausts its call count.
What goes wrong, and how you would notice
Three failures cover almost everything people hit in the first week, and each one looks like a different problem than it is. All three are visible from the error text alone if you know which question to ask.
- A 401 that is really a wrong credential type. API keys identify a project; they do not identify a person, and this API reads private data. If you pasted a key into a header, you get a 401. The fix is an OAuth token from step 2, not a new key.
- A 403 that is really missing access. A service account is a separate identity with its own email address, and it starts with access to nothing. Adding it as a user of the property in Search Console is a separate action from creating it, and it is the one that gets skipped.
- A quota error that is really a request shape. If a single call inside a ten-minute window fails on quota, you are not hitting the per-minute limit — you are hitting the long-term one, and the fix is to remove the grouping rather than to slow down.
One boundary worth writing down rather than discovering: this chapter covers the four services the reference index lists. If a report is not one of those four, there is no documented API for it, and that includes the generative AI reporting, whose limits are a separate subject. The short version is in the AI Mode reporting chapter: the report answers a question about surfaces, not about individual searches.
We also could not find a documented retention window on the API pages we read, so this chapter does not state one. If you are building anything that must survive a change in that window, the safe pattern is the same as it is for any external data source: pull on a schedule and keep your own copy. Recording a baseline before you change anything is that pattern written out for a single page.
Common questions
Is the Search Console API free?
Yes. There is no price attached to it; the limits above are the only cost, and they are limits on volume rather than on money. You do need a Google Cloud project, but that is an administrative container, not a paid product.
Do I need an API key?
No, and an API key will not work here. An API key identifies a project for public data. Search Console data belongs to a person or an organisation, so every call carries an OAuth 2.0 token, and the token carries one of two scopes: read and write, or read only.
Can I use a service account instead of signing in every time?
Yes, and for anything that runs on a schedule you should. The service account gets its own email address, which you then add as a user of the property in Search Console. The credentials never expire the way a user token does, which is the whole point.
How many rows can one request return?
25,000, and the default is 1,000 if you do not ask. Rows beyond that come from a second request that starts where the first one stopped, using the offset parameter. Most sites never need the offset; a site with a large number of pages and search terms will.
Why do I get the same error message for every kind of quota failure?
Because Google returns one message for all of them. The way to tell them apart is the shape of the failure: a call that fails on its own inside a ten-minute window is a long-term budget problem, and a call that fails only when your script runs flat out is a rate problem.
Part of the QueryWin handbook · Level 2


