README
¶
TTSBuddy CLI
Convert text to speech from the command line using the TTSBuddy API.
Install
Homebrew (macOS/Linux)
brew install ngelik/tap/ttsbuddy
Go
go install github.com/ngelik/ttsbuddy-cli/cmd/ttsbuddy@latest
Binary download
Download from GitHub Releases and place in your $PATH.
Update
Homebrew
brew upgrade ngelik/tap/ttsbuddy
Go
go install github.com/ngelik/ttsbuddy-cli/cmd/ttsbuddy@latest
Binary
Download the latest release from GitHub Releases and replace the existing binary in your $PATH.
Check version
type -a ttsbuddy
ttsbuddy version
After installing or updating, confirm that the first ttsbuddy found on your
PATH is the binary you intended to use. An older binary installed earlier on
PATH can shadow a newer Homebrew installation. If multiple installs appear,
inspect them before changing PATH or removing anything; do not delete an
install whose origin is unclear.
Quick Start
ttsbuddy auth browser
ttsbuddy speak "Hello from TTS Buddy"
ttsbuddy auth status
ttsbuddy auth browser can sign in an existing Clerk account or create a new
one through Clerk's hosted page. On a new account's first successful consent,
TTS Buddy initializes the standard Free plan before issuing the CLI session.
ttsbuddy auth email signs in an existing account; add --signup to create a
new account with email verification entirely in the terminal. The signup flow
initializes the same standard Free plan before issuing the CLI session. Clerk
owns identity creation and verification, while the CLI only submits the
verified session to TTS Buddy. Each method stores the same seven-day ttsc_
CLI session. A new login replaces the prior CLI session. There is no refresh;
login again after expiry.
On v0.11.0, ttsbuddy auth email may show Clerk request returned status 422
immediately after the email prompt, before a code prompt, when Clerk reports
that no account was found for the identifier. Check the address, or run
ttsbuddy auth email --signup to create an account. In v0.11.1 and newer, the CLI
makes that guidance explicit only for Clerk's allowlisted
form_identifier_not_found response; other provider failures remain generic.
In v0.11.2 and newer, signup addresses rejected by Clerk's
form_email_address_blocked policy receive direct guidance to use a different,
non-disposable email address and rerun ttsbuddy auth email --signup.
The signup prompt is intentionally conditional: an address that is already
registered may still reach Clerk's verification prompt under strict
enumeration protection, or may be rejected later by the provider. That prompt
does not prove a new account was created. For an existing account, use
ttsbuddy auth email; the CLI never turns a signup attempt into an automatic
login.
# 1. Set your API key (create one in Dashboard -> Settings)
ttsbuddy config set key ttsb_your_key_here
# 2. Convert text to speech
ttsbuddy speak "Hello, world!"
# 3. Listen to the output
afplay ttsbuddy-*.mp3 # macOS
Try It Without Signing Up
The no-signup CLI demo uses fixed sample files and pregenerated audio to prove the CLI workflow without exposing an unrestricted public TTS endpoint.
Run the same constrained demo locally:
./demo/cli-demo.sh
The script builds ttsbuddy when needed and uses only the public demo key and allowlisted sample inputs. See demo/README.md for the exact boundary and manual commands.
Authentication
Use ttsbuddy auth browser for interactive terminal work, or ttsbuddy auth email when a browser is unavailable. Use ttsbuddy auth email --signup to
create a new account by entering the verification code from email. If the
signup needs legal acceptance, CAPTCHA, MFA, or another field, use browser
authentication instead; the CLI never fills those requirements implicitly.
If the address is already registered, run ttsbuddy auth email; provider
enumeration protection can defer that determination until verification, so a
signup verification prompt is not evidence of a new identity.
Use a permanent ttsb_ key for CI and unattended automation. Login/logout
store the CLI session separately and never overwrite api_key. Effective
precedence is --key > TTSBUDDY_API_KEY > active CLI session > stored
permanent key.
ttsbuddy auth logout revokes the stored session before clearing it. A network
or server failure retains the local session so the command can be retried.
--local-only skips revocation and warns that server validity may continue
until the absolute seven-day expiry.
API keys are created in Dashboard → Settings at ttsbuddy.com/dashboard. The API Keys guide covers creation, storage, and revocation. Keys use the format ttsb_<public_id>_<secret>.
Three ways to provide your key (in priority order):
- Flag:
-k ttsb_...(leaks to shell history — avoid in shared environments) - Environment variable:
export TTSBUDDY_API_KEY=ttsb_... - Config file:
ttsbuddy config set key ttsb_...(stored at~/.ttsbuddy/config.json)
For full details on creating and managing keys, see the API Keys guide.
Commands
speak
Convert text to speech. The main command.
# Inline text
ttsbuddy speak "Hello world"
# From file (.md files auto-preprocessed for better narration)
ttsbuddy speak -f article.md -v bf_emma -o article.mp3
# From stdin
cat notes.txt | ttsbuddy speak -
# Custom voice, language, and speed
ttsbuddy speak "Bonjour" -v st_m1 --language fr -s 0.9
ttsbuddy speak "こんにちは" -v st_f2 --language ja
# JSON output (for scripting)
ttsbuddy speak "Hello" --json
# Print URL without downloading
ttsbuddy speak "Hello" --no-download
# Raw MP3 to stdout (pipe to player)
ttsbuddy speak "Hello" -o - | afplay -
Flags:
| Flag | Description |
|---|---|
-f, --file <path> |
Read text from file |
-v, --voice <id> |
Voice ID (default: st_m1) |
-l, --language <code> |
Supertonic language code (default: en; examples: fr, de, ja, ko, ar) |
-s, --speed <n> |
Speed 0.5–1.5 (default: 1.2) |
-o, --output <path> |
Output file (- for stdout) |
--output-dir <dir> |
Directory for auto-named files (default: .) |
--timeout <duration> |
Poll timeout (default: 10m) |
--raw |
Skip markdown preprocessing |
--no-download |
Print audio URL instead of downloading |
--idempotency-key <key> |
Override auto-generated idempotency key |
Notes:
.mdand.markdownfiles are automatically preprocessed: headings, links, images, and code blocks are stripped for cleaner narration. Use--rawto send verbatim.- Fast voices (
st_*) support 30+ language modes through--language, use native display names inttsbuddy voices, and support the full 0.5–1.5 speed range. - Auto-named files use the pattern
ttsbuddy-YYYYMMDD-HHMMSS-<voice>.mp3.
Fast voice language codes: en, ar, bg, hr, cs, da, nl, et, fi, fr, de, el, hi, hu, id, it, ja, ko, lv, lt, pl, pt, ro, ru, sk, sl, es, sv, tr, uk, vi.
web
Convert a readable webpage to speech.
# Use backend account preferences for voice, language, and speed
ttsbuddy web https://www.ttsbuddy.com/docs/
# Translate the article to Russian with a Fast voice
ttsbuddy web https://www.ttsbuddy.com/docs/ --language ru --voice st_m1
# Print URL without downloading
ttsbuddy web https://www.ttsbuddy.com/docs/ --no-download
web fetches only http and https pages, extracts the readable article text locally, and sends the extracted text plus source URL to the API. If --voice, --language, or --speed are omitted, the backend applies your TTSBuddy account preferences. When the extracted article language differs from the target language, the backend translates the article before speech generation.
web supports the same output and polling flags as speak: --voice, --language, --speed, --output, --output-dir, --timeout, --no-download, and --idempotency-key.
During longer jobs, web shows the local extraction step, backend submission,
queued/processing status, and real provider percentages when the API has them.
When conversion completes, human output includes the job ID plus speech length,
MP3 size, and generation speed when available.
voices
List available TTS voices.
# Curated list with Kokoro voices plus Supertonic language modes (always works offline)
ttsbuddy voices
# Full live catalog from API
ttsbuddy voices --all
# JSON output
ttsbuddy voices --json
Voice output includes ID, native display NAME, LANGUAGE, language CODE, and TYPE. Supertonic Fast voices (st_m1-st_m5, st_f1-st_f5) appear once per supported language mode, for example st_m1 appears as Louis under French with code fr and Noah under German with code de. If --all can't reach the live catalog, it falls back to the curated list with a warning.
Kokoro voices use compact provider codes such as a for American English, b for British English, f for French, and z for Chinese. Fast voices use standard language codes from the list above.
status
Check job status. Read-only — does not download audio.
# Check a specific job
ttsbuddy status <job_id>
# Check most recent job
ttsbuddy status
# Poll until complete
ttsbuddy status <job_id> --watch
# JSON output
ttsbuddy status <job_id> --json
config
Show or set configuration.
# Show all config values
ttsbuddy config
# Get a specific value
ttsbuddy config get voice
# Set values
ttsbuddy config set key ttsb_...
ttsbuddy config set voice st_m1
ttsbuddy config set language fr
ttsbuddy config set speed 0.9
ttsbuddy config set timeout 5m
ttsbuddy config set allow_custom_api_url true
Valid keys: key, voice, language, speed, timeout, output_dir, api_url, cli_auth_url, tts_api_base_url, allow_custom_api_url
version
ttsbuddy version
ttsbuddy version --json
Configuration
File: ~/.ttsbuddy/config.json (0600 permissions in 0700 directory)
{
"api_key": "ttsb_...",
"default_voice": "st_m1",
"default_language": "en",
"default_speed": 1.2,
"output_dir": ".",
"poll_timeout": "10m"
}
Precedence: flags > environment variables > config file > defaults
| Setting | Env Variable | Flag | Default |
|---|---|---|---|
| API key | TTSBUDDY_API_KEY |
-k |
— |
| CLI auth URL | TTSBUDDY_CLI_AUTH_URL |
— | https://www.ttsbuddy.com/v1/cli-auth |
| Voice | TTSBUDDY_VOICE |
-v |
st_m1 |
| Language | TTSBUDDY_LANGUAGE |
-l, --language |
en |
| Speed | TTSBUDDY_SPEED |
-s |
1.2 |
| Output dir | TTSBUDDY_OUTPUT_DIR |
--output-dir |
. |
| Poll timeout | TTSBUDDY_TIMEOUT |
--timeout |
10m |
| API URL | TTSBUDDY_API_URL |
— | (production) |
| Voice catalog API URL | TTSBUDDY_TTS_API_BASE_URL |
— | https://tts.api.prod.ttsbuddy.website |
| Allow custom API URL | TTSBUDDY_ALLOW_CUSTOM_API_URL |
— | false |
By default, credentialed commands may use the production TTSBuddy hosts or localhost development endpoints. Sending an API key to any other HTTPS API host requires explicit opt-in with ttsbuddy config set allow_custom_api_url true or TTSBUDDY_ALLOW_CUSTOM_API_URL=true.
Browser authentication uses the public production Clerk OAuth client ID built
into the source, including binaries produced by go install. Release builds
inject and verify the same public value (for example,
make build CLERK_OAUTH_CLIENT_ID=...). Local development may set
TTSBUDDY_CLERK_OAUTH_CLIENT_ID
and TTSBUDDY_CLERK_OAUTH_ISSUER only together with
TTSBUDDY_ALLOW_CUSTOM_API_URL=true. The callback always uses an ephemeral
127.0.0.1 port and /callback.
Release builds fail closed unless the repository variable
CLERK_OAUTH_CLIENT_ID is configured. It must match the public production
client ID compiled into source. Browser and email authentication are available
in packaged, Homebrew, and go install ...@latest builds.
Global Flags
These work on any command:
| Flag | Description |
|---|---|
-k, --key <key> |
API key (overrides config/env) |
--json |
JSON output to stdout only, no human output on stderr |
--quiet |
Suppress progress output |
Output Modes
| Mode | stdout | stderr |
|---|---|---|
Default speak |
nothing (file saved to disk) | spinner, status, "Saved to ...", final stats |
--json |
JSON response | nothing |
-o - |
raw MP3 bytes | spinner (if TTY) |
--quiet |
nothing | nothing |
--no-download |
nothing | audio URL and final stats |
--json and -o - are mutually exclusive (both write to stdout) — combining them exits with code 2.
Human progress output shows honest stages such as fetching, submitting, queued, processing, finalizing, and downloading. Percentages appear only when the backend receives real provider progress. Completed jobs show speech length, MP3 size when known, generation speed, and job ID.
Example human output for a webpage conversion:
Fetching webpage...
Extracted "Top announcements of AWS re:Invent 2025" (10793 chars)
Submitting webpage TTS request...
Queued job fe57968d...
Processing 42%... (1m47s)
https://tts-buddy-history-prod.s3.amazonaws.com/pro/example.mp3?...
Job ID: fe57968d-0958-4ccd-a3e1-d87a7972f01e
Speech length: 14m23s
MP3 size: 14.4 MB
Generation speed: 39 chars/sec
Exit Codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Runtime or API error (auth, provider, download) |
2 |
Usage or config error (bad flags, missing key, validation) |
130 |
Interrupted (Ctrl+C) — job ID printed for resume |
Pipe Examples
# Convert and play immediately (macOS)
ttsbuddy speak "Hello" -o - | afplay -
# Batch convert markdown files
for f in docs/*.md; do
ttsbuddy speak -f "$f" -o "${f%.md}.mp3"
done
# Get audio URL for scripting
ttsbuddy speak "Hello" --no-download --json | jq -r '.audio_url'
Rate Limits
| Limit | Value |
|---|---|
| POST requests per minute | 1 per API key |
| GET requests per minute | 30 per API key |
| Max text length | 500,000 characters |
| Monthly TTS minutes | Depends on plan |
| Audio URL lifetime | Temporary — download immediately |
For full API details, see the API Reference.
Troubleshooting
| Error | Fix |
|---|---|
| "Invalid API key" | Run ttsbuddy config set key <your-key> |
| "Subscription inactive" | Reactivate at ttsbuddy.com/billing |
| "Rate limited" | Wait and retry (automatic with backoff) |
| "Monthly minutes exhausted" | Upgrade plan or wait for reset |
| "No API access" | Your plan may not include API access |
| "Text too long" | Split input into chunks under 500k characters |
| Audio file not found | Files expire based on plan — re-generate |
Development
Running Tests
# Unit tests (deterministic, no network)
make test
# Unit tests with race detector
go test -race ./...
# Coverage report
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out # per-function summary
go tool cover -html=coverage.out # interactive HTML report
# Lint (install with: make tools)
make lint
# Reachable Go dependency and toolchain vulnerabilities
make vuln
# Live API acceptance tests
TTSBUDDY_API_KEY=ttsb_... make test-acceptance
Test Architecture
- Internal packages (
internal/api,internal/config,internal/markdown) use standard Go unit tests withhttptestservers — no network or live API needed. - Command tests (
cmd/) use a subprocess pattern to safely testos.Exitpaths and directos.Stdout/Stderrwrites. Each test re-invokes the test binary viaTestHelperProcess, capturing real output and exit codes. - Acceptance tests (
tests/acceptance_test.sh) run the built binary against the live API, gated byTTSBUDDY_API_KEY.
Build
make build # build to bin/ttsbuddy
make tools # install lint, vulnerability, SBOM, and release tools
make release-snapshot # test release build for all platforms
make clean # remove bin/ and dist/
Documentation
- API Reference — full endpoint documentation
- CLI Guide — detailed usage guide
- API Keys — creating and managing keys
License
MIT
Documentation
¶
There is no documentation for this package.
Directories
¶
| Path | Synopsis |
|---|---|
|
ttsbuddy
command
|
|
|
internal
|
|
|
tools/checkactionpins
command
|
|
|
tools
|
|
|
clerk-fapi-probe
command
|