npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zuppif/gsc-cli

v0.1.1

Published

Agent-friendly Google Search Console CLI

Readme

gsc-cli

Agent-friendly Google Search Console CLI.

gsc-cli wraps the Google Search Console API with a strict command contract:

gsc-cli <resource> <action> [params]

Stdout is always JSON. There is no --json, table output, color output, file output, pager, browser login, or interactive prompt. The CLI is meant to be easy for agents, scripts, CI jobs, and humans to call without guessing output shape.

Install

Global install:

npm install -g @zuppif/gsc-cli
gsc-cli --help

One-off run:

npx @zuppif/gsc-cli sites list

From this checkout:

bun install
bun run build
bun run dev --help

Local editable install with Bun:

cd /path/to/gsc-cli
bun link
gsc-cli --help

If another project needs the package as a dependency while you work on it:

cd /path/to/gsc-cli
bun link

cd /path/to/other-project
bun link @zuppif/gsc-cli

Get Google Credentials

The recommended setup for agents is a Google Cloud service account with a JSON key, added as a user to the target Search Console property.

Important distinction: Google Cloud IAM access and Search Console property access are different. Giving a service account a Google Cloud role does not give it Search Console data. The service account email must also be added to the Search Console property.

Do not use a Google API key for this CLI. Search Console data access needs OAuth credentials with Search Console scopes plus property-level permission. For unattended CLI and agent workflows, use a service account JSON key.

1. Create or Choose a Google Cloud Project

Open Google Cloud Console:

https://console.cloud.google.com/

Create a project or select an existing project that will own the service account.

2. Enable the Search Console API

Enable the Google Search Console API for the project:

https://console.developers.google.com/apis/api/searchconsole.googleapis.com/overview

You can also find it in Google Cloud Console:

APIs & Services -> Library -> Google Search Console API -> Enable

3. Create a Service Account

Open:

IAM & Admin -> Service Accounts -> Create service account

Use a clear name, for example:

gsc-cli-agent

Use this description:

Read-only Google Search Console access for gsc-cli reporting.

After creation, copy the service account email. It looks like this:

gsc-cli-agent@<project-id>.iam.gserviceaccount.com

Google Cloud project permissions: none needed beyond basic service account existence for Search Console reads. The real permission comes from adding that service account email inside Search Console in step 5.

4. Create a JSON Key

Open the new service account:

Service Accounts -> gsc-cli-agent -> three-dot menu -> Manage keys -> Add key -> Create new key -> JSON

Download the JSON file and store it somewhere stable:

mkdir -p "$HOME/.gsc/accounts"
mv "<PATH TO STORED JSON>" "$HOME/.gsc/accounts/gsc-cli-agent.json"
chmod 600 "$HOME/.gsc/accounts/gsc-cli-agent.json"

Do not commit this file. It contains a private key.

5. Add the Service Account to Search Console

Open Google Search Console:

https://search.google.com/search-console

Select the exact property you want the CLI to access, then:

Settings -> Users and permissions -> Add user

Paste the service account email:

gsc-cli-agent@<project-id>.iam.gserviceaccount.com

Choose the minimum permission that can do the job:

| Permission | Use it for | | --- | --- | | Restricted | Read-only reporting workflows such as Search Analytics and site listing. Start here when possible. | | Full | URL inspection and sitemap workflows when Restricted is not enough. Also useful for agents that need broader operational access. | | Owner | Only when you intentionally want the account to manage verification, settings, or users. Avoid this for basic agent work. |

Start with Restricted user. That should be enough for Search Analytics reads. If URL Inspection or sitemap actions fail later, bump the service account to Full, but start restricted.

6. Point gsc-cli at the Key

The clean default is GOOGLE_APPLICATION_CREDENTIALS:

export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gsc/accounts/gsc-cli-agent.json"
gsc-cli auth status

You can also put it in a local .env file:

GOOGLE_APPLICATION_CREDENTIALS=/Users/you/.gsc/accounts/gsc-cli-agent.json
GSC_SITE_URL=sc-domain:example.com

Or pass the key per command:

gsc-cli --credentials "$HOME/.gsc/accounts/gsc-cli-agent.json" sites list
gsc-cli sites list --credentials "$HOME/.gsc/accounts/gsc-cli-agent.json"

