SEO Skill

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.