A standalone command-line client for the Google Merchant API v1
(merchantapi.googleapis.com) — the GA replacement for the Content API for
Shopping, which Google retires in August 2026.
Drive your Merchant Center from the terminal (or from an AI agent): products,
feeds, performance reports, price benchmarks, promotions, account settings.
One auditable codebase, credentials never leave your machine, all output is
JSON on stdout — pipe it to jq.
pipx install gmc-cli
gmc auth login --client-id=… --client-secret=… --merchant-id=1234567890
gmc register-gcp # one-time Google requirement, see below
gmc products issues-summary | jq '.top_issue_codes'
gmc report performance --start 2026-06-01 --end 2026-06-30 --order-by clicks- CLI, not SaaS: no third-party service proxies your Merchant Center data.
- Merchant API v1: built on the new GA API, not the deprecated Content API v2.1.
- Read-first safety model: only three write commands exist, deletes require
--yes, and a global--dry-runprints the exact HTTP request instead of sending it. - Agent-friendly: predictable JSON output and one flat command tree make it easy to hand to Claude/other AI agents as a tool.
pipx install gmc-cli # recommended (isolated)
# or: pip install gmc-cliFrom source:
git clone https://github.com/almoretti/gmc-cli && cd gmc-cli
./setup.sh # local venv + editable install; run via ./gmc- Go to Google Cloud Console → pick or create a project.
- Enable the Merchant API.
- APIs & Services → Credentials → Create Credentials → OAuth client ID → Desktop app. Copy the client ID and secret.
- If the OAuth consent screen is in Testing mode, add yourself as a test user (and note refresh tokens expire after 7 days until you publish the app to In production).
gmc auth login --client-id=xxx --client-secret=xxx --merchant-id=1234567890Opens a browser — sign in with a Google account that has access to your
Merchant Center (the numeric merchant ID is in the top-right of
merchants.google.com). Credentials are saved
to ~/.config/gmc-cli/credentials.json (chmod 600) and refresh automatically.
Google requires the GCP project behind your OAuth client to be registered as a developer with the merchant account, or every API call returns 401:
gmc register-gcp --developer-email you@example.com
gmc auth check # should print your account name and "ok": trueIdempotent — safe to re-run.
- Service account (headless automation): set
GMC_SERVICE_ACCOUNT_KEYto a JSON key path, and add the service account's email as a user in Merchant Center → Settings → People and access. - Env/dotenv:
GMC_ACCOUNT_ID,GMC_SUBACCOUNT_ID(act on an MCA sub-account),GMC_OAUTH_TOKEN. A.envin the working directory is read. - ADC fallback:
gcloud auth application-default loginwith thecontentscope.
Resolution order: GMC_SERVICE_ACCOUNT_KEY → GMC_OAUTH_TOKEN →
~/.config/gmc-cli/credentials.json → ADC. Merchant ID: --merchant-id →
GMC_SUBACCOUNT_ID/GMC_ACCOUNT_ID → saved config.
gmc auth login | check | init --client-secrets FILE
gmc register-gcp [--developer-email EMAIL] # one-time per GCP project
gmc account get | list | issues | users | programs | shipping | homepage | business-info
gmc products list [--limit N] [--full]
gmc products get|status ID # ID = contentLanguage~feedLabel~offerId
gmc products disapproved # all disapproved products (via Reports)
gmc products issues-summary # issue codes ranked by frequency
gmc products insert --product @p.json --data-source accounts/…/dataSources/… (WRITE)
gmc products delete ID --data-source … --yes (WRITE)
gmc report query "SELECT … FROM product_performance_view WHERE …"
gmc report performance --start YYYY-MM-DD --end YYYY-MM-DD [--order-by clicks] [--top N]
gmc report zero-clicks [--min-impressions N] [--start …] [--end …]
gmc report price-competitiveness | price-insights | demoted
gmc report best-sellers [--country GB] [--granularity WEEKLY|MONTHLY]
gmc report competitors [--country GB] [--category 469] [--traffic-source ALL|ADS|ORGANIC]
gmc datasources list | get ID | fetch ID # fetch = WRITE, triggers feed re-crawl
gmc promotions list | get ID
gmc regions | quotas | return-policies
gmc raw GET|POST|PATCH|PUT|DELETE <path> [--body JSON] [--param k=v] # escape hatch
Global flags: --merchant-id ID, --dry-run (print writes instead of
sending), -v (retry diagnostics), --version.
Defaults note: report shortcuts default to --country GB and --category 469
(Health & Beauty) — override for your market
(category IDs).
# What's broken in my feed, ranked by issue code?
gmc products issues-summary | jq '.top_issue_codes'
# Top products by clicks, last 30 days, as TSV
gmc report performance --start 2026-06-09 --end 2026-07-08 \
| jq -r '.results[].productPerformanceView | [.offerId, .clicks, .impressions] | @tsv'
# Where am I priced above the market benchmark?
gmc report price-competitiveness | jq '.results[].priceCompetitivenessProductView
| select((.price.amountMicros|tonumber) > (.benchmarkPrice.amountMicros|tonumber))'
# Force an immediate re-fetch of a scheduled feed
gmc datasources list | jq -r '.dataSources[].dataSourceId'
gmc datasources fetch 123456789
# Preview a product upsert without sending it
gmc --dry-run products insert --product @sku.json --data-source accounts/…/dataSources/…
# Anything the built-ins don't cover
gmc raw GET conversions/v1/accounts/1234567890/conversionSources- Reads are unrestricted; writes exist only for
products insert/deleteanddatasources fetch; delete requires--yes. - Global
--dry-runshort-circuits any write and prints the exact request. - Retries with exponential backoff +
Retry-Afteron 429/5xx. gmc rawcan perform any method — use deliberately.- Product inserts only work against API-type data sources; feeds fetched from
files/Sheets can't be written via the API (use
datasources fetchto trigger re-crawls instead, or overlay a supplemental API data source).
- Product IDs:
contentLanguage~feedLabel~offerId(e.g.en~GB~SKU123). - Product writes go through
productInputs:insert(upsert — full body overwrites) and always require adataSource. - v1 vs v1beta field changes:
gtin→gtins(list),attributes→productAttributes, enums are UPPER_CASE strings,channel/taxesremoved. - Report dialect rules (all verified live; the built-in shortcuts handle them):
ctr→click_through_rate;conversion_value_micros→conversion_valueproduct_performance_viewrequires adatecondition in WHEREprice_competitiveness_product_viewrequiresreport_country_code+idin SELECTprice_insights_product_viewrequiresidin SELECTbest_sellers_*requiresreport_date,report_granularity,report_country_code,report_category_idin SELECT and areport_granularityWHERE conditioncompetitive_visibility_*requiresreport_country_code,report_category_id,traffic_sourcein SELECT and WHERE conditions ondate,report_category_id,traffic_source
./test.sh runs a 45-check functional suite against whatever merchant account
you're logged into: every read command live, every write in --dry-run only,
plus the guard rails. It never mutates your feed.
Endpoint conventions were originally mapped out by studying kiwoongeom/gmc-mcp (MIT), an MCP server for the same API. No code was copied; this project exists for people who want a plain CLI with no MCP layer.
Apache-2.0