Global flags work before or after the subcommand.

7. Verify Access

Check that credentials load:

gsc-cli auth status --pretty

Expected shape:

{
  "ok": true,
  "command": "auth.status",
  "data": {
    "authenticated": true,
    "source": "GOOGLE_APPLICATION_CREDENTIALS",
    "credentials": "/Users/you/.gsc/accounts/gsc-cli-agent.json"
  },
  "meta": {
    "elapsedMs": 123
  }
}

Check that Search Console property access works:

gsc-cli sites list --pretty

Example:

{
  "ok": true,
  "command": "sites.list",
  "data": {
    "siteEntry": [
      {
        "siteUrl": "sc-domain:example.com",
        "permissionLevel": "siteFullUser"
      }
    ]
  },
  "meta": {
    "elapsedMs": 292
  }
}

Quick Start

export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gsc/accounts/gsc-cli-agent.json"

gsc-cli auth status
gsc-cli sites list

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page \
  --type web \
  --limit 25 \
  --pretty

Site URL Formats

Use the property string exactly as Search Console knows it.

Domain property:

sc-domain:example.com

URL-prefix property:

https://www.example.com/

URL-prefix properties must include the protocol and trailing slash. A property for https://www.example.com/ is not the same as https://example.com/, http://www.example.com/, or sc-domain:example.com.

The safest flow is:

gsc-cli sites list

Copy the exact siteUrl value from the JSON output and reuse it in later commands.

Global Options

These options are accepted by every command:

| Option | Type | Description | | --- | --- | --- | | --credentials <path> | string | Path to a service account JSON key. Overrides ambient Google auth for the command. | | --timeout-ms <number> | integer | Google API timeout in milliseconds. Must be >= 1. | | --pretty | boolean | Pretty-print the JSON envelope. Default output is compact JSON. |

Examples:

gsc-cli --pretty sites list
gsc-cli sites list --pretty

gsc-cli --credentials "$HOME/.gsc/accounts/gsc-cli-agent.json" sites list
gsc-cli sites list --credentials "$HOME/.gsc/accounts/gsc-cli-agent.json"

gsc-cli analytics query --timeout-ms 30000 --site "sc-domain:example.com" --start 2026-06-01 --end 2026-06-12

Output Contract

Success:

{
  "ok": true,
  "command": "analytics.query",
  "data": {},
  "meta": {
    "elapsedMs": 123,
    "siteUrl": "sc-domain:example.com"
  }
}

Failure:

{
  "ok": false,
  "command": "analytics.query",
  "error": {
    "code": "MISSING_SITE",
    "message": "Missing required --site"
  }
}

Command outputs are always written to stdout. Failed commands exit non-zero.

Known error codes from local validation include:

| Code | Meaning | | --- | --- | | MISSING_SITE | --site is required. | | MISSING_URL | --url is required. | | MISSING_SITEMAP | --sitemap is required. | | MISSING_START | --start is required in analytics flag mode. | | MISSING_END | --end is required in analytics flag mode. | | INVALID_ARG | A value is not one of the accepted options. | | INVALID_NUMBER | A numeric flag is not an integer or is outside the accepted range. | | INVALID_BODY | --body is not valid JSON object input. | | AMBIGUOUS_BODY | --body was mixed with request-building flags. | | INVALID_DIMENSION | Analytics dimension is not accepted by this CLI. | | INVALID_FILTER | Analytics filter syntax, dimension, or operator is invalid. | | GOOGLE_API_ERROR | Google API or auth layer rejected the request. |

Commands

auth status

Validate that authentication can create a Google API client.

gsc-cli auth status
gsc-cli auth status --credentials "$HOME/.gsc/accounts/gsc-cli-agent.json" --pretty

Returns:

{
  "authenticated": true,
  "source": "credentials",
  "credentials": "/Users/you/.gsc/accounts/gsc-cli-agent.json"
}

source is one of:

| Source | Meaning | | --- | --- | | credentials | --credentials was passed. | | GOOGLE_APPLICATION_CREDENTIALS | The environment variable is set. | | application-default | Google Application Default Credentials were used. |

sites list

List Search Console properties visible to the authenticated account.

gsc-cli sites list
gsc-cli sites list --pretty

