The Search Console API: what it gives you and what it limits
The endpoints available, the real quota numbers — 1,200 queries per minute per site, 2,000 URL inspections per day, 25,000 rows per request, 16 months of history — how pagination and dataState work, and the reports that are deliberately not exposed.
The Search Console API exposes most of what the web interface shows, with higher row limits and no clicking. It is also the only supported way to get your search data into a warehouse before it ages out.
What you can call
- Search Analytics (
searchanalytics.query) — the performance report. Clicks, impressions, CTR and position, grouped by any combination ofquery,page,country,device,searchAppearanceanddate. - Sites — list, add and remove properties, and read your permission level on each.
- Sitemaps — list, submit and delete sitemaps, and read their processing status.
- URL Inspection — the index status of a single URL, mirroring the URL Inspection tool.
What is not exposed
Worth knowing before you plan around it:
- The Links report. No backlink endpoint exists. Any tool showing you backlinks is using its own crawler.
- Page indexing, Core Web Vitals, mobile usability, rich results, manual actions and security issues. All web-interface only.
- Requesting indexing. The separate Indexing API is restricted to
JobPostingandBroadcastEventstructured data. It is not a general "index this page" endpoint, whatever the blog posts say.
The limits that shape your code
- 25,000 rows per request. Set
rowLimitto 25000 and page withstartRowin increments of 25,000 until a response comes back short. - 16 months of history. Same as the interface. Anything older is gone, which is the whole argument for exporting on a schedule.
- Anonymised queries are still missing. The API applies the same privacy filtering, so query-dimension rows will not sum to the totals. See anonymised queries.
The two quotas, and which one actually stops you
Search Analytics is metered twice, and confusing the two is why people back off the wrong thing. Both return the same "quota exceeded" error, which does not help.
Rate quota, counted in requests
The published ceilings are generous:
- 1,200 queries per minute per site, and 1,200 per minute per user. These are combined figures — one user querying three properties shares a single 1,200 QPM allowance across all three.
- 40,000 queries per minute and 30 million per day per project, counted against your Cloud console key rather than the property.
- Sites, Sitemaps and the other resources get 20 QPS and 200 QPM per user — an order of magnitude tighter than Search Analytics, and easy to trip if you loop over a few hundred properties without pausing.
- URL Inspection is the tight one: 2,000 calls per day and 600 per minute, per property. That is the whole budget. Do not build a crawler on it.
Load quota, counted in how expensive your query is
This is the one most people hit, and it is not a request count at all. Every query consumes "load" in proportion to how much work it makes Google do, measured in 10-minute chunks (short-term) and 1-day chunks (long-term). Three things drive the cost:
- Grouping or filtering by
pageorqueryis expensive. Doing both at once is the most expensive request the API offers. - Date range multiplies it. Six months of data costs vastly more than one day, for the same dimensions.
- Re-querying the same data counts every time. Pulling last month again on every run is pure waste, and it is the usual reason a modest-looking job exhausts its quota.
The tell for which one you have hit: if a single query inside a 10-minute window still errors, that is long-term load quota, and the fix is a cheaper query shape or a shorter range. If it clears after 15 minutes, you were only over the short-term limit, and spreading the job across the day is enough. Either way, back off exponentially on HTTP 429 rather than retrying immediately.
Your actual consumption is visible in the quota tab of your project in the Google API Console. It is worth looking before you assume you need to redesign anything.
Two parameters worth understanding
dataState — final returns only fully processed days and runs about two days behind; all includes fresh partial days. Use final for anything you report on and all for monitoring. Detail in why Search Console data is two days behind.
aggregationType — byPage, byProperty or auto. This changes the numbers, because a single search showing two of your pages counts once at property level and twice at page level. Pick one and keep it consistent, or your week-over-week comparison will move for no reason.
Access. OAuth 2.0, or a service account added as a user on the property. Two scopes exist and there is nothing between them: webmasters.readonly reads, and webmasters reads and writes. Writing means four methods — submit a sitemap, delete a sitemap, add a property, delete a property — so requesting it to submit a sitemap also grants the ability to remove the property. Request the read-only scope unless you genuinely need one of those four, and if you do need one, keep the others out of your own code.
The one thing to build first
A daily export of query × page × date at dataState: final, appended to a table you own. It is the only way to have search data older than 16 months when you eventually want it, and if you build one thing against this API it should be this.
Note the tension with the load quota above: query × page is the most expensive grouping there is. It stays cheap here only because each run covers a single day. So run it daily and append — do not schedule it weekly and pull seven days, and do not try to backfill all 16 months in one request. Walk the backfill a day at a time, and let it take an afternoon.
Searchlight is a client for this API — the same data, on a phone, without writing any of it.
See how Searchlight shows it