English | 简体中文
playfetch is a command-line tool for downloading APKs from Google Play zero-config anonymously or using your own credentials. It features reproducible device emulation and official Google Play checksum verification, emitting a complete manifest.json detailing the device identity, package metadata, and hash verification logs for every download.
playfetch pull com.example.app- Reproducible Device Profiles: Pin and log exact hardware parameters (SDK level, ABI architecture, screen density DPI, and locale). Because Google Play serves different Split APKs based on hardware capabilities,
playfetchensures all downloads remain deterministically traceable. - Google Play Hash Verification: Verifies every downloaded APK against official SHA256 and SHA1 digests returned directly in Google Play's delivery response.
- Provenance Manifest Generation: Automatically generates a
manifest.jsonalongside downloaded APKs, recording device identity, application metadata, and verification results. - Flexible Packaging Modes: Supports uncompressed split APKs (
split), single.apksarchives (apks), or both (both). - Zero-Config Anonymous Retrieval: Fetches temporary credentials on-the-fly via an integrated token dispenser and performs local device checkin without requiring a personal Google account.
- Multi-Account & Session Management: Supports logging in with custom Google accounts (saving long-lived AAS tokens), multi-region account management, default account assignment, and session caching.
- CI & Script Friendly: Detects interactive TTY vs piped/CI environments automatically. Emits clean, non-mangling discrete progress updates in CI pipelines with standard stdout/stderr separation and strict exit codes.
go install github.com/Exmeaning/playfetch/cmd/playfetch@latestgit clone https://github.com/Exmeaning/playfetch.git
cd playfetch
go build ./cmd/playfetchDependencies: Requires only
google.golang.org/protobuf. Generated protobuf bindings and device profiles are vendored;protocis not required.
# Inspect the download plan and Play's official hashes without fetching files
playfetch plan com.example.app
# Download APKs and assemble into a single .apks archive
playfetch pull com.example.app -mode apks
# Inspect regional titles using specific locale and timezone
playfetch info com.example.jp -locale ja_JP -tz Asia/Tokyo
# Check for updates and pull only when a newer versionCode is available
playfetch watch com.example.app || playfetch pull com.example.app -mode apks| Command | Description |
|---|---|
pull <pkg> |
Download APKs, verify Play hashes, and write manifest.json (alias: download) |
plan <pkg> |
Resolve download package set and official hashes without fetching bytes |
info <pkg> |
Retrieve live package metadata and version details |
search <query> |
Search package names by keyword |
watch <pkg> |
Compare live versionCode against an existing manifest.json |
devices |
List bundled device profiles |
doctor |
Diagnose service connectivity, DFE headers, and token dispenser health |
login |
Authenticate with a personal Google account and store the AAS token |
logout |
Remove stored account credentials |
accounts |
List saved accounts and active default |
interactive |
Force interactive shell prompt |
version |
Print program version |
0: Success.1: Execution error.2: Invalid command-line usage or arguments.10: (watchonly) A newerversionCodeis available upstream.
playfetch reorders arguments automatically; flags may be specified before or after the package name:
-profile <name|path> Device profile name (see `devices`) or path to a custom .properties file
-locale <locale> Locale sent to Play (e.g. `ja_JP`, `en_US`)
-tz <timezone> Timezone (defaults to device profile value)
-sdk <int> Override profile SDK version (`Build.VERSION.SDK_INT`)
-abi <list> Override profile ABI list (e.g. `arm64-v8a`)
-dpi <int> Override profile screen density
-out <path> Exact output directory (overrides `-out-root`)
-out-root <path> Root directory for downloads (default: `./downloads`)
-mode <mode> Output mode: `split` (default), `apks`, or `both`
-label <string> Custom free-text tag written into `manifest.json`
-vc <int> Pin download to a specific `versionCode`
-force Force re-downloading files even if local verified copies exist
-json Emit output in JSON format
-v Verbose logging mode (logs API requests)
-account <name|email>Specify stored account name, email, or region label
-refresh Ignore cached session and re-authenticate/re-checkin
-anonymous Force dispenser anonymous mode (ignores saved accounts)
-accept-tos Automatically accept Play Terms of Service for new accounts
All downloaded packages are organized under ./downloads/<package_name>/<version_code>/:
downloads/com.example.app/1783590150/
├── base.apk
├── split_config.arm64_v8a.apk
├── split_config.xxhdpi.apk
└── manifest.json
{
"device": {
"profile": "px_9a",
"sdk": 35,
"abis": ["arm64-v8a"],
"locale": "en_US"
},
"app": {
"package": "com.example.app",
"version_code": 1783590150,
"version_name": "1.0.0"
},
"files": [
{
"role": "base",
"name": "base.apk",
"size": 177690473,
"play_sha256": "2b80ad54...",
"play_sha1": "ec39d85a...",
"local": {
"sha256": "2b80ad54...",
"sha1": "ec39d85a...",
"md5": "71357fcb..."
},
"verified": true,
"verify_note": "matched Play sha256+sha1"
}
]
}split(default): Preserves original files as served by Google Play (individual base and split APKs).apks: Packs base and split APKs into a single.apksarchive (ZIP container readable by SAI orbundletool), removing standalone split files after verification.both: Retains both standalone APKs and the.apksarchive.
Note: To produce a single installable universal APK, use
bundletoolorAPKEditorfor resource merging and re-signing.playfetchpreserves original, unmodified publisher-signed binaries.
Zero configuration required. playfetch requests temporary credentials from a token dispenser and performs local device checkin. Suitable for fetching public applications.
Warning
Public Dispenser Notice & Abuse Prevention
The default anonymous credentials rely on a shared public Token Dispenser account pool maintained by Aurora OSS for Aurora Store users (~250 shared accounts, strictly rate-limited by Google).
playfetch does not own or guarantee this public pool. High-frequency automated jobs or CI loops can degrade the pool, trigger Cloudflare blocks, or cause endpoint shutdowns for the entire community.
For CI/CD pipelines, automated scripts, or high-volume downloads, please follow the Self-Hosting Token Dispenser Guide or use dedicated accounts to avoid abusing shared community resources.
To download account-restricted or paid titles, sign in with a custom account:
playfetch loginAuthentication steps:
- Follow the interactive prompt to open the Google Embedded Setup page in a browser.
- Obtain the
oauth_tokenCookie underaccounts.google.comvia DevTools. - Paste the token into
playfetch. The tool exchanges it for a long-lived AAS Token stored with0600file permissions.
Direct AAS token input is also supported:
playfetch login -email user@example.com -aas-token <AAS_TOKEN>Manage multiple accounts and switch between them seamlessly:
# Save an account with a region tag
playfetch login -name jp-account -region JP
# List stored accounts
playfetch accounts
# Set default account
playfetch accounts -set-default jp-account
# Execute command with a specific account
playfetch pull com.example.app -account jp-account- Billing-bound Accounts: Storefront country is determined by the account's billing country.
- Unbound Accounts / Anonymous Mode: Storefront country is determined dynamically by the request's egress IP.
- Note:
infometadata availability does not guaranteepullaccess. If an application is unavailable in the active storefront region,planorpullwill fail with HTTP 400 (DF-DFERH-01). Use a VPN/proxy or region-matched account to access geo-restricted titles.
Google Play serves specific APK splits tailored to requesting hardware profiles. playfetch includes 45 bundled device profiles up to Android 15. The default profile is px_9a (Pixel 9a, SDK 35, arm64-v8a only, 420dpi).
Override hardware attributes via flags or specify custom .properties files:
playfetch pull com.example.app -sdk 34 -abi arm64-v8a,armeabi-v7a -dpi 480
# Or use a custom properties file
playfetch pull com.example.app -profile ./my-device.propertiesRunning playfetch without arguments in an interactive TTY launches the interactive shell prompt:
$ playfetch
playfetch v0.1.0 -- reproducible Google Play APK retrieval
account: anonymous (dispenser)
device: px_9a (sdk 35, arm64-v8a, 420dpi), locale en_US
playfetch> search bang dream
playfetch> info com.bushiroad.en.bangdreamgbp -locale ja_JP
playfetch> pull com.bushiroad.en.bangdreamgbp -mode apks
The interactive prompt accepts standard commands and flags identically to the CLI. Non-TTY invocations without arguments exit with status code 2.
Use the doctor command to diagnose service availability, DFE header integrity, and dispenser health:
playfetch doctorCore parameters can be overridden via environment variables:
PLAYFETCH_DISPENSER: Custom Token Dispenser service URL.PLAYFETCH_CREDENTIALS: Custom account credentials storage path.PLAYFETCH_DOWNLOADS: Root directory for downloads.PLAYFETCH_DFE_TARGETS: OverrideX-DFE-Encoded-Targetsheader.PLAYFETCH_DFE_PHENOTYPE: OverrideX-DFE-Phenotypeheader.
- License: Released under GPL-3.0-or-later.
- Acknowledgements:
- Aurora OSS: Protobuf schemas, device profile definitions, and token dispenser architecture.
- apkeep (EFForg): Device profile references and AAS token authentication flows.
- Full third-party notices available in THIRD-PARTY.md.