Use this command first. It tells you which siteUrl strings the authenticated identity can access.

sites get

Get one Search Console property.

gsc-cli sites get --site "sc-domain:example.com"
gsc-cli sites get --site "https://www.example.com/"

Required:

| Option | Description | | --- | --- | | --site <siteUrl> | Exact Search Console property string. |

analytics query

Query Search Analytics performance data.

There are two modes:

| Mode | Use it when | | --- | --- | | Flag mode | You want the CLI to build the Google request body from simple flags. | | Raw body mode | You need the full Google request body, multiple filter groups, future API fields, or exact control. |

Do not mix --body with request-building flags. The CLI will fail with AMBIGUOUS_BODY.

Flag Mode

Minimum query:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12

Top page metrics:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page \
  --type web \
  --limit 25 \
  --pretty

Page and query drilldown:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page,query \
  --type web \
  --limit 1000

Queries for one page:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions query \
  --filter "page:equals:https://example.com/pricing" \
  --limit 100

Brand/non-brand split with regex:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions query \
  --filter "query:excludingRegex:(?i).*example.*" \
  --limit 1000

Device split:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions device,page \
  --limit 500

Daily trend:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions date \
  --data-state final

Fresh data:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-14 \
  --end 2026-06-15 \
  --dimensions date \
  --data-state all

Hourly fresh data:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-15 \
  --end 2026-06-15 \
  --dimensions hour \
  --data-state hourly_all

Pagination:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page,query \
  --limit 25000 \
  --start-row 0

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page,query \
  --limit 25000 \
  --start-row 25000

Google sorts most Search Analytics results by clicks descending. If you need "top by impressions", fetch the rows and sort the returned JSON downstream by impressions.

Flag reference:

| Option | Required | Values | | --- | --- | --- | | --site <siteUrl> | yes | Exact Search Console property string. | | --start <YYYY-MM-DD> | yes in flag mode | Inclusive start date in Google Search Console's reporting timezone. | | --end <YYYY-MM-DD> | yes in flag mode | Inclusive end date. | | --dimensions <csv> | no | Any comma-separated subset of country, device, page, query, searchAppearance, date, hour. | | --type <type> | no | web, image, video, news, discover, googleNews. | | --data-state <state> | no | final, all, hourly_all. | | --aggregation <type> | no | auto, byPage, byProperty, byNewsShowcasePanel. | | --limit <number> | no | Integer from 1 to 25000. Maps to rowLimit. | | --start-row <number> | no | Integer >= 0. Maps to startRow. | | --filter <filter> | no | One filter as dimension:operator:expression. | | --body <json-or-> | no | Raw Google request body JSON object, or - to read JSON from stdin. |

Filter syntax:

dimension:operator:expression

Filter dimensions:

country
device
page
query
searchAppearance

Filter operators:

contains
equals
notContains
notEquals
includingRegex
excludingRegex

Examples:

--filter "country:equals:usa"
--filter "device:equals:MOBILE"
--filter "page:contains:/blog/"
--filter "query:includingRegex:(?i).*pricing.*"

For multiple filters or multiple filter groups, use raw body mode.

Raw Body Mode

Pass the Google Search Analytics request body directly:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --body '{"startDate":"2026-06-01","endDate":"2026-06-12","dimensions":["page"],"rowLimit":10}'

Read the request body from stdin:

printf '%s\n' '{"startDate":"2026-06-01","endDate":"2026-06-12","dimensions":["query"],"rowLimit":10}' \
  | gsc-cli analytics query --site "sc-domain:example.com" --body -

Multiple filters:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --body '{
    "startDate": "2026-06-01",
    "endDate": "2026-06-12",
    "dimensions": ["page", "query"],
    "dimensionFilterGroups": [
      {
        "groupType": "and",
        "filters": [
          { "dimension": "page", "operator": "contains", "expression": "/blog/" },
          { "dimension": "query", "operator": "notContains", "expression": "example" }
        ]
      }
    ],
    "rowLimit": 1000
  }'

Raw body fields supported by Google:

