One friendly CLI to search and bulk-download Android APKs (Google Play / APKPure / F-Droid / Huawei) and iOS IPAs (Apple App Store).
AppGrab wraps apkeep and ipatool behind a single, self-bootstrapping Python script. Point it at a search term and it grabs everything for you.
$ ./appgrab.py search "dubai rest" -p ios
→ Searching the App Store for "dubai rest" (limit=10) ...
1. ae.gov.dubailand.selfregistration Dubai REST (v7.2.1)
2. com.deg.mdubai DubaiNow (v14.6.30)
3. com.bayut.bayutapp Bayut – UAE Property Search (v16.1.4)
...
Download all 10 apps to '.'? [y/N] y
→ Downloading 10 apps from the App Store (parallel=4) ...
✓ All downloads complete. Files in: /Users/you/apps
- Two platforms, one tool — Android (
.apk) and iOS (.ipa) with a single-p android|iosswitch. - Search or direct download — search a store and bulk-grab every result, or download a single app by id.
- Automatic fallback — if the Google Play / APKPure source can't provide an app (missing there, geo-restricted, or no credentials), AppGrab retries it from Huawei AppGallery.
- Cross-platform — works on macOS, Ubuntu/Debian, Arch Linux (and other
brew/apt/pacman/dnf/zypperdistros). - Zero setup — the installer installs
apkeep,ipatooland the Python env up front; anything still missing is detected and installed on first run with your permission. Re-run anytime withappgrab install-deps. uvorvenv— uses PEP 723 inline metadata withuv, and falls back to a private virtualenv ifuvisn't installed.- No auth for free sources — Android defaults to APKPure; iOS only needs your own App Store account login.
- Pretty output — colored, script-friendly progress, and
Ctrl+Cexits cleanly without a traceback.
- Python 3.9+
uv(recommended, optional — otherwise a venv is created)
Everything else is installed on demand. AppGrab always asks before installing anything, and picks a strategy that works on your OS:
| Dependency | Needed for | How AppGrab installs it |
|---|---|---|
google-play-scraper |
Android search | isolated uv env, else a private venv |
apkeep |
Android downloads | prebuilt binary (Linux), Homebrew, or cargo install apkeep |
ipatool |
iOS | prebuilt binary (macOS/Linux), Homebrew, or go install |
| Rust/cargo | only if building apkeep |
brew / apt / pacman / dnf, else rustup |
| Go | only if building ipatool |
brew / apt / pacman / dnf |
Prebuilt binaries are downloaded straight from the official GitHub releases (with SHA-256 verification when the project publishes one), so most users never need a compiler.
- macOS (Intel & Apple Silicon)
- Ubuntu / Debian (and derivatives)
- Arch Linux (and derivatives)
- Other Linux with
dnf/zypper/Homebrew
Pass -y / --yes (or set APPGRAB_YES=1) to auto-accept every prompt — handy
for unattended installs and CI.
curl -fsSL https://raw.githubusercontent.com/iamhsouna/appgrab/main/install.sh | bashThen run appgrab --help. The installer prefers an isolated uv
tool install, falls back to pipx, then to dropping the single script in
~/.local/bin.
The installer also adds ~/.local/bin to your PATH (in your shell's
profile) and installs every runtime dependency up front — the Python env
with google-play-scraper, plus apkeep and ipatool — so the tools are ready
immediately. Set APPGRAB_NO_MODIFY_PATH=1 to skip the PATH edit or
APPGRAB_SKIP_DEPS=1 to skip the dependency install. You can install (or repair)
dependencies at any time:
appgrab install-deps # both platforms
appgrab install-deps --android-only # only apkeep
appgrab install-deps --ios-only # only ipatoolPrivate repo?
raw.githubusercontent.comneeds a token. Use theuvone-liner below instead (Git uses your stored credentials), or rungh api repos/iamhsouna/appgrab/contents/install.sh -H "Accept: application/vnd.github.raw" | bash.
uv tool install "git+https://github.com/iamhsouna/appgrab"git clone https://github.com/iamhsouna/appgrab.git
cd appgrab
./install.sh # or: ./appgrab.py --helpRun it without installing anything (uses uv for an ephemeral env):
uv run appgrab.py --helpappgrab <command> [options]
Commands:
setup Configure credentials (Google Play AAS token or App Store login)
search Search a store and download all results
download Download a single app by bundle/app id
install-deps Install all runtime dependencies (Python env, apkeep, ipatool)
update Update AppGrab to the latest version
Common options:
| Flag | Description |
|---|---|
-p, --platform {android,ios} |
Target platform (default: android) |
-o, --output DIR |
Output directory (default: .) |
-r, --parallel N |
Parallel downloads (default: 4) |
--limit N |
Max search results (default: 10) |
-y, --yes |
Auto-accept install/download prompts (APPGRAB_YES=1 also works) |
--dry-run |
List results without downloading |
-s, --source |
Android source: apk-pure, google-play, f-droid, huawei-app-gallery |
--fallback / --no-fallback |
Android: fall back to Huawei AppGallery when the chosen source can't provide an app (default: on) |
--purchase / --no-purchase |
iOS: acquire a license if required (default: on) |
# Search Google Play and download every result from the free APKPure source
./appgrab.py search "whatsapp"
# Only list results
./appgrab.py search "whatsapp" --dry-run
# Download a single app
./appgrab.py download com.whatsapp -o ./apks
# Use a different source
./appgrab.py search "fdroid" -s f-droidGoogle Play source needs a Google email + AAS token:
./appgrab.py setup # prompts for email + AAS token
./appgrab.py download com.whatsapp -s google-playHuawei AppGallery fallback is on by default for the apk-pure and
google-play sources: any app they can't provide is automatically retried from
Huawei AppGallery (useful for region-specific apps). Disable it with
--no-fallback:
./appgrab.py search "whatsapp" -s google-play --no-fallback# One-time App Store sign-in (wraps `ipatool auth login`)
./appgrab.py setup -p ios
# Search the App Store
./appgrab.py search "dubai rest" -p ios
# Download a single app by bundle id (or numeric app id)
./appgrab.py download ae.gov.dubailand.selfregistration -p ios -o ./ipasDownloads are saved as {bundleID}_{appID}_{version}.ipa.
Update in place — no need to re-run the installer:
appgrab update # update to the latest version
appgrab update --check # just report whether an update is available
appgrab --version # show the installed versionappgrab update detects how it was installed and does the right thing:
| Installation | What update runs |
|---|---|
| git clone | git pull --ff-only |
uv tool install |
uv tool upgrade appgrab (falls back to reinstall) |
pipx install |
pipx install --force git+… |
single-file in ~/.local/bin |
re-downloads the latest appgrab.py |
For a private repo, updating needs GitHub auth: have
gh auth logindone, or setGITHUB_TOKEN.
appgrab.pycarries inline PEP 723 metadata listing its Python dependency.- On first Android search, if
google-play-scraperis missing the script asks, then re-executes itself underuv run(preferred) or a private venv at~/.cache/appgrab/venv. - Missing native tools are detected by OS/architecture. AppGrab asks before
installing anything, then tries, in order: a prebuilt GitHub release binary →
the system package manager (
brew/apt/pacman/dnf/zypper) → building from source withcargo/go(installing that toolchain too, if you agree). - Installed binaries land in
~/.local/bin(or~/.cargo/bin/~/go/bin), which AppGrab adds toPATHfor the current session. - Search + download is delegated to the native tool, with JSON output parsed by AppGrab.
Config for the Google Play source is stored at ~/.config/appgrab/config.json (mode 0600).
Download only apps you have the right to download, and respect the terms of service of Google Play, the Apple App Store, and the upstream tools. Use at your own risk.