Search Console API

Google Search Console API limits, and what you still have to build.

The quotas are generous and the rows are free. The weekend goes on what comes after the client: a store that outlives the 16-month wall, a diff Google has no endpoint for, and a verdict on which page to open first.

From $49/mo, everything included · works in Claude Code, Cursor, Cline and Windsurf

searchAnalytics.query
// what the API answers with
{ "keys": ["/pricing"], "clicks": 41, "impressions": 3204,
  "ctr": 0.0128, "position": 4.3 }
// what you opened the terminal to find out
QueryScope · move 1 · win the click you already earn
Search search console api limits
Page /pricing
Position 4.3
Click rate 1.3% on 3,204 shown
→ one search at a time, with the measurement running before the next arrives.
Same row. The gap between the two is the code you were about to write.

Quota figures on this page are Google's. The rest comes from the Search Console code we run in production every day.

The actual numbers

The API limit is four ceilings, not one.

They fail in four different ways, and only one of them announces itself. Mixing them up is the reason most first scripts quietly return a partial answer and get believed.

Rows per request

25,000

The rowLimit parameter accepts 1 to 25,000 and defaults to 1,000. That default is the trap: leave it out and you get the first thousand rows with no error and no flag. Page through with startRow until a batch comes back short.

Fails silently. You get a number, just not the whole one.

Query rate

1,200 QPM

For Search Analytics, per site and per user alike, inside a project ceiling of 40,000 QPM. There is also a load quota measured in 10-minute and 1-day chunks, which complex queries burn faster: a page and query join over a long date range is expensive.

Fails loudly. You get an error you can retry.

URL inspections

2,000 / day

Also capped at 600 a minute, per property. This is the one you will actually hit, because it is per URL rather than per query, and indexing questions are exactly the ones you want to ask about every page. A 5,000-page site cannot be inspected in a day.

Fails loudly, then costs you days of calendar time.

History

16 months

Not a quota at all, which is why it never appears on the limits page, and it is the one that actually costs you something. Month 17 is gone, permanently, and there is no endpoint that tells you what changed since yesterday. If you want either, you store the data yourself, starting today.

Never fails. It just quietly forgets.

Every figure above is Google's, from the Search Console API usage limits and the searchAnalytics.query reference. Two of them are constants in our own code: we request 25,000 rows a call when we backfill a site's history, and we hold each daily inspection run well under the 2,000.

Search Console API rate limits and quotas, in full

QPM is queries per minute, QPD queries per day, and none means Google publishes no cap at that level. Per site counts every call about one property, per user every call one Google account makes, per project every call made with one Cloud project's key. The tightest one that applies is the one you hit.

API Scope QPM QPD
Search analytics Per site 1,200 none
Search analytics Per user 1,200 none
Search analytics Per project 40,000 30,000,000
URL Inspection Per site 600 2,000
URL Inspection Per project 15,000 10,000,000
Other calls Per user 200 none
Other calls Per project none 100,000,000

Other calls (the sites and sitemaps resources) are also capped at 20 a second per user. Search Analytics also carries a load quota with no published number: a short-term one measured in 10-minute chunks and a long-term one measured in 1-day chunks. A request grouped or filtered by page and query together, over a long date range, uses up far more of it than a one-day site total.

When you hit the limit

Every overage returns the same quota exceeded error, whichever ceiling you crossed, so the error alone does not tell you which one it was. Google's own advice tells them apart:

  • Wait 15 minutes and try again. If the retry works, you hit the short-term quota: spread the same queries across the day instead of firing them in a burst.
  • Still refused, or refused on a single query in a 10-minute window? That is the long-term quota. Drop the page or query grouping you do not need, shorten the date range, and stop re-pulling data you already have.
  • Hit 2,000 inspections on one property? Nothing resets it before tomorrow. Inspect the pages that matter first and carry the rest over.

The cheapest fix for all three is to store what you pulled. Last month's rows do not change once Search Console finalises them, so a script that re-reads them every run is spending quota on an answer it already has.

Scope

Where the API beats the export, and where it stops.

Only in the API

  • +The exact page and query join. Ask for both dimensions at once and every row carries both keys. The interface cannot show you this and no CSV export produces it.
  • +Hourly rows, through a separate data-state value, which is how the last-24-hours view is built.
  • +Search appearance, the rich-result types you win. Group by it and the response lists the values your property actually has.
  • +Discover as a separate surface. It carries clicks and impressions but no queries and no position, because nothing there is ranked.

Where it stops

  • −Links. The backlink report is interface-only, so there is no endpoint for who links to you.
  • −Anything about a competitor. It is your property or nothing, which also means it cannot see the results page you are ranking on.
  • −The page indexing report as a whole. You reconstruct coverage one URL at a time, against that 2,000 a day cap.
  • −Crawl stats and Core Web Vitals. Both are reports in the interface with no API behind them, so a tool that shows you page speed beside your rankings got it from somewhere else.
  • −The rare queries. Google leaves them out of the query table to protect the searcher's privacy, so the query table totals less than your real clicks. No quota causes this and no parameter turns it off.

That last one is the one that bites hardest, because it looks like data rather than a gap. If you sum query rows to get a site total, you will under-report your own traffic and never see an error. Page-level and query-level data are not interchangeable, and the rule that falls out of it is the one QueryScope is built on: take volume and change from the page dimension, and read queries for alignment and intent only.

Three things the documentation is right about and still does not prepare you for.

Each of these cost us a real debugging session. If the vocabulary is unfamiliar, the Search Console glossary defines every metric and indexing state in a sentence, with the caveat that makes it misleading.

