A powerful command-line tool for managing and downloading your Audible audiobooks.
Built with β€οΈ in Python.
Note
A ground-up Rust reimplementation of audible-cli (and the
Audible library) is in active
development: one fast, statically-linked binary β no Python required β
with a reworked command set, an encrypted auth format, a local library
database (SQLite/FTS5), and a capability-based plugin system.
It is pre-alpha and I'm looking for experienced users to run it against real accounts and report the rough edges. If you're comfortable on the command line and don't mind occasional breakage, please help kick the tyres β github.com/mkb79/audible-rs (the README there has a one-line installer for prebuilt Linux/macOS binaries). Share feedback, questions and findings in the audible-rs Discussions.
audible-cli stays fully supported β audible-rs is the future direction,
not an immediate replacement, and the two use separate config directories,
so you can run them side by side.
- π Manage multiple Audible accounts (profiles)
- π Browse and export your library and wishlist
- π§ Download audiobooks in AAXC or AAX with metadata & chapters
- β‘ Fast HTTP requests powered by httpx
- π οΈ Plugin system for custom commands & extensions
- π» Cross-platform: Linux, macOS, Windows
- π Prebuilt executables (no Python required)
With uv tool (recommended)
uv tool install audible-cliWith uvx
uvx --from audible-cli audibleFrom PyPI
pip install audible-cliFrom GitHub
git clone https://github.com/mkb79/audible-cli.git
cd audible-cli
pip install .β‘ Faster crypto (optional)
Install the optional cryptography extra to enable audible's Rust-accelerated
crypto backend. It noticeably speeds up cryptographic operations such as fetching
activation bytes and decrypting AAX/AAXC files:
pip install "audible-cli[cryptography]"
# or, when installed as a tool:
uv tool install "audible-cli[cryptography]"Without the extra, audible-cli still works using the pure-Python fallback.
Donβt want to install Python?
Prebuilt binaries are available on the releases page.
βΉοΈ The prebuilt binaries bundle an accelerated crypto backend, with no extra setup β the Rust-based
cryptographyeverywhere except Windows on arm64, which usespycryptodomebecausecryptographypublishes no wheel for it. Either way you are not on the slow pure-Python fallback.
Windows β x86_64 onedir (recommended), x86_64 onefile
Also on ARM: arm64 onedir, arm64 onefile
macOS β Apple silicon only: onefile, onedir
On an Intel Mac, install from PyPI instead: pipx install audible-cli
Linux β take these: x86_64, arm64
Built on Ubuntu 22.04, so they need glibc 2.35 β Ubuntu 22.04, Debian 12, RHEL 9 and anything newer.
There are also builds on a 24.04 base (x86_64, arm64). They need glibc 2.38 and so reach fewer systems; take one only if you have a reason to.
βΉοΈ The download names now carry the architecture. The old names without one (
audible_mac.zip,audible_win.zipand the rest) are still published alongside and will be dropped after 15 November 2026.
-
Run the interactive setup:
audible quickstart
β creates config, profile, and auth file.
-
List your library:
audible library list
-
Download your entire library:
audible download --all --aax
| Goal | Command |
|---|---|
| List all audiobooks | audible library list |
| Export library to JSON | audible library export --output library.json |
| Add to wishlist | audible wishlist add --asin B004V00AEG |
| Download since date | audible download --start-date "2023-01-01" --aaxc --all |
| Switch profile | audible -P germany library list |
audible-cli stores its configuration files in an app directory.
| OS | Path |
|---|---|
| Windows | C:\Users\<user>\AppData\Local\audible |
| Linux | ~/.audible |
| macOS | ~/.audible |
You can override this by setting the environment variable:
export AUDIBLE_CONFIG_DIR=/path/to/dir- Name:
config.toml - Format: TOML
- Structure:
[APP]section β global defaults[profile.<name>]section β settings per Audible account
Example:
[APP]
primary_profile = "default"
filename_mode = "ascii"
chapter_type = "tree"
[profile.default]
auth_file = "auth.json"
country_code = "us"
[profile.germany]
auth_file = "auth_de.json"
country_code = "de"- Each profile corresponds to an Audible account or marketplace
- Contains:
auth_fileβ authentication filecountry_codeβ Audible marketplace (us,de,uk, β¦)
- Switch profiles with:
audible -P germany library listThe [APP].primary_profile is used if no profile is specified.
- Stored in the same app directory as the config file
- Can be password-protected:
audible -p "mypassword" download --asin <ASIN>- If no password is passed, you will be prompted with hidden input
π§ APP section
primary_profile: default profile if none is specifiedfilename_mode: filename handling for downloadsascii(default)- override with
--filename-mode
chapter_type: chapter format for downloadstree(default)- override with
--chapter-type
π€ Profile section
auth_file: authentication file for this profilecountry_code: Audible marketplacefilename_mode: overrides[APP].filename_modechapter_type: overrides[APP].chapter_type
audible-cli can run headless in CI, for example to export your library on a
schedule. The setup-audible-cli
action installs audible-cli and configures a minimal, ephemeral Audible
authentication on the runner, so later steps can run audible commands.
Signed Audible API requests need only two values from your local auth.json, so
there is no need to upload the whole config directory. If you have not
authenticated yet, do it once locally with audible quickstart (or
audible manage auth-file add); this writes an auth.json into your config
directory. The two values a runner needs are:
adp_tokendevice_private_key(a PEM block)
Add them as repository secrets and your marketplace as a plain variable.
With gh and jq you can read both straight from
auth.json:
jq -r .device_private_key auth.json | gh secret set AUDIBLE_DEVICE_PRIVATE_KEY
jq -r .adp_token auth.json | gh secret set AUDIBLE_ADP_TOKEN
gh variable set AUDIBLE_COUNTRY_CODE --body de # your Audible marketplaceThen the workflow is just:
name: Audible export
on:
workflow_dispatch:
schedule:
- cron: "0 8 * * *"
jobs:
export:
runs-on: ubuntu-latest
steps:
- uses: mkb79/setup-audible-cli@v1
with:
adp-token: ${{ secrets.AUDIBLE_ADP_TOKEN }}
device-private-key: ${{ secrets.AUDIBLE_DEVICE_PRIVATE_KEY }}
country-code: ${{ vars.AUDIBLE_COUNTRY_CODE }}
- run: audible library export --format json --output library.jsonOnly these two values are stored as secrets; access and refresh tokens, cookies,
and account details are never uploaded, and the runner's auth files exist only
for the lifetime of the job. See the
setup-audible-cli action for its
full set of inputs and outputs.
β οΈ These credentials grant access to your Audible account. Run authenticated workflows only from trusted branches, scheduled runs, or manual dispatches, never from arbitrary pull-request code.
A live example: earshot refreshes its data from Audible on a daily schedule using this action.
- activation-bytes β Manage DRM activation keys
- api β Call raw Audible API endpoints
- download β Download audiobooks
- library β List, export your library
- wishlist β Manage wishlist (list, add, remove, export)
- manage β Profiles, configs, auth-files
- quickstart β Interactive setup
Show help:
audible <command> -hCreate a file in the plugin folder, e.g. cmd_hello.py:
import click
@click.command(name="hello")
def cli():
click.echo("Hello from plugin!")Important: A custom plugin must start with the prefix
cmd_. The loader scans forcmd_*.pyfiles and attaches each command to the CLI.
Run:
audible helloYou can also distribute plugins as Python packages via entry points.
The entry point group is audible.cli_plugins.
[project]
name = "audible-myplugin"
version = "0.1.0"
dependencies = ["audible-cli", "click"]
[project.entry-points."audible.cli_plugins"]
my_command = "myplugin.cli:my_command"
another = "myplugin.cli:another"After installation, your plugin commands will automatically be available in audible:
audible my-command
audible anotherControl logging output:
audible -v debug library list
audible -v error download --allLevels: debug, info, warning, error, critical
Default: info
Every command keeps its two streams apart. stdout carries the result you asked for: an API response, a list of titles, the activation bytes. stderr carries everything else β progress, warnings, errors, and the questions an interactive command asks. So a pipe only ever receives the payload:
audible api library | jq '.items[].title'
audible library list > titles.txtTo keep a record of a run, use --log-file rather than redirecting a stream.
It writes the same log with a timestamp, module and line, follows --verbosity,
and appends across runs:
audible --log-file audible.log download --allColour follows the terminal, and honours NO_COLOR and FORCE_COLOR.
Questions go to stderr too, so 2> on an interactive command hides the
question while the command waits for an answer. Keep that stream on screen
while answering. --log-file is no substitute there: it records what the
command has to say about its work, not the conversation, and deliberately
so β a password or a captcha answer has no business in a file on disk.
download reserves the bottom rows of the terminal for one progress line per job
plus one for the queue. Log output scrolls above them and stays in the scrollback
when the command finishes.
Resizing the window during a download can leave a copy of the old lines standing
above the new ones until the next log line scrolls them away. A program cannot see
what the terminal did with rows it has already drawn, so this is a limit shared by
every tool that pins progress without taking the whole screen. Pass --no-progress
to turn the display off.
- audible-cli-flask β Run
audible-cliin a Flask web server - audible-series β Organize series from your library
Want your add-on listed? β Open a PR or issue π
Contributions welcome!
- File issues
- Open pull requests
- Share plugins and add-ons
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only).
See LICENSE for details.
