
Guide
How to use the Google Search Console API
Pull search analytics from the Search Console API: auth, searchAnalytics.query, the 25,000-row cap, domain vs URL-prefix properties, and why errors are 403 not 404.
Published August 16, 2026
The Search Console API has one endpoint that matters: searchAnalytics.query. POST a date range and dimensions, get clicks, impressions, CTR, and position back — up to 25,000 rows per request, 16 months back, running two to three days behind. Auth is a service account added to the property as Restricted.
What do you need before the first query?
A service account with the Search Console API enabled and the robot added under Settings → Users and permissions as Restricted — the service account guide covers every console screen. Then the detail that produces the most confusing failure in the whole API: the siteUrl has two forms, and they are different properties.
| Property type | siteUrl in the API | Covers |
|---|---|---|
| Domain | sc-domain:example.com | Every protocol, subdomain, and path |
| URL-prefix | https://www.example.com/ | Exactly that origin and prefix |
Request the form you don't have and the answer is 403, not 404 — indistinguishable from having no access. So don't guess: call the sites list first, and configure from what the API says the account can see. Google explains the property types in Add a website property.
What does a searchAnalytics.query request look like?
POST https://www.googleapis.com/webmasters/v3/sites/
sc-domain%3Aexample.com/searchAnalytics/query
Authorization: Bearer <token>
{
"startDate": "2026-07-01",
"endDate": "2026-07-28",
"dimensions": ["query"],
"rowLimit": 25000,
"startRow": 0
}Rows come back with keys (dimension values in order), clicks, impressions, ctr, and position. Dimensions can combine query, page, date, country, device, and searchAppearance — full request shape in the searchAnalytics.query reference. Worth knowing: this is one plain POST with a body you fully control. The system behind this post calls it with fetch directly — pulling in the entire generated googleapis package for one endpoint buys hundreds of megabytes of dependency for no correctness.
How do pagination and retries actually work?
- Ask for the maximum:
rowLimit: 25000— Google's hard cap per request (see usage limits). - Page by advancing
startRowby the rows received; stop when a page comes back short. - Retry only
429,500, and503, with exponential backoff. Errors like 403 are configuration, not weather — retrying them just hides the message. - Put a ceiling on total pages and record
truncated: truewhen you hit it — a report that says it might be incomplete is honest; one that silently is, lies.
const RETRY_DELAYS_MS = [1000, 2000, 4000, 8000, 16000]
// retry only 429 / 500 / 503; page with startRow until
// rows.length < rowLimit; cap total pages and mark truncatedWhy pull several small shapes instead of one big one?
The intuition is to pull [date, query, page] once and derive everything else. It's wrong, for a documented reason: Search Console withholds rare rows for privacy, and the more dimensions a request has, the more it withholds. Each dimension you add fragments the data into smaller buckets, and small buckets disappear.
The gap between totals and sum of by-query rows is itself a metric: the share of traffic on anonymized queries. Surface it as a first-class caveat instead of discovering it in a spreadsheet argument.
Which limits can't be coded around?
- 16 months of history, then gone. The API is not an archive. If you want year-over-year in two years, pull windows now and store them — the system behind this post commits raw JSON responses to git, 28-day windows frozen once written.
- The last ~3 days are unsettled. Freeze historical windows, and re-pull only the trailing partial window until it clears the settling period.
- Position is impression-weighted. Not a rank. Never present it as one.
What to do with the archive once it exists — turning it into CLI reports an AI agent can consume — is its own build note.
Questions
How delayed is Search Console API data?
The most recent two to three days are incomplete or missing, and recent days can be revised after first appearing. Treat anything younger than about three days as unsettled: pull it, but re-pull it before freezing it into reports.
What are the Search Console API limits?
The load-bearing ones: 25,000 rows per request (page with startRow), 16 months of history, and per-site and per-project request quotas that ordinary reporting never approaches. The practical limit is privacy filtering — rare queries are withheld, so totals across rows never sum to the true total.
Why does the API return 403 for a site I own?
Almost always the property form is wrong. A domain property is addressed as sc-domain:example.com; a URL-prefix property as the full URL with protocol and trailing slash. Asking for the form you don't have returns 403 — the same error as no access at all — so list sites first and copy the string from the response.
Is average position from the API my actual rank?
No. It is an impression-weighted average across every query, page, and country in scope, over the whole date range. A page ranking #2 for one common query and #40 for many rare ones reports a position near neither number. Track clicks and impressions as primary; treat position as a trend line only.
Related notes
- Service account access to GA4 and Search Console
- How to pull GA4 reports with the Data API
- SEO reports from the CLI, sized for AI agents
More on systems integration
- Own your analytics stack, end to end
- The DataForSEO API: SEO data by the cent
- What is server-side tagging?
- How to migrate systems without losing data
Related service: Systems Integration
Want this kind of engineering on your project?
Tall Karol takes on fractional and project-based engagements for startups and agencies.
Book a working session