01 The mismatch

The API and the interface disagree, and both are correct.

One setting decides whether a response includes the freshest days or only finalized ones, and it changes the totals for the same site over the same dates. We take finalized data for the stored weekly snapshot, because a week-over-week diff has to compare like with like, and fresh data for everything read live, because that is what the interface shows. Same endpoint, two answers, no bug. Nearly every thread about numbers that do not match is this parameter.

finalized: stable, trails ~3 days · fresh: matches the interface

Why Search Console throws the history away
02 The one we got wrong

We anchored a 24-hour window to now(). It reported 1 click instead of 7.

The hourly pipeline trails the present by a few hours, so a window ending at the current time slices off the hours that actually carry the traffic and counts empty recent hours in their place. It under-reports badly and it never errors, which is why it survived review: the number looked plausible. The fix is to anchor the window to the latest hour that has data, not to the clock. The comment explaining it is still in our sync code, because the next person deserves to find it.

now() anchor: 1 click · latest-hour anchor: 7 clicks

What we found running this on a real SaaS
03 The property string

A domain property and a URL-prefix property are different properties.

The same site can be verified as sc-domain:example.com, as https://example.com/, or with the www or http spelling, and the wrong one returns a 403 rather than an empty result. Guessing is not viable, so we list the properties the credential can actually read and match against the candidates in order. Two minutes of reading beats an afternoon spent debugging an authentication error that was never about authentication.

list the properties first · never guess the string

Connect it over MCP instead

Two ways to get there

The API is free. The layer on top of it is the actual project.

Writing the client is an afternoon and it is a genuinely good afternoon. What follows it is a store, a daily job, a diff, and the judgment call about which of forty pages to open first. QueryScope is that layer, already built, against the same free API.

Write it yourself

1Cloud project, OAuth client, tokens to keep fresh 2paginate 25k at a time, store every pull 3build the diff, there is no delta endpoint 4run it daily forever or lose month 17 5output: correct rows, no verdict

Free, and yours to keep running.

Connect QueryScope

> one line in your IDE, then authorize
✓no Cloud project or consent screen of your own ✓pagination, the daily job and the diff handled ✓daily snapshots kept past the 16-month wall ✓output: the one search to work next

One line, hosted, and the decision made.

What the layer actually is

Five tools in your editor, not a dashboard to alt-tab to. keyword sizes the searches your agent reads out of your repo: how many people search each one, where you stand, and who ranks above you. focus records what the site sells and to whom, and renders the plan to queryscope.md in your repo: a change to what a visitor reads waits for your commit, which is what agrees to it. verdict records what you decided about each search. work is the loop after that, one target at a time, and it will not hand the next one back while a change is still settling, because a second edit inside that window destroys the measurement of the first. history hands your agent the stored series behind all of it, past the 16-month wall, as tables.

Below position 10 Search Console records nothing at all, no impression and no position, so for the searches you declared QueryScope reads the results page directly and fills that dark half. The seam is hard: every page-1 position on your screen came from Search Console, a crawl is only ever used below 10, and the two are never mixed. It stops at the click, it writes no content, and it never touches your repo: your own agent makes every edit.

One line in your IDE. Then authorize and pick a site.

install.sh
claude mcp add --transport http queryscope https://mcp.queryscope.dev/mcp

Works the same in Cursor, Cline and Windsurf. Your IDE opens a browser once to authorize.

Then ask: “what should I work on?”

Keep the API. Skip the layer on top of it.

Same data, same read-only grant, already paginated, stored past the 16-month wall, and worked one search at a time.

7-day free trial · nothing due today · cancel in one click

FAQ

Search Console API questions.

Is the Google Search Console API free?

Yes. There is no charge and no billing account required, only quotas. That is exactly why the interesting question is not what the API costs but what you build on top of it: the storage, the daily job, and the decision about what to fix are the parts that take real time.

How many rows can I get per request?

Up to 25,000, but the default is 1,000 and it applies silently if you leave the parameter out. Page through with the start-row offset and stop when a batch returns fewer rows than you asked for. If you cap your own pagination, log it when you hit the cap: a truncated result that looks complete is worse than an error.

Why does my API data not match the Search Console interface?

Usually the data-state setting. Finalized data trails roughly three days and stays stable; fresh data includes the most recent days and is what the interface shows by default, so the same query over the same dates returns different totals. The second common cause is summing query rows: Google leaves rare queries out of the query table for privacy, so query totals are always lower than page totals.

Can I get backlinks or competitor data from the Search Console API?

No, and no. The links report exists only in the interface, and the API is scoped to properties you have verified access to, so competitor data is out of reach by design. QueryScope fills exactly one part of that gap and says where it came from: for the searches you declared it reads the results page directly, which is how it can tell you an AI Overview or a rival sits above you. Every page-1 position still comes from Search Console, and a crawl is only ever used below position 10.

How far back does the Search Console API go?

Sixteen months, and it rolls forward, so month 17 is deleted rather than archived. There is also no endpoint for what changed since yesterday. Both are reasons to start storing your own daily copy today rather than the day you need the comparison, which is usually the day it is already too late. QueryScope backfills the full window on connect and keeps a daily snapshot from then on, so the history outlives the wall.

Does QueryScope write content or change my site?

No. It never touches your repo and generates no content: your own agent makes every edit. QueryScope holds the plan, reads your Search Console, says which search to work next and what to change, then starts the measurement clock the day Google recrawls the page. It also stops at the click, so it will never tell you a ranking change earned you a signup.