| Field | Type | Required | Notes | | --- | --- | --- | --- | | startDate | string | yes | Inclusive date, YYYY-MM-DD. | | endDate | string | yes | Inclusive date, YYYY-MM-DD. | | dimensions | string array | no | Groups rows. Common values: country, device, page, query, searchAppearance, date, hour. | | type | string | no | web, image, video, news, discover, googleNews. searchType is deprecated by Google; use type. | | dimensionFilterGroups | array | no | Filter groups. Google currently supports groupType: "and". | | dimensionFilterGroups[].groupType | string | no | Use and. | | dimensionFilterGroups[].filters[].dimension | string | no | country, device, page, query, searchAppearance. | | dimensionFilterGroups[].filters[].operator | string | no | contains, equals, notContains, notEquals, includingRegex, excludingRegex. | | dimensionFilterGroups[].filters[].expression | string | no | Filter expression. Device values are usually DESKTOP, MOBILE, TABLET; countries use 3-letter country codes. | | aggregationType | string | no | auto, byPage, byProperty, byNewsShowcasePanel. | | rowLimit | integer | no | 1 to 25000; Google default is 1000. | | startRow | integer | no | Zero-based row offset for pagination. | | dataState | string | no | final, all, hourly_all. |

The CLI only validates that --body is a JSON object. It passes the object through to Google, so new API fields can be used before this README is updated.

Search Analytics response shape:

{
  "rows": [
    {
      "keys": ["https://example.com/pricing", "pricing software"],
      "clicks": 12,
      "impressions": 340,
      "ctr": 0.03529411764705882,
      "position": 6.7
    }
  ],
  "responseAggregationType": "byPage"
}

url inspect

Inspect the indexed status of one URL.

gsc-cli url inspect \
  --site "sc-domain:example.com" \
  --url "https://example.com/pricing" \
  --language-code en-US \
  --pretty

Required:

| Option | Description | | --- | --- | | --site <siteUrl> | Exact Search Console property string. | | --url <url> | Fully-qualified URL to inspect. Must be under the property in --site. |

Optional:

| Option | Default | Description | | --- | --- | --- | | --language-code <code> | en-US | BCP-47 language code for translated issue messages. |

Google's URL Inspection API returns the indexed version known to Google. It does not run a live URL test.

sitemaps list

List submitted sitemaps for a site.

gsc-cli sitemaps list --site "sc-domain:example.com"

With a sitemap index:

gsc-cli sitemaps list \
  --site "sc-domain:example.com" \
  --sitemap-index "https://example.com/sitemap_index.xml"

Required:

| Option | Description | | --- | --- | | --site <siteUrl> | Exact Search Console property string. |

Optional:

| Option | Description | | --- | --- | | --sitemap-index <url> | List sitemap entries included in a sitemap index. |

sitemaps get

Get one sitemap.

gsc-cli sitemaps get \
  --site "sc-domain:example.com" \
  --sitemap "https://example.com/sitemap.xml"

Required:

| Option | Description | | --- | --- | | --site <siteUrl> | Exact Search Console property string. | | --sitemap <url> | Sitemap URL. |

sitemaps submit

Submit a sitemap.

gsc-cli sitemaps submit \
  --site "sc-domain:example.com" \
  --sitemap "https://example.com/sitemap.xml"

Returns data: null on success.

This command uses the write OAuth scope:

https://www.googleapis.com/auth/webmasters

The service account must have enough Search Console permission for the property.

sitemaps delete

Delete a submitted sitemap from Search Console.

gsc-cli sitemaps delete \
  --site "sc-domain:example.com" \
  --sitemap "https://example.com/sitemap.xml"

Returns data: null on success.

This removes the sitemap submission from Search Console. It does not delete the sitemap file from your website.

Common Agent Workflows

Find accessible properties:

gsc-cli sites list

Fetch page metrics for a date range:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page \
  --limit 25000

Fetch query metrics for one page:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions query \
  --filter "page:equals:https://example.com/pricing" \
  --limit 1000

Fetch page-query pairs:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page,query \
  --limit 25000

Fetch country and device mix:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions country,device \
  --type web

Compare Discover separately from web search:

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --start 2026-06-01 \
  --end 2026-06-12 \
  --dimensions page \
  --type discover \
  --limit 1000

Inspect a URL after an indexing issue:

gsc-cli url inspect \
  --site "sc-domain:example.com" \
  --url "https://example.com/pricing"

