I was uploading an AAB to the Play Console last year and thought — why am I still clicking through this in 2026?
Every Android release is the same ritual. Open the Console, upload the bundle, fill in release notes, pick a track, set the rollout percentage, click through confirmation screens. Fifteen minutes of clicking for
something that should be one command.
So I looked at what exists.
Fastlane supply requires Ruby, Bundler, and about 150 gems. It covers roughly 20 of the 204 Google Play API endpoints. Uploads and metadata — that's about it. No reviews, no vitals, no subscriptions.
gradle-play-publisher is tied to Gradle. You can't use it in a standalone script or outside a build.
Custom bash scripts work until the person who wrote them leaves your team.
Nothing covers the full API. So I built one.
What is GPC?
GPC is a command-line interface that maps the entire Google Play Developer API v3 — 204 endpoints — to simple, consistent commands.
bash
# Upload and release
gpc releases upload app.aab --track beta
# Check vitals
gpc vitals crashes --version 142
# Read reviews
gpc reviews list --stars 1-2 --since 7d
# Manage subscriptions
gpc subscriptions list
It runs on Node.js, installs via npm, Homebrew, or as a standalone binary (no Node.js required). Cold start is under 500ms. Works on macOS, Linux, and Windows.
It's not just a release tool
The part that surprised me during development was how much of the Play Console is actually accessible through the API that nobody builds tools for.
You can sync store listings across 70+ languages. Pull crash data and ANR rates into your monitoring pipeline. Gate a rollout on vitals — if your crash rate spikes, GPC refuses to increase the rollout and tells
you why. Manage subscriptions, verify purchases, convert pricing across regions.
But two commands ended up being my most used:
Preflight — catch policy violations before Google does
gpc preflight app.aab
Runs 9 parallel scanners on your AAB — entirely offline, no API calls. Checks targetSdk compliance, missing exported flags, restricted permissions, hardcoded API keys, non-Play billing SDKs, privacy/tracking issues, COPPA flags, download size, and store listing metadata.
You get a findings report with severity levels. In CI, exit code 6 means something needs fixing before you upload.
I built this because I was tired of uploading a bundle and finding out two hours later that it was rejected. Now it's the first thing that runs in my pipeline.
Status — your app's health in one command
gpc status
Fires 10 parallel API calls and gives you a snapshot: active releases by track, crash rate, ANR rate, slow start rate, slow render rate, and recent reviews — with trend arrows and threshold indicators.
Add --watch 30 and it polls every 30 seconds. Add --notify and you get a desktop notification when a threshold is breached. --since-last shows what changed since your last check.
This replaced the "open three Console tabs and cross-reference" workflow I used to do after every release.
Built for CI/CD
I designed GPC for pipelines from day one.
- name: Upload to Play Store
env:
GPC_SERVICE_ACCOUNT: ${{ secrets.SA_KEY }}
run: |
npm install -g @gpc-cli/cli
gpc preflight app.aab
gpc releases upload app.aab --track internal
gpc status --format summary
A few things that make this work well:
- TTY-aware output — tables in your terminal, JSON when piped. No flags needed.
- Semantic exit codes — 3 means auth failed, 4 means API error, 6 means your crash rate threshold was breached. Your CI can react differently to each.
- --dry-run on every write operation — test your pipeline without shipping to production.
- Vitals gating — block a rollout increase if crash/ANR rates exceed your threshold. One flag.
The architecture
GPC is a TypeScript monorepo with 7 publishable packages:
┌─────────────────────┬──────────────────────────────────────────┐
│ Package │ What it does │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/cli │ The CLI you run │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/core │ Business logic and command orchestration │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/api │ Typed Google Play API client │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/auth │ Service account, OAuth, ADC │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/config │ Config loading, profiles, env vars │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/plugin-sdk │ Plugin interface for extensions │
├─────────────────────┼──────────────────────────────────────────┤
│ @gpc-cli/plugin-ci │ CI/CD helpers │
└─────────────────────┴──────────────────────────────────────────┘
The key decision was TypeScript over Go. The existing play-console-cli is written in Go — fast binary, zero deps. But Android developers already have Node.js (React Native, build tools), npm is already in every
CI pipeline, and @gpc-cli/api works as a standalone SDK in any Node.js project.
Go gives you a binary. TypeScript gives you an ecosystem.
Where it stands
- 204 Google Play API endpoints — all covered
- 1,845 tests, 90%+ line coverage on every core package
- Available on npm, Homebrew, and as a standalone binary
- Fastlane metadata format compatible (drop-in for store listings)
- Plugin system with lifecycle hooks
- Interactive mode for when you don't remember the flags
It's pre-1.0, but it's the most tested project I've shipped, and I use it for my own apps.
Get started
# npm
npm install -g @gpc-cli/cli
# Homebrew
brew install yasserstudio/tap/gpc
# Standalone binary
curl -fsSL https://raw.githubusercontent.com/yasserstudio/gpc/main/scripts/install.sh | sh
- Docs: yasserstudio.github.io/gpc
- GitHub: github.com/yasserstudio/gpc
It's free to use. Code is on GitHub.
If you ship Android apps — what's the one thing about the Play Console you wish you could automate? I'm genuinely curious and happy to prioritize it.
Top comments (0)