# Agents (https://projects.piyushgambhir.com/gsc-cli/docs/agents) `gsc` is built for coding agents as much as for people. Any agent or harness that can run shell commands can use it; nothing else is needed. ## Ground rules [#ground-rules] * Run commands with `-o json --no-input`. Data is on stdout; notes and structured errors are on stderr. * Use `--read-only` unless the user asked for a change. It blocks every command that changes Search Console, local credentials, or the binary itself. `query` and `inspect` send POST requests but only read, so they still work. * Run writes (`sites add`, `sites remove`, `sitemaps submit`, `sitemaps delete`) only when the user asked, and with `--dry-run` first to show the request. Destructive commands also need `--yes` under `--no-input`; pass it only for the exact change the user asked for. * Check `gsc auth status -o json` first; it reads local configuration only. If nothing is logged in, ask the user to run `gsc auth login`. Never invent credentials. In CI, `GSC_CREDENTIALS` or `GSC_ACCESS_TOKEN` may already be set. ```bash # A safe, non-interactive read for an agent gsc --read-only top queries --last 28d --limit 20 -o json --no-input ``` ## Discover commands [#discover-commands] `gsc --help` and `gsc COMMAND --help` list every command and flag, and the [command reference](https://projects.piyushgambhir.com/gsc-cli/docs/reference/commands) has all of them on one page. For an API method without a dedicated command, `gsc api METHOD PATH` sends an authenticated request with the same redaction and error handling. `GET` and the two read-only `POST`s (`searchAnalytics/query` and `urlInspection/index:inspect`) count as reads; every other request is a write, blocked by `--read-only` and printed instead of sent with `--dry-run`. ## Automation flags [#automation-flags] | Flag | Environment | Effect | | -------------- | ----------------- | -------------------------------------------------------------------------------- | | `--read-only` | `GSC_READ_ONLY=1` | Blocks remote writes, local credential changes, and self-update | | `--no-input` | `GSC_NO_INPUT=1` | Never prompts or opens a browser; commands fail instead | | `--dry-run` | | Prints write requests and sends nothing; `--read-only` still refuses the command | | `--yes` | | Confirms destructive commands without a prompt | | `--quiet` | `GSC_QUIET=1` | Drops informational notes from stderr | | `--verbose` | `GSC_VERBOSE=1` | Logs method, URL, and status to stderr (never tokens or bodies) | | `--site`, `-s` | `GSC_SITE` | Property for this command | | `--profile` | `GSC_PROFILE` | Saved profile for this command | Agents never see the update notice: its once-a-day GitHub check runs only when stderr is a terminal, and `GSC_NO_UPDATE_NOTIFIER=1` turns it off everywhere. `gsc update --check -o json` reports the current and latest versions. ## Reporting results honestly [#reporting-results-honestly] * Take totals from `gsc performance`. Never sum query or page rows into site totals: anonymized queries are withheld and at most 50,000 rows per day are exposed. * Run `gsc freshness` before trusting the last few days. Never present `--data-state all` numbers as final. * `complete: true` only means pagination ended. With `--compare`, `null` means a row was not returned for that period; treat it as unknown, not zero. * `gsc inspect` reports Google's indexed version, not a live test, and cannot request indexing. It costs quota (2,000 per property per day), so inspect only the URLs you need and use `--max`. * Search Analytics has unpublished load quotas. Prefer narrower date ranges, avoid grouping by page and query together over long ranges, and do not retry quota errors in a loop. [Reading the data](https://projects.piyushgambhir.com/gsc-cli/docs/data) covers each of these in detail. ## Agent skill and LLM files [#agent-skill-and-llm-files] * Install the agent skill with `npx skills add piyush-gambhir/gsc-cli@gsc` (Claude Code, Codex, Cursor, and other agents that read `~/.agents/skills`), or copy the [`gsc/`](https://github.com/piyush-gambhir/gsc-cli/tree/main/gsc) directory into your agent's skills directory. It bundles the command and auth references, so it works on its own. * `gsc commands -o json` describes every command offline: usage, flags and defaults, whether it reads or writes (`read`, `remote_write`, `local_write`), whether it may prompt, the API methods it exists to call, and examples. A bare host given to `-s`, `--verify`, `doctor --online`, and login also list properties once (`webmasters.sites.list`). `gsc api methods -o json` is the same map from the API side. * Failures exit `1` with a structured error whose `kind` (`auth`, `permission`, `not_found`, `rate_limit`, `usage`, `read_only`, `confirmation_required`, ...) says what to do next; see [Output formats](https://projects.piyushgambhir.com/gsc-cli/docs/output-formats#errors). * [`llms.txt`](https://projects.piyushgambhir.com/gsc-cli/llms.txt) indexes these docs, and [`llms-full.txt`](https://projects.piyushgambhir.com/gsc-cli/llms-full.txt) contains all of them, including the full command reference. # Authentication (https://projects.piyushgambhir.com/gsc-cli/docs/authentication) Search Console data belongs to a Google account, so every request needs an OAuth access token. API keys do not work for this API, and Google's device-code flow does not allow Search Console scopes. `gsc` supports every other way to get a token. | Method | Command | Best for | What is stored | | ------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------- | | Browser login (default) | `gsc auth login` | People on a machine with a browser | Refresh token in the OS keychain | | Headless browser login | `gsc auth login --no-browser` | SSH sessions, containers | Same | | Your own OAuth client | `gsc auth login --client-secret-file client_secret.json` | Teams wanting their own quota or branding | Refresh token and client secret in the keychain | | Service account | `gsc auth login --service-account key.json` | CI, servers | Path to the key file (no secret copied) | | Application Default Credentials | `gsc auth login --adc` | gcloud users, GitHub Actions federation, GCE and Cloud Run | Nothing | | Impersonation | `gsc auth login --adc --impersonate SA_EMAIL` | Keyless CI, least privilege | Nothing | | Credentials file per command | `--credentials FILE` or `GSC_CREDENTIALS` | One-off automation | Nothing | | Raw access token | `--access-token` or `GSC_ACCESS_TOKEN` | Scripts, debugging | Nothing | ## Credential precedence [#credential-precedence] Every command uses the first of: `--access-token` / `GSC_ACCESS_TOKEN`, then `--credentials` / `GSC_CREDENTIALS`, then the selected profile. Profiles are chosen by `--profile`, then `GSC_PROFILE`, then the saved default. `gsc` never falls back to ambient Application Default Credentials on its own, so an agent never runs as an identity nobody chose. ## Browser login [#browser-login] ```console $ gsc auth login Opening your browser to sign in. If it does not open, visit: https://accounts.google.com/o/oauth2/v2/auth?... Logged in as me@example.com (profile "default"). Found 3 properties. Default site set to sc-domain:example.com (change with: gsc sites use SITE). ``` 1. `gsc` listens on `127.0.0.1` on a free port and opens Google's consent page (PKCE S256, a random `state`, offline access). The URL is printed to stderr in case the browser does not open. 2. Approve access on Google's consent screen. The tab then confirms you are signed in; close it and return to the terminal. 3. The refresh token is saved in the OS keychain (macOS Keychain, Windows Credential Manager, or the Secret Service on Linux). Access tokens refresh automatically from then on. Keychain entries are namespaced by config file, so same-named profiles in different configs (`GSC_CONFIG` or a custom `XDG_CONFIG_HOME`) never overwrite each other. 4. `gsc` lists your properties once and picks a default site: the only property, or your choice from a list. When it cannot ask (for example a service account or ADC login with `--no-input`), it picks the first domain property. `--no-verify` skips this call. `gsc` asks Google for Search Console access (`https://www.googleapis.com/auth/webmasters`) plus `openid` and `email`, which let it show the signed-in account. What it does with that access is in the [privacy policy](https://projects.piyushgambhir.com/gsc-cli/privacy). Options: * `--profile NAME` saves under a named profile (default: the current profile, or `default`). * `--scope readonly` requests `webmasters.readonly` only. Write commands then stop early with a message. The default `full` scope covers every command; `--read-only` still blocks writes for agents. * `--insecure-storage` keeps the token in `secrets.yaml` (mode `0600`) next to the config instead of the keychain. Google's OAuth policy asks for encrypted storage, so use it only where no keychain exists. Without the flag, a missing keychain stops the login (and asks first when you are at a terminal). Run `gsc auth login` again any time Google revokes access; errors say exactly which command to run. The built-in OAuth client is added to release builds when they are built; `gsc version -o json` reports `builtin_oauth_client` for the binary you have. Everyone using it shares one Google Cloud project's quota, so heavy users should bring their own client or a service account. ## Headless login [#headless-login] For a machine without a browser: 1. Run `gsc auth login --no-browser` and open the printed URL on any device. 2. Approve access. The browser then tries to load `http://127.0.0.1:8085/?code=...` and fails. That is expected. 3. Copy the full address from the browser and paste it into the terminal. This uses the same loopback redirect and PKCE as the normal flow. It is not Google's retired out-of-band (copy-a-code) flow. ## Your own OAuth client [#your-own-oauth-client] 1. In Google Cloud Console, create or pick a project and enable the **Google Search Console API**. 2. Configure the OAuth consent screen (External or Internal), then create an OAuth client of type **Desktop app** and download its JSON. 3. Run `gsc auth login --client-secret-file client_secret_XXXX.json`. Alternatively set `GSC_CLIENT_ID` and `GSC_CLIENT_SECRET`, which override the built-in client for every login. Each profile records which client it used, so refreshes always use the same one. Keep the consent screen **In production**: Google expires refresh tokens after 7 days for apps left in Testing. ## Service accounts [#service-accounts] 1. In Google Cloud Console, enable the Search Console API, create a service account, and create a JSON key. 2. In Search Console, open the property, then **Settings > Users and permissions > Add user**, and add the service account email (`...@...iam.gserviceaccount.com`) with **Full** permission (Owner only if you need owner-only actions). 3. Run `gsc auth login --profile ci --service-account /path/key.json`. `gsc` stores the key file path, not the key. Google Workspace admins can use domain-wide delegation with `--subject user@yourdomain.com`; the impersonated user must have access to the property. For a single command without a profile: `GSC_CREDENTIALS=/path/key.json gsc performance -s ...`. ## Application Default Credentials and impersonation [#application-default-credentials-and-impersonation] `gsc auth login --adc` saves a profile that resolves Google's Application Default Credentials when a command runs: `GOOGLE_APPLICATION_CREDENTIALS`, the gcloud ADC file, workload identity federation, or the GCE and Cloud Run metadata server. gcloud's own client is meant for Google Cloud scopes, so log in with your Desktop client and the Search Console scope: ```bash gcloud auth application-default login \ --client-id-file=client_secret.json \ --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/webmasters.readonly gsc auth login --profile gcloud --adc ``` Keyless CI (for example GitHub Actions with `google-github-actions/auth`): give the federated identity `roles/iam.serviceAccountTokenCreator` on a service account, add that service account to the Search Console property, then: ```bash gsc auth login --profile ci --adc --impersonate reporting@my-project.iam.gserviceaccount.com --no-verify ``` ## Raw access tokens [#raw-access-tokens] `GSC_ACCESS_TOKEN=$(gcloud auth print-access-token) gsc sites list` works with any token that carries a Search Console scope. Nothing is stored and nothing refreshes. `gsc auth token` prints the current profile's token for debugging; treat it as a secret. ## Status, profiles, and logout [#status-profiles-and-logout] ```bash gsc auth status # which credential is in use, from local config only gsc auth status --verify # also lists properties once gsc auth list # profiles, without secrets gsc auth use work # switch the default profile gsc auth logout # delete this profile and its token locally gsc auth logout --revoke # also revoke at Google (asks; --yes to skip) ``` `--revoke` is opt-in because Google revokes the account's tokens for every client in the OAuth project, which signs out every other machine and profile using the same client. You can also remove access from your Google Account at [myaccount.google.com/permissions](https://myaccount.google.com/permissions). `--read-only` blocks every command that changes local credentials or configuration, as well as remote writes; tokens refreshed in read-only mode stay in memory. `gsc login` and `gsc status` are aliases of `gsc auth login` and `gsc auth status`, and `gsc config show`, `config list-profiles`, and `config use-profile` match the other CLIs in the suite. ## Troubleshooting [#troubleshooting] | Message | Fix | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `this build has no built-in OAuth client` | Use a release build, set `GSC_CLIENT_ID` and `GSC_CLIENT_SECRET`, or pass `--client-secret-file` | | `the OS keychain is unavailable` | Start a Secret Service (for example gnome-keyring), re-run login with `--insecure-storage`, or use `GSC_ACCESS_TOKEN` or `GSC_CREDENTIALS` | | `OS keychain did not respond within 10s` | Unlock the keychain and retry | | `Google rejected the saved credentials (invalid_grant ...)` | Run `gsc auth login --profile NAME`. Causes: revoked access, a password change, or a Testing-mode client | | `The signed-in identity lacks access to this property` | Check `gsc sites list`, or add the user or service account in Search Console | | `Enable the Google Search Console API ...` | Enable the API in the OAuth client's Google Cloud project | | `another gsc is on PATH` (doctor) | Ghostscript and Gambit Scheme also install `gsc`. Put this binary's directory first on `PATH`, or alias it | # Bulk export (https://projects.piyushgambhir.com/gsc-cli/docs/commands/export) ```bash gsc export --last 16m -d query -d page --out ./gsc-export gsc export --start 2026-01-01 --end 2026-03-31 -d page --format ndjson --out ./q1 ``` `gsc export` splits the range into days, as Google's extraction guide recommends, fetches every row Google exposes for each day, and writes one file per day (`2026-01-01.csv`, or `.ndjson` with `--format ndjson`) to the `--out` directory. Each file is written to a temporary file first and renamed into place, so an interrupted day never leaves a partial file behind. ## Resuming [#resuming] `manifest.json` in the output directory records the request and the days already written. Run the same command again to pick up where it stopped. * Days after the latest final date, and days Google marks incomplete, are recorded as preliminary and fetched again on the next run. * If the manifest was written for a different request (another property, dimensions, filters, data state, or format), `gsc export` stops instead of overwriting it. Pass `--restart` to discard it, or choose a new `--out` directory. ## Pacing and retries [#pacing-and-retries] Each day's fetch starts at least `--interval` (default `250ms`) after the previous one, to stay under the per-property quota; the pages within one day are fetched back to back. There are no automatic retries by default: an error stops the export, and rerunning resumes it. `--retry N` retries quota and server errors up to N times per day with backoff, logging each retry on stderr. ## Options [#options] `export` takes the same dimensions, filters, search type, data state, and aggregation flags as [`gsc query`](https://projects.piyushgambhir.com/gsc-cli/docs/commands/query). `--data-state final` is the default, so files hold final data unless you ask for preliminary days. `export` only writes the files you asked for and changes nothing in Search Console, so it also runs under `--read-only`. Each day is capped by Google at 50,000 rows per search type, and anonymized queries are never included. Totals come from `gsc performance`, not from summing exported rows. # Overview (https://projects.piyushgambhir.com/gsc-cli/docs/commands) Every command has `-h`/`--help`, and every command that returns data accepts `-o json|yaml`, plus `-o csv` for row-shaped results. The [command reference](https://projects.piyushgambhir.com/gsc-cli/docs/reference/commands) lists every flag; these pages show how the groups fit together. ## Groups [#groups] ## Global flags [#global-flags] | Flag | Effect | | ----------------------------------- | --------------------------------------------------------------------------------------------- | | `-s`, `--site` | Property: `sc-domain:example.com`, `https://www.example.com/`, or a bare host (or `GSC_SITE`) | | `--profile` | Saved profile (or `GSC_PROFILE`) | | `-o`, `--output` | `table` (default), `json`, `yaml`, or `csv` | | `--read-only` | Block remote writes, local credential changes, and self-update | | `--dry-run` | For write commands: print the request and send nothing | | `--yes` | Confirm destructive commands without a prompt | | `--no-input` | Never prompt or open a browser | | `-q`, `--quiet` / `-v`, `--verbose` | Fewer notes, or a log line per request, on stderr | | `--timeout` | HTTP request timeout (default `30s`) | | `--access-token`, `--credentials` | Credential overrides; prefer `GSC_ACCESS_TOKEN` or `GSC_CREDENTIALS` | The property comes from `--site`, then `GSC_SITE`, then the profile's default site (set at login or with `gsc sites use`). ## Account and setup [#account-and-setup] ```bash gsc auth status # also: login, list, use, logout, token gsc doctor --online # local checks plus one API call gsc update --check gsc completion zsh > "${fpath[1]}/_gsc" ``` ## Any API method [#any-api-method] `gsc api` sends an authenticated request to the Search Console API, so a method Google adds later is usable the day it ships. `PATH` is relative to `https://searchconsole.googleapis.com` and must already be URL-escaped: ```bash gsc api GET /webmasters/v3/sites gsc api POST /webmasters/v3/sites/sc-domain%3Aexample.com/searchAnalytics/query --data '{"startDate":"2026-09-01","endDate":"2026-09-30"}' gsc api PUT /webmasters/v3/sites/sc-domain%3Aexample.com/sitemaps/https%3A%2F%2Fexample.com%2Fsitemap.xml --dry-run ``` `GET` and the two read-only `POST`s are reads. Every other request is treated as a write: blocked by `--read-only`, printed instead of sent with `--dry-run`, and confirmed (or `--yes`) before it is sent. `--data` takes JSON, `@file`, or `-` for stdin. # Insights (https://projects.piyushgambhir.com/gsc-cli/docs/commands/insights) Each insight runs ordinary Search Analytics queries and computes the result locally. The thresholds it used are printed on stderr and included in JSON output, along with a note on how the result was computed, and the main thresholds have flags. Results describe the rows Google exposes; anonymized queries are never included. All insights take `--last` (default `28d`), `--start`, `--end`, `--type`, `--filter`, `--data-state`, `--aggregation`, and `--limit`. ## Striking distance [#striking-distance] ```bash gsc insights striking-distance --last 28d gsc insights striking-distance --min-position 8 --max-position 15 --min-impressions 500 ``` Queries whose average position is between `--min-position` (default 4) and `--max-position` (default 20\), with at least `--min-impressions` (default 100), sorted by impressions. Average position is per query across all pages. ## Low CTR [#low-ctr] ```bash gsc insights low-ctr --factor 0.5 --min-impressions 200 ``` Queries whose CTR is below `--factor` (default 0.5) of this site's median CTR for the same position bucket. Buckets are positions 1 to 10 one by one (rounded), then 11 to 20, then 21 and beyond; a bucket with fewer than three qualifying queries is skipped. `missed_clicks_estimate` is impressions times the gap to the median: an estimate, not a forecast. ## Cannibalization [#cannibalization] ```bash gsc insights cannibalization --min-share 0.2 ``` Queries with at least `--min-impressions` (default 100) where two or more pages each have at least `--min-share` (default 0.1) of the query's impressions. Rows list one competing page each, grouped by query, with that page's `share`. ## Decliners [#decliners] ```bash gsc insights decliners --by page --compare yoy ``` Queries (or pages with `--by page`) that lost the most clicks against the `--compare` period (`previous` by default, or `yoy`), among rows with at least `--min-clicks` (default 5) in the comparison period. Only rows returned in both periods count: a row missing from one period is left out rather than treated as zero. ## New and lost queries [#new-and-lost-queries] ```bash gsc insights new-queries --compare previous gsc insights lost-queries --by page ``` `new-queries` lists rows returned now but not in the comparison period; `lost-queries` lists the reverse. Both describe exposure within the fetched rows, not rankings: a query can drop out of the returned rows without losing its position. # URL Inspection (https://projects.piyushgambhir.com/gsc-cli/docs/commands/inspection) ```bash gsc inspect https://www.example.com/pricing gsc inspect --file urls.txt -o json cat urls.txt | gsc inspect --file - --max 200 ``` `gsc inspect` calls the URL Inspection API for each URL. The table shows the verdict, coverage state, indexing state, last crawl time, page fetch state, robots.txt state, Google-selected and user-declared canonicals, the crawler used, and rich results. JSON returns Google's full result for each URL. URL Inspection reports the version Google has **indexed**, as last crawled. It is not a live test of the page as it is now, and the API cannot request indexing. ## Batches and quota [#batches-and-quota] Google allows 2,000 inspections per property per day and 600 per minute. `gsc inspect`: * reads URLs from arguments, or from `--file` (one per line, `-` for stdin), and skips duplicates; * inspects at most `--max` URLs (default 2,000, the daily quota) and warns when it drops the rest; * pauses `--interval` between inspections (default `120ms`, which stays under 600 per minute); * records an error for a single URL and moves on, but stops cleanly when a quota is reached, keeping the results it has. Inspect only the URLs you need: every inspection counts against the property's quota. ## Language [#language] `--language` sets the language of issue messages as a BCP 47 code (default `en-US`): ```bash gsc inspect https://www.example.com/de/preise --language de-DE ``` # Search performance (https://projects.piyushgambhir.com/gsc-cli/docs/commands/performance) ```bash gsc performance --last 28d --compare previous # totals, and the change from the 28 days before gsc top queries --last 3m --limit 50 # top rows for one dimension gsc trend --last 16m --by month # a time series gsc freshness # the latest final and preliminary dates ``` ## Totals [#totals] `gsc performance` runs a query with no dimensions, so its clicks, impressions, CTR, and average position match the Performance report chart, anonymized queries included. Use it whenever you need site totals. ```bash gsc performance --type discover --last 3m -o json gsc performance --filter 'page ~ /blog/' --compare yoy ``` ## Top rows [#top-rows] `gsc top` groups by one dimension and returns the top rows by clicks, 25 by default: ```bash gsc top queries gsc top pages --limit 100 gsc top countries # ISO 3166-1 alpha-3 codes, such as usa and ind gsc top devices gsc top appearance # search appearance types ``` `searchAppearance` cannot be grouped with other dimensions, so `top appearance` lists the values first. To break one down further, filter on it with `gsc query --filter 'searchAppearance = VALUE'`. ## Trends [#trends] `gsc trend` reports clicks, impressions, CTR, and position over time: ```bash gsc trend --last 3m --by week gsc trend --by hour --last 2d # hourly, preliminary data, last 10 days gsc trend --last 16m --by month --filter 'page ~ /blog/' ``` `--by date` (the default) and `--by hour` come from Google. `--by week` (ISO weeks) and `--by month` are rolled up locally from daily rows: clicks and impressions are summed, CTR is recomputed from the sums, and position is weighted by impressions. The output says so. ## Freshness [#freshness] ```bash gsc freshness gsc freshness --type news -o json ``` Two small date-grouped queries over the last 10 days, one for final data and one including preliminary data. Final data usually lags two to three days; check this before reading the last few days. ## Periods and filters [#periods-and-filters] These flags work the same on `performance`, `top`, `trend`, `query`, `export`, and the insights: | Flag | Meaning | | ------------------ | --------------------------------------------------------------------------------------------- | | `--last` | `7d`, `28d` (default), `4w`, `3m`, `16m`: a window ending at the latest available date | | `--start`, `--end` | Inclusive dates, `YYYY-MM-DD`, in Pacific Time | | `--type` | `web` (default), `image`, `video`, `news`, `discover`, or `googleNews` | | `--filter` | `'DIM OP VALUE'`; repeat to AND. See [Search Analytics queries](https://projects.piyushgambhir.com/gsc-cli/docs/commands/query#filters) | | `--data-state` | `final` (default), `all` (includes preliminary data), or `hourly_all` | | `--aggregation` | `auto`, `byPage`, `byProperty`, or `byNewsShowcasePanel` | ## Comparisons [#comparisons] `--compare previous` adds the period just before; `--compare yoy` adds the same dates a year earlier. It works on `performance`, `top`, and `query`: ```bash gsc performance --compare yoy gsc top pages --compare previous --limit 50 -o json ``` Rows are joined on their dimension values and gain `_prev`, `_delta`, and `_pct` columns. A row missing from one period is `null`, not zero; CTR changes are in percentage points; percent change is `null` when the earlier value is zero. Never add query or page rows up into site totals; Google withholds anonymized queries and caps the rows it returns. Use `gsc performance`. See [Reading the data](https://projects.piyushgambhir.com/gsc-cli/docs/data). # Properties & sitemaps (https://projects.piyushgambhir.com/gsc-cli/docs/commands/properties) ## Properties [#properties] ```bash gsc sites list # properties you can access and your permission level gsc sites get # the default property (or name one) gsc sites use sc-domain:example.com # set the profile's default property gsc sites add https://www.example.com/ # add to your list (write) gsc sites remove https://www.example.com/ # remove from your list (write, asks first) ``` A property is a domain property (`sc-domain:example.com`) or a URL prefix (`https://www.example.com/`), used exactly as `sites list` prints it. Commands pick it from `--site`, then `GSC_SITE`, then the profile's default. A bare host such as `example.com` is matched against your properties: a domain property wins, otherwise a single URL-prefix match, and an ambiguous match is an error that lists the candidates. `sites use` saves an exact identifier without a network call; a bare host costs one `sites.list` call. `sites add` needs an exact identifier and only adds the property to your list: it does not verify ownership, which you complete in Search Console. ## Sitemaps [#sitemaps] ```bash gsc sitemaps list # submitted sitemaps gsc sitemaps list --index https://www.example.com/sitemap-index.xml gsc sitemaps get https://www.example.com/sitemap.xml # status, errors, and warnings gsc sitemaps submit https://www.example.com/sitemap.xml # write gsc sitemaps delete https://www.example.com/old-sitemap.xml # write, asks first ``` `sitemaps delete` removes the sitemap from the report; Google may still crawl it. ## Writes [#writes] `sites add`, `sites remove`, `sitemaps submit`, and `sitemaps delete` change Search Console, so: * `--dry-run` prints the request and sends nothing; * `--read-only` blocks them; * `sites remove` and `sitemaps delete` ask for confirmation, or need `--yes` under `--no-input`; * a profile logged in with `--scope readonly` stops them early with a message, because the read-only scope cannot write. ```bash gsc sitemaps submit https://www.example.com/sitemap.xml --dry-run ``` # Search Analytics queries (https://projects.piyushgambhir.com/gsc-cli/docs/commands/query) `gsc query` runs `searchAnalytics.query` with every option the API has. The helpers on [Search performance](https://projects.piyushgambhir.com/gsc-cli/docs/commands/performance) are shortcuts for common shapes of this command. ```bash gsc query -s sc-domain:example.com -d query -d page --limit 5000 -o csv gsc query --last 3m -d date --filter 'country = ind' --filter 'query ~ shoes' gsc query -d hour --data-state hourly_all --last 2d -o json gsc query -d query --compare previous --limit 500 ``` Without dates it covers the last 28 days ending at the latest day with data. Google sorts rows by clicks. ## Dimensions [#dimensions] `-d` (`--dimension`) groups the rows; repeat it or pass a comma list. The columns follow the order you give: | Dimension | Values | | ------------------ | --------------------------------------------------------------------------- | | `date` | `YYYY-MM-DD`, Pacific Time | | `hour` | Hourly timestamps; needs `--data-state hourly_all` and covers about 10 days | | `query` | Search queries (anonymized queries are never included) | | `page` | Page URLs | | `country` | ISO 3166-1 alpha-3 codes, such as `usa` | | `device` | `DESKTOP`, `MOBILE`, `TABLET` | | `searchAppearance` | Rich result and feature types; cannot be grouped with other dimensions | ## Filters [#filters] `--filter 'DIM OP VALUE'` filters on `query`, `page`, `country`, `device`, or `searchAppearance`; dates come from `--start`, `--end`, or `--last`. Repeated filters are ANDed; the API has no OR. | Operator | Meaning | | -------- | ---------------------------------------------- | | `=` | Equals (case-sensitive for `page` and `query`) | | `!=` | Does not equal | | `~` | Contains | | `!~` | Does not contain | | `=~` | Matches an RE2 regular expression | | `!=~` | Does not match an RE2 regular expression | ```bash gsc query -d page --filter 'page =~ ^https://www.example.com/blog/' --filter 'device = MOBILE' gsc query -d query --filter 'query !~ example' # leave out branded queries ``` ## Search type, data state, and aggregation [#search-type-data-state-and-aggregation] * `--type`: `web` (default), `image`, `video`, `news`, `discover`, or `googleNews`. * `--data-state`: `final` (default), `all` to include preliminary data, or `hourly_all` for the `hour` dimension. See [Reading the data](https://projects.piyushgambhir.com/gsc-cli/docs/data#final-and-preliminary-data). * `--aggregation`: `auto`, `byPage`, `byProperty`, or `byNewsShowcasePanel`. Neither `byProperty` nor `byNewsShowcasePanel` can be combined with page grouping or a page filter. `byProperty` is not available for `discover` or `googleNews`; `byNewsShowcasePanel` needs one of them and exactly one `searchAppearance = NEWS_SHOWCASE` filter. These rules are checked before any request is sent. Anything else is left to Google, whose error is shown as it is. ## How many rows [#how-many-rows] `--limit` sets the number of rows (default 1,000). Rows are fetched in pages of up to 25,000 with `startRow`, and `--all` keeps fetching until Google has nothing more. `complete: true` in JSON means pagination reached the end of what Google exposes, never that every query is included. ## Raw requests [#raw-requests] ```bash gsc query -d query --filter 'page ~ /blog/' --print-request # show the body, send nothing gsc query --request-file body.json --last 7d # start from a body; flags override it gsc query -d query --raw # Google's response, unchanged ``` `--request-file` takes a JSON request body (or `-` for stdin) for anything flags do not express. `--raw` prints a single page of Google's response unchanged, so it cannot be combined with `--all` or `--compare`. # Compatibility (https://projects.piyushgambhir.com/gsc-cli/docs/compatibility) `gsc` targets the Search Console API at `https://searchconsole.googleapis.com/`. Older method pages still show `https://www.googleapis.com/`, which `gsc` does not use. ## Pinned API snapshot [#pinned-api-snapshot] `gsc` is built and tested against Google's discovery documents, captured from Google's live discovery service on 2026-10-07 and vendored unchanged in the repository. A coverage test checks their SHA-256 and revision on every test run. | API | Discovery revision | Methods | | --------------------- | ------------------ | -------------------------------------------------------------------------------- | | Search Console API v1 | `20261005` | 11: the 10 active methods `gsc` implements, and the retired Mobile-Friendly Test | | Indexing API v3 | `20260923` | 2, skipped by decision | The test fails when a snapshot method has no command or skip reason, or when a mapped command does not exist. The [API coverage](https://projects.piyushgambhir.com/gsc-cli/docs/reference/api-coverage) page lists every method, and the [compatibility record](https://github.com/piyush-gambhir/gsc-cli/blob/main/docs/compatibility.md) has the hashes and how to refresh the snapshot. The Indexing API is skipped because Google limits it to pages with `JobPosting` or `BroadcastEvent` structured data; a general "request indexing" command would invite unsupported use. Google retired the Mobile-Friendly Test API on 2023-12-01. ## Known differences and choices [#known-differences-and-choices] * **Metadata spelling.** The method reference spells the incompleteness fields `first_incomplete_date` and `first_incomplete_hour`; discovery and Google's Go client use camelCase. `gsc` accepts both and always prints snake_case. * **Search appearance.** The method reference allows any dimension combination; the extraction guide says `searchAppearance` cannot be grouped with other dimensions. `gsc` warns instead of rejecting. `gsc top appearance` lists the values to filter on. * **Retention.** 16 months (10 days for hourly data) is data availability, not a request rule: explicit dates beyond it produce a warning, and `--last` and comparison periods are clipped with a notice. * **Enums.** Discovery lists some enums in upper case; `gsc` sends the method-reference spellings (`byPage`, `hourly_all`) and accepts any case on input. Returned values are kept as Google sends them. * **Validation.** Before any request, `gsc` checks only what Google documents: date order, `hour` only with `hourly_all`, duplicate dimensions, aggregation rules, and regex syntax. Anything else is left to the server, whose error is shown as it is. ## Not covered [#not-covered] PageSpeed Insights, the Chrome UX Report, and Search Console's bulk data export to BigQuery are separate services with their own access and semantics, so `gsc` does not cover them. `gsc export` is the API-side alternative to the BigQuery export. ## Platforms [#platforms] Prebuilt binaries for macOS, Linux, and Windows on `amd64` and `arm64`. CI runs the tests on all three operating systems. Building from source needs Go 1.26 or later. # Reading the data (https://projects.piyushgambhir.com/gsc-cli/docs/data) Search Console data has rules that are easy to misread. `gsc` states them in its output, and this page explains why they matter. ## Dates are Pacific Time [#dates-are-pacific-time] Search Console reports days in Pacific Time, and every date in `gsc` (`--start`, `--end`, and the `start` and `end` fields) is a Pacific Time date, inclusive. `--last 28d` ends at the latest day with data for the chosen data state; one small extra request finds that day, and passing `--end` skips it. Month windows that end on a month's last day cover whole calendar months. ## Final and preliminary data [#final-and-preliminary-data] Recent days start out **preliminary** and become **final** later, usually after two to three days. * `--data-state final` is the default, so numbers are final unless you ask otherwise. * `--data-state all` includes preliminary days. Never present those numbers as final: they can still change. JSON reports `first_incomplete_date` when Google provides it. * `--data-state hourly_all` is required for the `hour` dimension and covers about the last 10 days. * `gsc freshness` reports the latest final date and the latest preliminary date, so you can check before trusting the last few days. ## Rows are not totals [#rows-are-not-totals] Google does not return every query: * **Anonymized queries are withheld.** Queries too rare to show for privacy reasons count toward the totals but never appear as rows. * **Rows are capped.** Google exposes at most 50,000 rows per day per search type, however you page. So **never add up query or page rows to get site totals.** Use `gsc performance`, a query with no dimensions whose totals match the Performance report chart, anonymized queries included. In JSON, `complete: true` only means pagination reached the end of what Google exposes; it never means every query is there. ## Row limits [#row-limits] `--limit` defaults to 1,000 rows (25 for `top`). Larger limits are fetched in pages of up to 25,000 rows, and `--all` fetches every row Google exposes. For the full history of a property, `gsc export` fetches day by day, as Google's extraction guide recommends. ## Comparisons [#comparisons] `--compare previous` runs a second query for the period just before; `--compare yoy` uses the same dates a year earlier. * A row missing from one period is `null`, **not zero**. Results are capped, so a row can be missing even when the query had traffic. * CTR changes are in percentage points. Percent change is `null` when the earlier value is zero. * `insights new-queries` and `lost-queries` describe rows newly exposed or no longer exposed within the fetched rows, not changes in ranking. ## Retention [#retention] Search Console keeps 16 months of daily data and about 10 days of hourly data. Explicit dates beyond that produce a warning, and `--last` and comparison periods are clipped with a notice. ## URL Inspection is the indexed version [#url-inspection-is-the-indexed-version] `gsc inspect` reports what Google has **indexed**, as last crawled. It is not a live test of the page as it is now, and the API cannot request indexing. Each inspection counts against a quota of 2,000 per property per day and 600 per minute; `inspect` paces itself and stops cleanly at the quota, and `--max` caps how many URLs it spends. ## Quotas [#quotas] Search Analytics allows 1,200 queries per minute per property, plus unpublished load quotas: grouping by page and query together over long ranges is expensive. When a quota is hit, narrow the date range or the dimensions and wait about 15 minutes. `gsc` never retries automatically; only `gsc export --retry N` opts in. ## Features without an API [#features-without-an-api] Some Search Console reports have no public API, so `gsc` cannot read them: the branded query filter, query groups and the Insights report, generative AI performance for AI Overviews and AI Mode (that traffic is included in ordinary totals but cannot be separated), Core Web Vitals, Page indexing, manual actions, links, and crawl stats. The [API coverage](https://projects.piyushgambhir.com/gsc-cli/docs/reference/api-coverage) page has the full list. ## Corrections from Google [#corrections-from-google] Google occasionally corrects historical data, for example an impression-logging issue affecting 2025-05-13 to 2026-04-27. Check Google's [data anomalies log](https://support.google.com/webmasters/answer/6211453) before attributing a historical change to your site or to `gsc`. # Introduction (https://projects.piyushgambhir.com/gsc-cli/docs) **gsc-cli** (`gsc`) is a command-line interface for **Google Search Console**. Sign in with your Google account, then read search performance, see how Google indexed your pages, manage sitemaps and properties, and export your data from your terminal. It is built for people and coding agents alike. Every command can print JSON, writes can be previewed with `--dry-run`, and `--read-only` blocks every change. The binary is `gsc`. `gsc auth login` opens your browser once and keeps the login in your OS keychain; access tokens refresh automatically after that. ## What it covers [#what-it-covers] * **Search performance:** totals that match the Performance report, top queries, pages, countries, devices, and search appearances, and trends by hour, day, week, or month. * **Every Search Analytics option:** seven dimensions, six search types, filters with regular expressions, final, preliminary, and hourly data, and every row Google exposes, well past the web UI's 1,000-row table. * **Comparisons:** any period against the one before it or the same dates a year earlier, with rows missing from one period reported as `null`, not zero. * **URL Inspection:** Google's indexed view of one URL or a batch, paced under the quota. * **Sitemaps and properties:** list, check, submit, and delete sitemaps; list, add, and remove properties. * **Bulk export:** day-by-day CSV or NDJSON files that resume after an interruption. * **Insights:** striking-distance queries, low CTR for the position, cannibalization, decliners, and new and lost queries, computed locally with their thresholds printed. * **Anything else:** `gsc api` sends an authenticated request to any Search Console API method. Each of the 10 active Search Console API methods has a command. The [API coverage](https://projects.piyushgambhir.com/gsc-cli/docs/reference/api-coverage) page maps them one by one. Search Console data has rules that are easy to miss, such as preliminary days, anonymized queries, and row caps; [Reading the data](https://projects.piyushgambhir.com/gsc-cli/docs/data) explains them. ## Next steps [#next-steps] **Independent project.** Not affiliated with, endorsed by, or sponsored by Google. Google and Google Search Console are trademarks of Google LLC, named here only to identify the service this tool works with. # Installation (https://projects.piyushgambhir.com/gsc-cli/docs/installation) ## Install script (recommended) [#install-script-recommended] For macOS and Linux. The script downloads the latest release for your OS and architecture, checks its SHA-256 checksum, and installs `gsc` to `~/.local/bin`: ```bash curl -fsSL https://raw.githubusercontent.com/piyush-gambhir/gsc-cli/main/install.sh | sh ``` Pin a version or change the install directory: ```bash curl -fsSL https://raw.githubusercontent.com/piyush-gambhir/gsc-cli/main/install.sh | VERSION=v0.1.5 sh curl -fsSL https://raw.githubusercontent.com/piyush-gambhir/gsc-cli/main/install.sh | INSTALL_DIR=/usr/local/bin sh ``` Ghostscript and Gambit Scheme also install a program named `gsc`. The installer warns when a different `gsc` comes first on your `PATH`, and `gsc doctor` checks for it too. ## Download a release manually [#download-a-release-manually] Each [GitHub release](https://github.com/piyush-gambhir/gsc-cli/releases) has `gsc-cli__.tar.gz` archives for `darwin` and `linux` (`amd64` or `arm64`) and `gsc-cli_windows_.zip` for Windows. Extract `gsc` and put it on your `PATH`. `checksums.txt` lists the SHA-256 of every archive. ## Verify a download [#verify-a-download] Releases are immutable once published. Every archive ships with an SBOM and a signed build-provenance attestation, so you can confirm it was built by the repository's release workflow: ```bash gh attestation verify gsc-cli_darwin_arm64.tar.gz --repo piyush-gambhir/gsc-cli ``` ## Build from source [#build-from-source] Requires Go 1.26 or later: ```bash git clone https://github.com/piyush-gambhir/gsc-cli.git cd gsc-cli/cli-go make install # installs to $(go env GOPATH)/bin, or INSTALL_DIR=... ``` A source build has no built-in OAuth client, so `gsc auth login` needs your own ([`--client-secret-file`](https://projects.piyushgambhir.com/gsc-cli/docs/authentication#your-own-oauth-client), or `GSC_CLIENT_ID` and `GSC_CLIENT_SECRET`). Service accounts, Application Default Credentials, and access tokens work as usual. `gsc version -o json` reports `builtin_oauth_client`. ## Check and update [#check-and-update] ```bash gsc version # build info, plus the latest version from the last release check gsc doctor # configuration, credentials, keychain, scopes, and PATH; no API calls gsc update --check # is a newer release out? (-o json for scripts) gsc update # asks, then installs it after SHA-256 verification ``` `gsc update` works on macOS, Linux, and Windows. It downloads the release archive for your platform, checks it against the release's `checksums.txt`, and replaces the running executable. On Windows the old executable is renamed to `gsc.exe.old` and deleted the next time `gsc` starts. `--yes` skips the question, and `--no-input` requires `--yes`. If the install directory is not writable, the update stops and the old binary keeps working: re-run with `sudo`, or reinstall with the install script into a directory you own. A `gsc` in a Go bin directory (`$GOBIN`, `$GOPATH/bin`, or `~/go/bin`) came from a source build, so `gsc update` tells you to run `git pull && make install` in your checkout instead of replacing it. `--read-only` blocks `gsc update` but still allows `gsc update --check`. ### Update notice [#update-notice] In an interactive terminal, `gsc` checks the github.com release page for a new release at most once a day (not the GitHub API, so its per-IP rate limit never applies). When one is out, it prints a notice on stderr after the command's output, at most once a day per version: ```text A new version of gsc is available: v0.1.4 -> v0.1.5 Update with: gsc update Release notes: https://github.com/piyush-gambhir/gsc-cli/releases/tag/v0.1.5 ``` Only the one command a day that runs the check waits for its answer, at most 1 second after its output; every other command uses the cached answer and never waits. `gsc update` and `gsc update --check` store what they find in the same cache. The check never runs (no network, no output) when: * stderr is not a terminal (pipes, scripts, coding agents), * `CI` is set, * `--quiet` or `GSC_QUIET=1` is on, * `GSC_NO_UPDATE_NOTIFIER` or `NO_UPDATE_NOTIFIER` is set to any value, or * the command is `update`, `version`, `completion`, or `help`. To turn it off for good, add `export GSC_NO_UPDATE_NOTIFIER=1` to your shell profile. # Output formats (https://projects.piyushgambhir.com/gsc-cli/docs/output-formats) ## Formats [#formats] `-o table` is the default. `-o json` and `-o yaml` work on every command that returns data, and `-o csv` works on row-shaped results such as Search Analytics, `top`, `trend`, insights, `inspect`, and `sites list`; other commands reject it with a message. CSV is one flat row per result, so nested fields (a sitemap's `contents`, URL Inspection details such as referring URLs) appear only in JSON and YAML. ```bash gsc sites list # table gsc top queries -o json # JSON for scripts and agents gsc query -d page --limit 5000 -o csv > pages.csv # spreadsheet-ready ``` Tables show CTR as a percentage and positions to two decimals, shorten timestamps, and keep wide results readable: comparisons show current values and changes, sitemaps omit `isSitemapsIndex`, and inspections show the URL, verdict, coverage, last crawl, canonical, and error. JSON, YAML, and CSV keep the raw values: `ctr` is a ratio such as `0.0249`. In CSV, text starting with `=`, `+`, `-`, or `@` gets a leading apostrophe so a spreadsheet never runs a search query as a formula. ## stdout and stderr [#stdout-and-stderr] Data goes to **stdout**. Notes, warnings, and errors go to **stderr**, so piping stdout always gives you clean data. `--quiet` drops informational notes, and `--verbose` logs each request's method, URL, and HTTP status to stderr (never tokens or bodies). ## The Search Analytics envelope [#the-search-analytics-envelope] `query`, `performance`, `top`, and `trend` print an envelope in JSON and YAML, so the numbers carry what you need to interpret them: ```json { "site": "sc-domain:example.com", "start": "2026-09-03", "end": "2026-09-30", "type": "web", "data_state": "final", "dimensions": ["query", "page"], "aggregation": "byPage", "first_incomplete_date": null, "first_incomplete_hour": null, "metadata_returned": false, "row_count": 1000, "complete": false, "requests": 1, "rollup": null, "rows": [ { "query": "running shoes", "page": "https://example.com/shoes", "clicks": 120, "impressions": 4810, "ctr": 0.0249, "position": 7.42 } ] } ``` * `start` and `end` are inclusive dates in Pacific Time, Search Console's timezone. * `complete` is about pagination only: `true` means every row Google exposes for the request was fetched. It never means every query, because Google withholds anonymized queries. * `metadata_returned` says whether Google sent the `first_incomplete_date` or `first_incomplete_hour` fields. Google sends them only for `--data-state all` with date grouping or `hourly_all` with hour grouping, so `null` with `metadata_returned: false` means "not provided", not "complete". * `aggregation` is the aggregation Google reports it used. `requests` counts the API requests made. * `rollup` describes a local week or month rollup from `trend --by week|month`. ## Comparisons [#comparisons] With `--compare previous` or `--compare yoy`, `current` and `previous` each carry their own envelope fields, and every row adds `_prev`, `_delta`, and `_pct` columns: ```json { "mode": "previous", "current": { "start": "2026-09-03", "end": "2026-09-30", "row_count": 500, "complete": false }, "previous": { "start": "2026-08-06", "end": "2026-09-02", "row_count": 500, "complete": false }, "dimensions": ["query"], "note": "null means the row was not returned for that period (not zero); ctr_delta_pp is in percentage points", "rows": [ { "query": "running shoes", "clicks": 120, "clicks_prev": null, "clicks_delta": null, "clicks_pct": null } ] } ``` The example is abridged. A row missing from one period is `null`, never zero. `clicks_pct` and `impressions_pct` are `null` when the earlier value is zero, CTR changes are in percentage points (`ctr_delta_pp`), and position changes are absolute differences (`position_delta`). ## Other commands [#other-commands] * `freshness` prints `latest_final_date`, `latest_preliminary_date`, and `first_incomplete_date`. * Insights print the insight name, a `note` on how it was computed, the `thresholds` used, the source queries, and the rows. * `inspect` prints `site`, `requested`, `inspected`, a `note`, and one entry per URL with Google's full inspection result or that URL's error. `stopped_reason` appears when a quota or a rejected login stopped the batch early. * `sites` and `sitemaps` commands keep Google's field names (`siteUrl`, `permissionLevel`). ## Errors [#errors] Every failure exits with status `1`, like the other CLIs in this suite. With `-o json` or `-o yaml`, stderr carries a structured error: ```json { "error": "Search Console API: User does not have sufficient permission for site 'sc-domain:example.com'. See also: https://support.google.com/webmasters/answer/2451999. (HTTP 403, forbidden). The signed-in identity lacks access to this property; check gsc sites list or add the user in Search Console > Settings > Users and permissions", "kind": "permission", "reason": "forbidden", "status": 403 } ``` `kind` classifies every failure so agents can branch without parsing the message: | `kind` | Meaning | | --------------------------------- | ------------------------------------------------------ | | `usage` | Unknown command or flag, or wrong arguments | | `auth` | Not logged in, or the saved credentials no longer work | | `permission` | The identity lacks access to the property (HTTP 403) | | `not_found` | Google reports no such property or sitemap (HTTP 404) | | `rate_limit` | Quota or rate limit (HTTP 429); wait for `retry_after` | | `invalid_request` | Google rejected the request (other HTTP 4xx) | | `server` | Google failed (HTTP 5xx) | | `read_only` | `--read-only` or a read-only scope blocked a write | | `confirmation_required` | A destructive command needs `--yes` without a terminal | | `timeout`, `network`, `cancelled` | The request did not complete | | `error` | Anything else | Three commands write stdout and still exit `1`, so the work done so far is not lost: `gsc doctor` prints its check report when a check fails, `gsc inspect` prints the URLs it inspected before a quota stop, and `gsc export` prints its summary when it stops early (rerun it to resume). `reason` is Google's error reason when it sends one, and `retry_after` is added when Google sends a `Retry-After` header. The message ends with a hint for the common cases: | Situation | What to do | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | HTTP 401 | The access token was rejected; run `gsc auth login` | | HTTP 403, no permission for the property | Check `gsc sites list`, or add the user or service account in Search Console | | HTTP 403, API not enabled | Enable the Google Search Console API in the OAuth client's Google Cloud project | | Quota or rate limit (403 or 429) | Narrow the date range, avoid grouping by page and query together, and wait about 15 minutes. `gsc` never retries automatically | Tokens and client secrets never appear in errors or logs. # Quick start (https://projects.piyushgambhir.com/gsc-cli/docs/quickstart) ## 1. Log in [#1-log-in] ```bash gsc auth login ``` Your browser opens Google's consent page. Approve access, and `gsc` saves the login in your OS keychain, lists your properties, and picks a default site: your only property, or the one you choose from a list. On a machine without a browser, use `gsc auth login --no-browser`; [Authentication](https://projects.piyushgambhir.com/gsc-cli/docs/authentication) covers service accounts and CI. ## 2. Check your properties [#2-check-your-properties] ```bash gsc sites list # properties and your permission level gsc sites use sc-domain:example.com # change the default property gsc auth status # the active credential and property; no network call ``` Properties are either domain properties (`sc-domain:example.com`) or URL prefixes (`https://www.example.com/`), exactly as `sites list` prints them. `-s` picks one for a single command, and a bare host such as `example.com` is matched against your properties: a domain property wins, otherwise a single URL-prefix match. ## 3. Read your performance [#3-read-your-performance] ```bash gsc freshness # the latest final and preliminary dates gsc performance --last 28d --compare previous # totals, and the change from the 28 days before gsc top queries --last 3m --limit 50 gsc trend --last 16m --by month ``` ## 4. Dig into the rows [#4-dig-into-the-rows] ```bash gsc query -d query -d page --filter 'page =~ ^https://www.example.com/blog/' --limit 5000 -o csv > blog.csv gsc insights striking-distance --last 28d ``` ## 5. Check pages and sitemaps [#5-check-pages-and-sitemaps] ```bash gsc inspect https://www.example.com/pricing gsc sitemaps list gsc sitemaps submit https://www.example.com/sitemap.xml --dry-run # see the request first ``` Commands use final data by default, and the last two or three days are usually still preliminary, so `--last 28d` ends at the latest final day. `gsc freshness` shows where that is, and [Reading the data](https://projects.piyushgambhir.com/gsc-cli/docs/data) explains the rest. ## Discovering commands [#discovering-commands] ```bash gsc --help gsc query --help gsc doctor # configuration, credentials, keychain, scopes, and PATH ``` # API coverage (https://projects.piyushgambhir.com/gsc-cli/docs/reference/api-coverage) Every method in the pinned Google API snapshots, mapped to the `gsc` commands that call it, or listed as skipped with the reason. The snapshots (discovery documents, revisions `20261005` (Search Console) and `20260923` (Indexing), captured 2026-10-07) are vendored in `cli-go/internal/coverage/testdata/`; see [Compatibility](https://projects.piyushgambhir.com/gsc-cli/docs/compatibility) for hashes and the refresh procedure. This page is checked by `TestAPICoverage` (`cli-go/cmd/coverage_test.go`): the test fails if a snapshot method is missing from the coverage table, if a mapped command does not exist, or if this page does not list a method or command. Summary: **10 of 10 active Search Console methods implemented**, 1 retired method skipped, 2 Indexing API methods skipped by decision. ## Search Console API v1 [#search-console-api-v1] Host `https://searchconsole.googleapis.com`. Read methods accept either OAuth scope; write methods need the full `webmasters` scope. | Method | HTTP and path | Status | Commands | | ------------------------------------------------------ | ---------------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `webmasters.sites.list` | `GET webmasters/v3/sites` | Implemented | `gsc sites list` (also used once after login and to resolve bare hosts) | | `webmasters.sites.get` | `GET webmasters/v3/sites/{siteUrl}` | Implemented | `gsc sites get` | | `webmasters.sites.add` | `PUT webmasters/v3/sites/{siteUrl}` | Implemented (write) | `gsc sites add` | | `webmasters.sites.delete` | `DELETE webmasters/v3/sites/{siteUrl}` | Implemented (write) | `gsc sites remove` | | `webmasters.sitemaps.list` | `GET webmasters/v3/sites/{siteUrl}/sitemaps` | Implemented | `gsc sitemaps list` | | `webmasters.sitemaps.get` | `GET webmasters/v3/sites/{siteUrl}/sitemaps/{feedpath}` | Implemented | `gsc sitemaps get` | | `webmasters.sitemaps.submit` | `PUT webmasters/v3/sites/{siteUrl}/sitemaps/{feedpath}` | Implemented (write) | `gsc sitemaps submit` | | `webmasters.sitemaps.delete` | `DELETE webmasters/v3/sites/{siteUrl}/sitemaps/{feedpath}` | Implemented (write) | `gsc sitemaps delete` | | `webmasters.searchanalytics.query` | `POST webmasters/v3/sites/{siteUrl}/searchAnalytics/query` | Implemented (read, despite POST) | `gsc query`, `gsc performance`, `gsc top queries`, `gsc top pages`, `gsc top countries`, `gsc top devices`, `gsc top appearance`, `gsc trend`, `gsc freshness`, `gsc export`, `gsc insights striking-distance`, `gsc insights low-ctr`, `gsc insights cannibalization`, `gsc insights decliners`, `gsc insights new-queries`, `gsc insights lost-queries` | | `searchconsole.urlInspection.index.inspect` | `POST v1/urlInspection/index:inspect` | Implemented (read, despite POST) | `gsc inspect` | | `searchconsole.urlTestingTools.mobileFriendlyTest.run` | `POST v1/urlTestingTools/mobileFriendlyTest:run` | Retired, skipped | none. Google retired the Mobile-Friendly Test API on 2023-12-01; the method is still in the discovery document but no longer operates. | Every request field of `searchanalytics.query` is reachable from `gsc query`: dimensions (`date`, `hour`, `query`, `page`, `country`, `device`, `searchAppearance`), `type`, `dataState` (`final`, `all`, `hourly_all`), `aggregationType`, `dimensionFilterGroups` with all six operators, `rowLimit`, and `startRow` (pagination). `--request-file` accepts a raw body for anything flags do not express. `gsc api METHOD PATH` can call any of these methods directly, and any method Google adds later. `gsc api methods -o json` prints this table offline (method, HTTP verb, path, read or write effect, status, and commands). ## Indexing API v3 (skipped by decision) [#indexing-api-v3-skipped-by-decision] Host `https://indexing.googleapis.com`, a separate service from Search Console. | Method | HTTP and path | Status | Reason | | --------------------------------------- | ---------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `indexing.urlNotifications.publish` | `POST v3/urlNotifications:publish` | Skipped | Google limits the Indexing API to pages with `JobPosting` or `BroadcastEvent` (in `VideoObject`) structured data, requires an owner-level service account, and enforces against misuse. A general "request indexing" command would invite unsupported use. | | `indexing.urlNotifications.getMetadata` | `GET v3/urlNotifications/metadata` | Skipped | Same service and restrictions as above. | If a site you own qualifies, the plan (PLAN.md, decision 4) describes adding it later with a structured-data eligibility check. ## Related Google APIs and data sources [#related-google-apis-and-data-sources] | Source | How it is handled | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PageSpeed Insights API (`pagespeedonline/v5/runPagespeed`) | Not covered. It analyzes any URL with Lighthouse, uses an API key, and is unrelated to Search Console property data. A separate performance tool fits it better. | | Chrome UX Report (CrUX) API | Not covered. Real-user field data with an API key and its own quotas; different semantics from Search Console. | | Search Console bulk data export to BigQuery | Not covered. Owners turn it on in the Search Console UI; the data is then queried in BigQuery (`bq`). It avoids the API's row limits but does not backfill history. `gsc export` is the API-side alternative. | | Search Console URL Inspection live test, "Request indexing" | Not available in any public API. `gsc inspect` reports the indexed version only. | ## Search Console features without an API (as of the snapshot) [#search-console-features-without-an-api-as-of-the-snapshot] These appear in the Search Console UI but have no public API method or field, so `gsc` cannot report them: * Branded and non-branded query filter (2025-11, worldwide rollout 2026-03). A regex filter on `query` can approximate it, but that is your classification, not Google's. * Query groups in Search Console Insights (2025-10) and the Insights report itself (2025-06). * Generative AI performance reports for AI Overviews and AI Mode in Search and Discover (2026-06, worldwide 2026-08-31). This traffic is included in ordinary performance totals but cannot be separated. * Web multimodal reporting for Lens, Circle to Search, and image search (2026-09). * Platform properties for Instagram, TikTok, X, and YouTube (2026-07). They work only as far as `gsc sites list` returns them; their identifiers are not documented for the API. * Weekly and monthly UI views (2025-12). `gsc trend --by week` and `--by month` roll up daily rows locally instead. * Core Web Vitals, Page indexing, Manual actions, Security issues, Links, Removals, Crawl stats, and user and permission management reports. # Command reference (https://projects.piyushgambhir.com/gsc-cli/docs/reference/commands) Generated from the `gsc` command tree on `main`. The [command guides](https://projects.piyushgambhir.com/gsc-cli/docs/commands) explain when to use each group. Global flags apply to every command. ```text --access-token string Use this OAuth access token (prefer GSC_ACCESS_TOKEN) --credentials string Google credentials JSON: service account, authorized user, or external account (or GSC_CREDENTIALS) --dry-run For write commands: print the request and send nothing --no-input Never prompt or open a browser -o, --output string Output format: table, json, yaml, csv (default "table") --profile string Named profile (or GSC_PROFILE) -q, --quiet Suppress informational stderr output --read-only Block remote writes, local credential changes, and self-update -s, --site string Property: sc-domain:example.com, https://www.example.com/, or a bare host (or GSC_SITE) --timeout duration HTTP request timeout (default 30s) -v, --verbose Log request method, URL, and status to stderr (never tokens or bodies) -y, --yes Confirm destructive commands and updates without prompting ``` ## gsc [#gsc] Google Search Console from your terminal Search performance, URL inspection, sitemaps, and properties from Google Search Console. Run `gsc auth login` once; it opens your browser and remembers the login. Data goes to stdout and diagnostics to stderr. --read-only blocks every write. ```text gsc [flags] ``` ```text -h, --help help for gsc ``` ## gsc api [#gsc-api] Send an authenticated request to the Search Console API (escape hatch) PATH is relative to [https://searchconsole.googleapis.com](https://searchconsole.googleapis.com) and must already be URL-escaped. GET and the read-only POSTs (searchAnalytics/query, urlInspection/index:inspect) are reads; every other request is treated as a write: blocked by --read-only, printed instead of sent with --dry-run, and confirmed interactively (or with --yes) before it is sent. ```text gsc api METHOD PATH [flags] ``` ```bash gsc api GET /webmasters/v3/sites gsc api POST /webmasters/v3/sites/sc-domain%3Aexample.com/searchAnalytics/query --data '{"startDate":"2026-09-01","endDate":"2026-09-30"}' gsc api PUT /webmasters/v3/sites/sc-domain%3Aexample.com/sitemaps/https%3A%2F%2Fexample.com%2Fsitemap.xml --dry-run ``` ```text --data string JSON request body, @file, or - for stdin -h, --help help for api ``` ## gsc api methods [#gsc-api-methods] List every Search Console API method: HTTP verb, read or write effect, status, and the commands that call it Offline: reads the method registry pinned to the vendored discovery snapshot. path is what gsc api takes; status is implemented, retired, or skipped (with a note saying why). ```text gsc api methods [flags] ``` ```bash gsc api methods gsc api methods -o json ``` ```text -h, --help help for methods ``` ## gsc auth [#gsc-auth] Log in, inspect credentials, and manage profiles ```text gsc auth [flags] ``` ```text -h, --help help for auth ``` ## gsc auth list [#gsc-auth-list] List saved profiles (no secrets) ```text gsc auth list [flags] ``` ```bash gsc auth list gsc auth list -o json ``` ```text -h, --help help for list ``` ## gsc auth login [#gsc-auth-login] Sign in with Google (opens your browser) or save another credential type With no flags, opens Google's consent page in your browser and saves the login in the OS keychain. Tokens refresh automatically afterwards. --no-browser prints a URL to approve on any device and asks you to paste the final redirected URL back. --service-account and --adc save non-interactive credentials for CI. See docs/auth.md for every method. ```text gsc auth login [flags] ``` ```bash gsc auth login gsc auth login --profile work --scope readonly gsc auth login --no-browser gsc auth login --client-secret-file client_secret.json gsc auth login --profile ci --service-account key.json gsc auth login --profile gcloud --adc --impersonate reporting@my-project.iam.gserviceaccount.com ``` ```text --adc Use Application Default Credentials (gcloud, workload identity federation, metadata server) --client-secret-file string Use your own Desktop OAuth client (JSON from Google Cloud Console) -h, --help help for login --impersonate string With --adc: service account email to impersonate through IAM Credentials --insecure-storage Store the login in a 0600 plaintext file instead of the OS keychain --no-browser Headless login: approve on any device, then paste the redirected URL --no-verify Skip listing properties after login (no API call) --scope string Search Console access to request: full or readonly (default "full") --service-account string Save a service account JSON key file for this profile --subject string With --service-account: Workspace user to impersonate (domain-wide delegation) ``` ## gsc auth logout [#gsc-auth-logout] Remove a profile and its saved login from this machine Deletes the profile and its stored token locally. --revoke also revokes access at Google, which signs this Google account out of every machine and profile that uses the same OAuth client project; it asks for confirmation unless --yes is given. ```text gsc auth logout [flags] ``` ```bash gsc auth logout gsc auth logout --profile work --revoke --yes ``` ```text -h, --help help for logout --revoke Also revoke access at Google (signs out every machine using the same OAuth client) ``` ## gsc auth status [#gsc-auth-status] Show which credential is in use, without revealing it Reads local configuration only. --verify additionally lists properties with the credential (one API call). ```text gsc auth status [flags] ``` ```bash gsc auth status gsc auth status --verify -o json ``` ```text -h, --help help for status --verify Check the credential by listing properties (one API call) ``` ## gsc auth token [#gsc-auth-token] Print a fresh access token for debugging (keep it secret) ```text gsc auth token [flags] ``` ```bash curl -H "Authorization: Bearer $(gsc auth token)" https://searchconsole.googleapis.com/webmasters/v3/sites gsc auth token --profile ci ``` ```text -h, --help help for token ``` ## gsc auth use [#gsc-auth-use] Set the default profile ```text gsc auth use NAME [flags] ``` ```bash gsc auth use work ``` ```text -h, --help help for use ``` ## gsc commands [#gsc-commands] Describe every command, its flags, and whether it reads or writes (for agents) Offline: lists every runnable command with its usage, flags and defaults, effect (read, remote_write, or local_write), whether it may prompt, the Search Console API methods it exists to call, and examples. Global flags are listed once. Use -o json for the full description. Some calls are incidental and not listed: a bare host given to -s, --verify, doctor --online, and login each list properties once (webmasters.sites.list). ```text gsc commands [flags] ``` ```bash gsc commands gsc commands -o json | jq -r '.commands[] | select(.effect != "read") | .command' ``` ```text -h, --help help for commands ``` ## gsc completion [#gsc-completion] Generate shell completion script ```text gsc completion [bash|zsh|fish|powershell] [flags] ``` ```bash source <(gsc completion zsh) gsc completion bash > /usr/local/etc/bash_completion.d/gsc gsc completion fish > ~/.config/fish/completions/gsc.fish ``` ```text -h, --help help for completion ``` ## gsc config [#gsc-config] Inspect configuration and select profiles ```text gsc config [flags] ``` ```text -h, --help help for config ``` ## gsc config list-profiles [#gsc-config-list-profiles] List saved profiles (no secrets) ```text gsc config list-profiles [flags] ``` ```bash gsc config list-profiles gsc config list-profiles -o json ``` ```text -h, --help help for list-profiles ``` ## gsc config show [#gsc-config-show] Show which credential is in use, without revealing it Reads local configuration only. --verify additionally lists properties with the credential (one API call). ```text gsc config show [flags] ``` ```bash gsc config show gsc config show --verify -o json ``` ```text -h, --help help for show --verify Check the credential by listing properties (one API call) ``` ## gsc config use-profile [#gsc-config-use-profile] Set the default profile ```text gsc config use-profile NAME [flags] ``` ```bash gsc config use-profile work ``` ```text -h, --help help for use-profile ``` ## gsc doctor [#gsc-doctor] Check configuration, credentials, and PATH (local unless --online) ```text gsc doctor [flags] ``` ```bash gsc doctor gsc doctor --online -o json ``` ```text -h, --help help for doctor --online Also call the API once to list properties ``` ## gsc export [#gsc-export] Export Search Analytics day by day to CSV or NDJSON files (resumable) Splits the range into days, as Google's extraction guide recommends, fetches every exposed row for each day, and writes one file per day through a temp file and rename. manifest.json records finished days; rerun the same command to resume. Days after the latest final date, and days Google marks incomplete, are recorded as preliminary and re-fetched on resume. No automatic retries unless --retry is given. ```text gsc export [flags] ``` ```bash gsc export --last 16m -d query -d page --out ./gsc-export gsc export --start 2026-01-01 --end 2026-03-31 -d page --format ndjson --out ./q1 ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --data-state string final (default), all (includes preliminary data), or hourly_all -d, --dimension strings Group by: date, hour, query, page, country, device, searchAppearance (repeat or comma list) --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND --format string File format: csv or ndjson (default "csv") -h, --help help for export --interval duration Minimum time between the start of one day and the next (a day's pages are fetched back to back) (default 250ms) --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --out string Output directory (created if missing) --restart Discard a manifest written for a different request --retry int Retry quota and server errors this many times per day, with backoff (default 0) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc freshness [#gsc-freshness] Latest dates with final and with preliminary data Two small date-grouped queries over the last 10 days: one for final data and one including preliminary data. Final data usually lags two to three days. ```text gsc freshness [flags] ``` ```bash gsc freshness gsc freshness --type discover -o json ``` ```text -h, --help help for freshness --type string Search type (default web) ``` ## gsc insights [#gsc-insights] Opportunities computed locally from explicit queries (thresholds are printed) Each insight runs ordinary Search Analytics queries and computes the result locally. The thresholds used are printed on stderr and included in JSON output. Results reflect rows Google exposes; anonymized queries are never included. ```text gsc insights [flags] ``` ```text -h, --help help for insights ``` ## gsc insights cannibalization [#gsc-insights-cannibalization] Queries where two or more pages split the impressions ```text gsc insights cannibalization [flags] ``` ```bash gsc insights cannibalization --last 3m gsc insights cannibalization --min-share 0.2 --filter 'query ~ pricing' ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for cannibalization --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum queries to show (default 25) --min-impressions float Minimum total impressions for a query (default 100) --min-share float Minimum share of a query's impressions for a page to count as competing (default 0.1) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc insights decliners [#gsc-insights-decliners] Queries or pages that lost the most clicks versus the comparison period ```text gsc insights decliners [flags] ``` ```bash gsc insights decliners --compare yoy gsc insights decliners --by page --min-clicks 20 -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --by string Compare queries or pages (default "query") --compare string Comparison period: previous or yoy (default "previous") --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for decliners --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to show (default 50) --min-clicks float Minimum clicks in the comparison period (default 5) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc insights lost-queries [#gsc-insights-lost-queries] Queries or pages returned in the comparison period but not now (no longer exposed) ```text gsc insights lost-queries [flags] ``` ```bash gsc insights lost-queries --last 7d gsc insights lost-queries --by page -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --by string Compare queries or pages (default "query") --compare string Comparison period: previous or yoy (default "previous") --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for lost-queries --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to show (default 50) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc insights low-ctr [#gsc-insights-low-ctr] Queries whose CTR is well below this site's median for the same position ```text gsc insights low-ctr [flags] ``` ```bash gsc insights low-ctr gsc insights low-ctr --factor 0.4 --min-impressions 200 -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --factor float Flag queries with CTR below this fraction of the bucket median (default 0.5) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for low-ctr --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to show (default 50) --min-impressions float Minimum impressions for a query to count (default 100) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc insights new-queries [#gsc-insights-new-queries] Queries or pages returned now but not in the comparison period (newly exposed) ```text gsc insights new-queries [flags] ``` ```bash gsc insights new-queries --last 7d gsc insights new-queries --by page --compare yoy ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --by string Compare queries or pages (default "query") --compare string Comparison period: previous or yoy (default "previous") --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for new-queries --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to show (default 50) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc insights striking-distance [#gsc-insights-striking-distance] Queries ranking just off the top (average position 4 to 20) with real impressions ```text gsc insights striking-distance [flags] ``` ```bash gsc insights striking-distance gsc insights striking-distance --min-impressions 500 --max-position 15 -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for striking-distance --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to show (default 50) --max-position float Highest average position to include (default 20) --min-impressions float Minimum impressions in the period (default 100) --min-position float Lowest average position to include (default 4) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc inspect [#gsc-inspect] URL Inspection: Google's indexed view of URLs (not a live test) Calls the URL Inspection API for each URL. It reports the indexed version only: there is no live test and no request-indexing in the API. Google allows 2,000 inspections per property per day and 600 per minute; batches are paced below that and stop cleanly when a quota is reached. ```text gsc inspect [URL...] [flags] ``` ```bash gsc inspect https://www.example.com/pricing gsc inspect --file urls.txt -o json cat urls.txt | gsc inspect --file - --max 200 ``` ```text --file string Read URLs from a file, one per line (- for stdin) -h, --help help for inspect --interval duration Pause between inspections (stays under 600 per minute) (default 120ms) --language string BCP 47 language for issue messages (default en-US) --max int Inspect at most this many URLs (the per-property daily quota is 2,000) (default 2000) ``` ## gsc login [#gsc-login] Alias of auth login With no flags, opens Google's consent page in your browser and saves the login in the OS keychain. Tokens refresh automatically afterwards. --no-browser prints a URL to approve on any device and asks you to paste the final redirected URL back. --service-account and --adc save non-interactive credentials for CI. See docs/auth.md for every method. ```text gsc login [flags] ``` ```bash gsc login gsc login --no-browser gsc login --profile work --scope readonly ``` ```text --adc Use Application Default Credentials (gcloud, workload identity federation, metadata server) --client-secret-file string Use your own Desktop OAuth client (JSON from Google Cloud Console) -h, --help help for login --impersonate string With --adc: service account email to impersonate through IAM Credentials --insecure-storage Store the login in a 0600 plaintext file instead of the OS keychain --no-browser Headless login: approve on any device, then paste the redirected URL --no-verify Skip listing properties after login (no API call) --scope string Search Console access to request: full or readonly (default "full") --service-account string Save a service account JSON key file for this profile --subject string With --service-account: Workspace user to impersonate (domain-wide delegation) ``` ## gsc performance [#gsc-performance] Totals for a period: clicks, impressions, CTR, average position A query with no dimensions, so totals match the Performance report chart (anonymized queries included). ```text gsc performance [flags] ``` ```bash gsc performance --last 28d --compare previous gsc performance --type discover --last 3m -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for performance --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc query [#gsc-query] Search Analytics with every API option (dimensions, filters, types, data state) Runs searchAnalytics.query. Without dates it covers the last 28 days ending at the latest day with data (one small extra request finds that day; pass --end to skip it). Results are sorted by clicks by Google. "complete" in the output means every row Google exposes was fetched; anonymized queries are never included. ```text gsc query [flags] ``` ```bash gsc query -s sc-domain:example.com -d query -d page --limit 5000 -o csv gsc query --last 3m -d date --filter 'country = ind' --filter 'query ~ shoes' gsc query -d hour --data-state hourly_all --last 2d -o json gsc query -d query --compare previous --limit 500 ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --all Fetch every row Google exposes for the query --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all -d, --dimension strings Group by: date, hour, query, page, country, device, searchAppearance (repeat or comma list) --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for query --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to return (pages of up to 25,000 are fetched) (default 1000) --print-request Print the request body and send nothing --raw Print the API response unchanged (single page, no --all or --compare) --request-file string JSON request body to start from (flags override its fields; - for stdin) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc sitemaps [#gsc-sitemaps] List, inspect, submit, and delete sitemaps for a property ```text gsc sitemaps [flags] ``` ```text -h, --help help for sitemaps ``` ## gsc sitemaps delete [#gsc-sitemaps-delete] Delete a sitemap from the report (Google may still crawl it) ```text gsc sitemaps delete SITEMAP_URL [flags] ``` ```bash gsc sitemaps delete https://www.example.com/old-sitemap.xml --yes ``` ```text -h, --help help for delete ``` ## gsc sitemaps get [#gsc-sitemaps-get] Show one sitemap's status, errors, and warnings ```text gsc sitemaps get SITEMAP_URL [flags] ``` ```bash gsc sitemaps get https://www.example.com/sitemap.xml ``` ```text -h, --help help for get ``` ## gsc sitemaps list [#gsc-sitemaps-list] List submitted sitemaps ```text gsc sitemaps list [flags] ``` ```bash gsc sitemaps list gsc sitemaps list --index https://www.example.com/sitemap_index.xml -o json ``` ```text -h, --help help for list --index string Only sitemaps listed in this sitemap index URL ``` ## gsc sitemaps submit [#gsc-sitemaps-submit] Submit a sitemap URL for the property ```text gsc sitemaps submit SITEMAP_URL [flags] ``` ```bash gsc sitemaps submit https://www.example.com/sitemap.xml gsc sitemaps submit https://www.example.com/sitemap.xml --dry-run ``` ```text -h, --help help for submit ``` ## gsc sites [#gsc-sites] List and manage Search Console properties ```text gsc sites [flags] ``` ```text -h, --help help for sites ``` ## gsc sites add [#gsc-sites-add] Add a property to your Search Console list (does not verify ownership) SITE must be exact: sc-domain:example.com or a URL-prefix such as [https://www.example.com/](https://www.example.com/). Adding a property does not verify it. A URL-prefix inside a domain property you already own is verified at once; otherwise complete verification in Search Console. The output reports the resulting permission. ```text gsc sites add SITE [flags] ``` ```bash gsc sites add https://blog.example.com/ gsc sites add sc-domain:example.org --dry-run ``` ```text -h, --help help for add ``` ## gsc sites get [#gsc-sites-get] Show one property (default: the profile's site) ```text gsc sites get [SITE] [flags] ``` ```bash gsc sites get gsc sites get https://www.example.com/ -o json ``` ```text -h, --help help for get ``` ## gsc sites list [#gsc-sites-list] List properties you can access and your permission level ```text gsc sites list [flags] ``` ```bash gsc sites list gsc sites list -o json ``` ```text -h, --help help for list ``` ## gsc sites remove [#gsc-sites-remove] Remove a property from your Search Console list ```text gsc sites remove SITE [flags] ``` ```bash gsc sites remove https://old.example.com/ --yes ``` ```text -h, --help help for remove ``` ## gsc sites use [#gsc-sites-use] Set the profile's default site Exact property identifiers are saved without a network call; a bare host is resolved with one sites.list call. ```text gsc sites use SITE [flags] ``` ```bash gsc sites use sc-domain:example.com gsc sites use www.example.com ``` ```text -h, --help help for use ``` ## gsc status [#gsc-status] Alias of auth status Reads local configuration only. --verify additionally lists properties with the credential (one API call). ```text gsc status [flags] ``` ```bash gsc status gsc status --verify ``` ```text -h, --help help for status --verify Check the credential by listing properties (one API call) ``` ## gsc top [#gsc-top] Top queries, pages, countries, devices, or search appearances ```text gsc top [flags] ``` ```text -h, --help help for top ``` ## gsc top appearance [#gsc-top-appearance] Search appearance types (filter on one with --filter 'searchAppearance = VALUE' in gsc query) ```text gsc top appearance [flags] ``` ```bash gsc top appearance --last 3m -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --all Fetch every row Google exposes for the query --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for appearance --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to return (pages of up to 25,000 are fetched) (default 25) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc top countries [#gsc-top-countries] Clicks and impressions by country (ISO 3166-1 alpha-3) ```text gsc top countries [flags] ``` ```bash gsc top countries --last 3m ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --all Fetch every row Google exposes for the query --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for countries --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to return (pages of up to 25,000 are fetched) (default 25) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc top devices [#gsc-top-devices] Clicks and impressions by device ```text gsc top devices [flags] ``` ```bash gsc top devices --compare yoy ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --all Fetch every row Google exposes for the query --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for devices --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to return (pages of up to 25,000 are fetched) (default 25) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc top pages [#gsc-top-pages] Top pages by clicks ```text gsc top pages [flags] ``` ```bash gsc top pages --filter 'page ~ /blog/' gsc top pages --all -o csv > pages.csv ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --all Fetch every row Google exposes for the query --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for pages --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to return (pages of up to 25,000 are fetched) (default 25) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc top queries [#gsc-top-queries] Top search queries by clicks ```text gsc top queries [flags] ``` ```bash gsc top queries --last 7d gsc top queries --compare previous --limit 50 -o json ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --all Fetch every row Google exposes for the query --compare string Also query a comparison period: previous or yoy --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for queries --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --limit int Maximum rows to return (pages of up to 25,000 are fetched) (default 25) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc trend [#gsc-trend] Clicks, impressions, CTR, and position over time Groups by date (default), hour (last 10 days, preliminary data), ISO week, or calendar month. Week and month totals are rolled up locally from daily rows: clicks and impressions summed; CTR recomputed from the sums; position weighted by impressions. ```text gsc trend [flags] ``` ```bash gsc trend --last 3m --by week gsc trend --by hour --last 2d gsc trend --last 16m --by month --filter 'page ~ /blog/' ``` ```text --aggregation string auto, byPage, byProperty, or byNewsShowcasePanel --by string Granularity: date, hour, week, or month (default "date") --data-state string final (default), all (includes preliminary data), or hourly_all --end string End date YYYY-MM-DD (Pacific Time, inclusive) --filter stringArray Filter 'DIM OP VALUE'; OP is = != ~ !~ =~ !=~ (contains/regex); repeat to AND -h, --help help for trend --last string Window ending at the latest available date: 7d, 28d, 4w, 3m, 16m (default 28d) --start string Start date YYYY-MM-DD (Pacific Time, inclusive) --type string Search type: web (default), image, video, news, discover, googleNews ``` ## gsc update [#gsc-update] Update gsc to the latest release (SHA-256 verified) Downloads the latest GitHub release for this OS and architecture, verifies it against the release's checksums.txt, and replaces the running gsc executable. Works on macOS, Linux, and Windows; on Windows the old executable is renamed to gsc.exe.old and deleted on a later run. Asks "Update now? \[Y/n]" when stdin is a terminal; --yes skips the question, and --no-input requires \--yes. --check only reports the current and latest versions. A gsc in a Go bin directory ($GOBIN, $GOPATH/bin, or \~/go/bin) is not replaced: rebuild it from your checkout instead. --read-only blocks installing but allows --check. Update notice: in an interactive terminal, gsc checks the github.com releases page (not the GitHub API, so its rate limit never applies) for a new release at most once a day and, after a command's output, prints a notice on stderr. The command that runs the day's check waits up to 1 second after its output for the answer; other commands never wait. gsc update and update --check store their result in the same cache. It never checks when stderr is not a terminal, when CI is set, with --quiet, or when GSC_NO_UPDATE_NOTIFIER or NO_UPDATE_NOTIFIER is set (to anything). ```text gsc update [flags] ``` ```bash gsc update --check gsc update gsc update --yes --no-input ``` ```text --check Only report the current and latest versions (always checks the github.com releases page) -h, --help help for update ``` ## gsc version [#gsc-version] Print build information Prints the version, commit, build date, and whether a built-in OAuth client is present. latest and update_available come from the last release check (see gsc update --help) and appear only when one is cached; version never uses the network. ```text gsc version [flags] ``` ```bash gsc version gsc version -o json ``` ```text -h, --help help for version ```