Google Search Console API: query data you can trust
How the SEO CLI uses the Google Search Console API for query, page, and position data, plus what row limits and anonymized queries mean for your conclusions.
Search Console shows how a verified property performed in Google Search. GA4 shows what happened after measured visitors reached your site. Joining them can turn a vague traffic problem into a page, query, and post-click investigation.
The sources are still different datasets. They will not reconcile row for row, and a gap in one should not be filled with numbers from the other.
See verified properties without copying IDs
Run the guided setup:
seo start
The browser asks you to approve read-only Search Console and Analytics access. The CLI then lists the properties available to that Google account. Choose the Search Console property first and add GA4 only when you want post-click context.
If one GA4 web stream clearly matches the property you chose, the CLI attaches that GA4 property and shows the stream and hostname it matched. When several GA4 properties match, or none is clear, it asks you to choose or skip GA4. Skipping it is fine. You can attach GA4 later.
You do not need to copy a property id if Google can list it. You also do not need GA4 to crawl a public site or run Search Console-only opportunity reports.
Use these commands to check the connection later:
seo auth whoami
seo auth status
seo sites
seo projects list
Use Search Console to find search demand and affected pages
Search Console reports clicks, impressions, CTR, and average position for the property and dimensions you request, and the CLI uses that evidence for reports such as quick wins, page-two opportunities, decaying pages, cannibalisation review, query clusters, and update comparisons.
Google’s Search Console introduction explains the product’s search performance and indexing views. The Google Search Console API gives the CLI programmatic access to part of that evidence. It is not a rank tracker and does not expose every impression as a raw event.
Expect query rows to be smaller than chart totals
Google omits anonymized queries from Search Console tables and the API. The chart can therefore show more clicks than the sum of exported query rows. Google documents that difference in its performance data explanation.
The API also says it returns top rows rather than guaranteeing every row. Its performance data limits currently describe a maximum of 50,000 rows per day per search type, sorted by clicks.
That changes how you should read a report:
- “No retained query matched” is not the same as “the query never appeared.”
- A query list can rank the evidence it received without claiming to cover all demand.
- Adding page, query, device, or country dimensions can change aggregation and retained rows.
- A zero is trustworthy only when the result says the relevant source was complete enough to support it.
Reports keep caps, filters, and source state in their provenance so an agent can make that distinction.
Ask Search Console a precise question
seo gsc-query gives you the Search Analytics rows behind the higher-level
opportunity reports. Use it when you need a specific date range or dimension
combination and a focused report would make too many decisions for you.
It helps with questions such as:
- Which queries sent impressions to one page last month?
- Did mobile and desktop visibility move differently?
- Which countries account for a change in clicks?
- What rows did Search Console retain for a page and query combination?
Run a query with explicit dates and dimensions:
seo gsc-query --project example --start-date 2026-05-01 --end-date 2026-05-31 --dimensions query,page --limit 100 --json
seo gsc-query --help
The command requests clicks, impressions, CTR, and average position grouped by
the dimensions you choose. Supported dimensions include query, page, country,
device, and date. --type changes the Search Console search type. Advanced
users can pass a complete Search Analytics request with --body or
--body-file.
JSON output includes the property, request body, returned rows, rows fetched, API call count, and the number of rows kept after the local output limit. Human output shows a compact table of the first rows.
This is retained Search Console data, not a complete keyword database. Anonymized queries are missing, dimensional rows may not reconcile with property totals, and Google returns top rows rather than promising every row. Keep the request dates, dimensions, search type, filters, and row limits beside any conclusion.
Use quick-win review, segment impact, or decaying pages when you want the CLI to turn those rows into a bounded investigation queue.
Check the indexed version Google reports for one URL
seo url-inspect calls the Search Console URL Inspection API for a URL inside
a property you can access. Use it when the decision depends on Google’s stored
index evidence rather than another live fetch from your computer.
seo url-inspect --project example --url https://example.com/pricing --json
seo url-inspect --help
The request checks that the URL belongs to the selected property, then asks Google for its inspection result. Human output shows the verdict, coverage state, robots state, last crawl time, and Google-selected canonical. JSON keeps the complete structured response returned by the API.
The result describes Google’s indexed snapshot. It is not a live test and it does not prove that the page appears for a query today. A URL can also change after Google’s recorded crawl. URL Inspection has provider quotas, so use it for important URLs and representative samples rather than trying to inspect a large site in one run.
Run audit one page to compare the current live URL with that indexed evidence. Use index monitoring when you need a repeatable, quota-bounded sample across a sitemap.
Compare finalized Search Console windows
Recent Search Console data can be incomplete. Google defines finalized data as
the default API state and dates its metadata in the America/Los_Angeles time
zone. The
Search Analytics API reference
also warns that rows after the first incomplete date may change noticeably.
The CLI uses finalized windows for comparisons that need a directional verdict. That matters for traffic anomalies, update analysis, SEO tests, and before-and-after measurement. Comparing a complete month with the last five unfinished days can manufacture a decline that never happened.
Check the dates in every result. Use --refresh when you need to bypass the
local cache, but remember that a fresh API response can still contain provider
data that is not final.
Use official Google update windows
seo updates lists ranking updates from the official Google Search Status
Dashboard. Use it to confirm the name, start date, end date, and status of a
known update before comparing search performance around it.
seo updates --product Ranking --limit 10
seo updates --product Ranking --limit 10 --json
seo updates --help
The command requests official incidents for the selected product and returns
their start time, end time, type, name, and status. An open incident has no end
date yet. --limit bounds the rows returned.
This feed does not include third-party volatility scores, unconfirmed chatter, or a diagnosis for your site. Matching dates establish timing context only. A release, migration, tracking change, seasonality, competitor, or demand shift can overlap the same window.
Use update correlation to compare a property with overlapping official windows. Use the Google update postmortem when you also need page, query, device, country, and known-confounder evidence.
Add GA4 when post-click behavior changes the decision
GA4 can add landing-page sessions, users, engagement, events, key events, sources, campaigns, and known AI referrals. It helps answer a different set of questions:
- Did the landing page receive measured sessions after its search clicks changed?
- Did a traffic shift affect engagement or a configured key event?
- Which known referral sources sent measurable visits?
- Does one page group behave differently from another after the click?
GA4 cannot restore visits that were never measured, identify a referrer that was stripped, or prove that an SEO edit caused a conversion change.
Run a raw report when you need a supported GA4 dimension and metric combination:
seo ga4-report --project example --start-date 2026-05-01 --end-date 2026-05-31 --dimensions landingPage --metrics sessions,totalUsers,eventCount --limit 100 --json
seo ga4-report --help
The command uses the GA4 property saved in the project profile unless you pass
--property. It requests the date range, dimensions, metrics, and row limit you
provide. Advanced users can pass a complete Data API request with --body or
--body-file.
Human output shows the selected property, row count, and a compact table. JSON
keeps the Data API response, including headers, rows, totals, metadata, and row
count when Google returns them. This makes seo ga4-report useful when you need
raw post-click evidence without a higher-level SEO interpretation.
Use AI referrals for known assistant referral sources. Use measure change when a recorded page or page-group change needs equal before and after windows.
Leave room for GA4 processing and data controls
Google says Analytics data can change while events are processed. Its data freshness guidance notes that processing can take 24 to 48 hours and that recent reports can use stricter cardinality limits before daily data is ready.
The Data API can also return sampled data, estimated unique counts, thresholded
rows, and an (other) row for high-cardinality dimensions. Google’s
Data API reporting expectations
explain why an API response and a UI report can differ even when both are
working as designed.
Reports preserve available metadata for those states. If a result is partial or thresholded, use it for bounded direction rather than an exact all-site total.
Understand the local OAuth handoff
The public package can include the project’s Google desktop OAuth client. The
CLI opens the Google authorization page, listens on a temporary loopback
address for the response, exchanges the code, then stores the resulting tokens
in your system keychain when it is available. A private local file with 0600
permissions is the fallback for headless machines, locked keychains, and any
machine where you choose file storage.
Google recommends loopback redirects for installed desktop apps on macOS, Linux, and Windows in its OAuth guide for desktop apps. The browser is talking to Google during consent. The loopback redirect only returns the authorization response to the CLI running on your machine.
Some builds may not include the shared client. Add your own Google desktop OAuth client when needed:
seo auth setup-client
This is an advanced fallback, not a normal onboarding step.
Use seo auth status to see where the active tokens are stored. You can make
the choice yourself when a local setup needs it:
seo auth storage --keychain
seo auth storage --file
Changing storage carries an existing local login across. A file fallback does not make the CLI less private. It keeps the token file readable only by your user account.
Use a service account in CI or a container
Browser sign-in belongs on a person’s machine. A GitHub Actions runner, a container, or a server needs a credential with no browser involved. The CLI supports one Google service account JSON credential for that job.
Create the service account in a Google Cloud project with the Search Console API, Google Analytics Data API, and Google Analytics Admin API enabled. Then grant the service account email access to the properties it needs:
- In Search Console, a property owner adds the service account email under Settings, then Users and permissions. Google explains the available Search Console roles and user setup.
- In GA4, grant the service account access to the property before expecting it to appear in account summaries. Google’s Admin API quickstart covers service-account property access and API setup.
Keep the JSON key in your platform’s secret store. Do not commit it, put it in a project profile, or copy a personal OAuth token into a deployment image.
GitHub Actions can pass the whole JSON key as one secret:
- name: Run the main SEO report
run: |
npm i -g seo
seo report --site sc-domain:example.com --json > seo-report.json
env:
SEO_GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.SEO_GOOGLE_SERVICE_ACCOUNT_JSON }}
The service account uses the same read-only Search Console and Analytics scopes as browser sign-in. Run these commands in the job log to confirm which credential source is active and which properties Google returned:
seo auth status
seo sites
For a local container or server, prefer a mounted read-only file:
export SEO_GOOGLE_SERVICE_ACCOUNT_FILE=/run/secrets/seo-google-service-account.json
seo auth status
seo report --site sc-domain:example.com --json
GOOGLE_APPLICATION_CREDENTIALS is also accepted as a file-path alias. The CLI
does not search for local gcloud credentials or write a private key
to disk. Set only one of the three variables. A configured service account
takes precedence over a saved browser login, so unset it before running
seo auth login.
If Google denies a property, check that the service account email was granted
access to that exact Search Console or GA4 property. seo auth status shows
the active identity without printing the key. seo doctor also checks the
credential configuration before a report runs.
Remove access and local tokens
seo auth logout
seo privacy
Logout deletes locally stored Google tokens. It does not delete your Search Console property or GA4 property. The privacy policy documents the local paths and network requests, while the setup guide covers project profiles and first-run troubleshooting.