Check submitted sitemaps:

gsc-cli sitemaps list --site "sc-domain:example.com"

Submit a sitemap after publishing:

gsc-cli sitemaps submit \
  --site "sc-domain:example.com" \
  --sitemap "https://example.com/sitemap.xml"

Troubleshooting

auth status fails with ENOENT

The key path is wrong.

ls -l "$HOME/.gsc/accounts/gsc-cli-agent.json"
gsc-cli auth status --credentials "$HOME/.gsc/accounts/gsc-cli-agent.json"

auth status works but sites list is empty

Authentication works, but the authenticated identity has no Search Console properties.

Fix:

  1. Copy the client_email from the JSON key.
  2. Open the Search Console property.
  3. Add that email under Settings -> Users and permissions.
  4. Run gsc-cli sites list again.

403 or insufficient permissions

Common causes:

| Cause | Fix | | --- | --- | | Search Console API is not enabled in the Google Cloud project. | Enable the Search Console API for the project that owns the key. | | Service account was not added to Search Console. | Add the client_email from the key as a Search Console user. | | Wrong property string. | Copy the exact siteUrl from gsc-cli sites list. | | Write command with read-only access. | Use a Search Console account with enough permission or raise the service account permission. | | Wrong key file. | Run gsc-cli auth status --pretty and check the credential source. |

sites get fails but sites list shows properties

You are probably passing a different property string than Search Console expects. Use the exact siteUrl value from sites list.

Good:

gsc-cli sites get --site "sc-domain:example.com"
gsc-cli sites get --site "https://www.example.com/"

Common mistakes:

gsc-cli sites get --site "example.com"
gsc-cli sites get --site "https://www.example.com"
gsc-cli sites get --site "http://www.example.com/"

analytics query returns no rows

Check:

| Check | Why it matters | | --- | --- | | Date range | Search Console data may not exist for the requested dates. | | Property string | sc-domain:example.com and https://www.example.com/ can expose different scopes. | | Search type | web, discover, googleNews, news, image, and video are separate. | | Filters | A strict page/query/device/country filter can eliminate all rows. | | Data freshness | Use --data-state all for recent partial data, or final for stable data. |

Need more than 25000 rows

Use pagination with --start-row.

gsc-cli analytics query --site "sc-domain:example.com" --start 2026-06-01 --end 2026-06-12 --dimensions page,query --limit 25000 --start-row 0
gsc-cli analytics query --site "sc-domain:example.com" --start 2026-06-01 --end 2026-06-12 --dimensions page,query --limit 25000 --start-row 25000
gsc-cli analytics query --site "sc-domain:example.com" --start 2026-06-01 --end 2026-06-12 --dimensions page,query --limit 25000 --start-row 50000

Stop when the returned rows array is missing or empty.

Need multiple filters

Use --body.

gsc-cli analytics query \
  --site "sc-domain:example.com" \
  --body '{"startDate":"2026-06-01","endDate":"2026-06-12","dimensions":["page","query"],"dimensionFilterGroups":[{"groupType":"and","filters":[{"dimension":"page","operator":"contains","expression":"/blog/"},{"dimension":"query","operator":"notContains","expression":"example"}]}],"rowLimit":1000}'

Need stable JSON for another tool

Default output is compact JSON:

gsc-cli sites list

Use --pretty only for humans:

gsc-cli sites list --pretty

Official API References

  • Google service account credentials: https://developers.google.com/workspace/guides/create-credentials#service-account
  • Google Cloud service accounts: https://docs.cloud.google.com/iam/docs/service-accounts-create
  • Search Console users and permissions: https://support.google.com/webmasters/answer/7687615
  • Search Console API reference: https://developers.google.com/webmaster-tools/v1/api_reference_index
  • Search Analytics query: https://developers.google.com/webmaster-tools/v1/searchanalytics/query
  • URL Inspection inspect: https://developers.google.com/webmaster-tools/v1/urlInspection.index/inspect
  • Sitemaps API: https://developers.google.com/webmaster-tools/v1/sitemaps

Development

bun install
bun run dev --help
bun run check
bun run build

Before publishing, verify the package contents:

npm pack --dry-run

Publish

bun run check
bun run build
npm publish --access public