Save to Spotify
A command-line tool for saving audio content to Spotify. Built for agents and automation, generate a daily briefing, language lesson, or meeting recap, then push it to Spotify where it's available alongside your other listening.
What you can make
Point your agent at content you have, and get a finished episode on Spotify:
- Morning briefing — your calendar, tasks, and overnight repo activity as a 3-minute daily brief
- Deep dive — turn a topic, article, or PDF into a personalized audio explainer
- Meeting recap — drop in a transcript, get a 2-minute summary with action items
- Travel guide — a private audio itinerary for your next trip
- News roundup — a daily roundup from the newsletters, feeds, and sites you follow
- Bedtime story — a custom story using your kid's name and favorite setting
Describe what you want in plain English; the agent sources, scripts, narrates, and saves it.
Making the audio
This CLI saves audio to Spotify — it does not generate speech itself. You bring a voice engine:
- Kokoro (recommended) — free, local, no API key, no limits (~340 MB)
- OpenAI TTS — high quality, needs
OPENAI_API_KEY - ElevenLabs — most natural, needs
ELEVENLABS_API_KEY
No key? Kokoro is the default. Already have an OpenAI or ElevenLabs key? The agent detects it and offers to use it — no extra setup.
Run save-to-spotify tts status to see what's available, or save-to-spotify tts add to register any other engine.
Quick Start
Prompt your agent to install:
> Install Save to Spotify by running https://saveto.spotify.com/install.sh
Installs the CLI and exposes the skill to Claude Code, Cursor, Codex, and any agent that reads .agents/skills/.
Once installed, invoke with /save-to-spotify (Claude Code) or $save-to-spotify (Codex), or just describe what you want in plain English.
Install
Curl-bash
Same script the Quick Start prompt runs:
curl -fsSL https://saveto.spotify.com/install.sh | bash
Common options:
# Pin a specific version
curl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --version 0.2.0
# Custom install directory
curl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --dir ~/.local/bin
# Binary only, skip the agent skill
curl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --no-skills
Skills.sh
Install the agent skill to Claude Code, Cursor, Codex, and 50+ other agents:
npx skills add spotify/save-to-spotify
ClawHub / OpenClaw
Save to Spotify is listed on ClawHub as @spotify/save-to-spotify. Install it into your active OpenClaw workspace:
openclaw skills install @spotify/save-to-spotify
The ClawHub listing installs the agent skill. The skill uses the save-to-spotify CLI, so if the binary is not already on your PATH, run the curl-bash installer above.
Claude Code plugin marketplace
/plugin marketplace add spotify/save-to-spotify
/plugin install save-to-spotify@save-to-spotify
Manual
If you can't run the install script:
1. Download save-to-spotify-{os}-{arch}-v{version}.zip and its matching .sha256 from the releases page. 2. Verify integrity:
shasum -c save-to-spotify-darwin-arm64-v0.2.0.zip.sha256
3. Unzip, you get the binary plus a skills/save-to-spotify/ tree. 4. Move the binary to a directory on your PATH and chmod +x it (not needed on Windows). 5. (Optional) Copy skills/save-to-spotify/ into your agent's skill directory, e.g. ~/.claude/skills/save-to-spotify/ or ~/.cursor/skills/save-to-spotify/.
Windows notes
- Run the install script in Git Bash (ships with Git for Windows); it installs
save-to-spotify.exeto~/.local/binand offers a PATH fix. - The Windows binaries are not code-signed yet: SmartScreen may block the first run. Unblock with right-click → Properties → Unblock, or
Unblock-File save-to-spotify.exein PowerShell.
Build from source
Requires Go 1.21+.
go build -ldflags "-X github.com/spotify/save-to-spotify/cmd.commit=$(git rev-parse --short HEAD)" \
-o save-to-spotify .
sudo mv save-to-spotify /usr/local/bin/
Authentication
Log in once, the CLI stores your token and refreshes it automatically.
# Opens your browser to authorize (default)
save-to-spotify auth login
# Headless mode, prints a URL to open on any device, then you paste back the redirect URL
# Useful for SSH sessions and remote servers
save-to-spotify auth login --no-browser
# Check if you're logged in
save-to-spotify auth status
# Log out (removes stored credentials)
save-to-spotify auth logout
Tokens are stored in ~/.config/save-to-spotify/token.json (using $XDG_CONFIG_HOME if set). If you use DPoP-bound tokens (the default), the sibling dpop_key.json is also required. For CI and headless environments, see the CI / Automation guide.
Commands
upload
The primary command. Uploads a media file and creates an episode in one step. This is the main entrypoint for agents, a single command handles show creation, file upload, and episode metadata.
save-to-spotify upload <file> --title <title> [flags]
| Flag | Description | |---|---| | --title | Episode title (required) | | --show-id | Target show ID or URI | | --new-show | Create a new show with this title and save the file to it | | --summary | Episode description | | --image | Cover image file (.jpg/.png, max 1 MB) | | --language | Language code (default: en) |
If --show-id or --new-show is not specified, the CLI will use your most recently created show, or create a new one if none exists.
# Save an audio file to Spotify, uses your most recent show (or creates one automatically)
save-to-spotify upload ./recap.mp3 --title "Monday Standup Recap"
# Save to a specific show
save-to-spotify upload ./lesson.mp3 --title "Spanish Lesson 12" --show-id spotify:show:abc123
# Organize saved items into separate shows
save-to-spotify upload ./lecture.mp3 --title "CS 101 — Lecture Notes Week 6" --new-show "Lecture Notes"
# With cover image and description
save-to-spotify upload ./briefing.mp3 \
--title "Morning Briefing" \
--summary "Weather and calendar for today" \
--image ./cover.jpg
Supported formats: .mp3, .m4a, .wav, .ogg.
shows
Shows are containers for your saved content. You can use a single show for everything or create separate shows to organize by topic (e.g. "Daily Briefings", "Language Practice", "Meeting Recaps").
# List your shows
save-to-spotify shows
# Create a show
save-to-spotify shows create --title "Lecture Notes" --summary "Audio summaries of CS 101 lectures"
# Get show details
save-to-spotify shows get <id>
# Delete a show and all its episodes
save-to-spotify shows delete <id>
shows create flags:
| Flag | Description | |---|---| | --title | Show title (required) | | --summary | Show description (default: "(no description)") | | --image | Cover image file (.jpg/.png, max 1 MB) | | --language | Language code (default: en) |
episodes
Each saved file becomes an episode within a show.
# List episodes (for your most recent show)
save-to-spotify episodes
# List episodes for a specific show
save-to-spotify episodes --show-id spotify:show:abc123
# Create an episode (more control than `upload`, same underlying operation)
save-to-spotify episodes create --title "Sprint Review Notes" --file ./review.mp3
# Check if an episode is ready for playback
save-to-spotify episodes status <episode-id>
# Wait until an episode is ready (default timeout: 5m)
save-to-spotify episodes status <episode-id> --wait
# Wait with a custom readiness timeout
save-to-spotify episodes status <episode-id> --wait 2m
# Delete an episode
save-to-spotify episodes delete <episode-id>
episodes create flags:
| Flag | Description | |---|---| | --title | Episode title (required) | | --file | Media file path (required) | | --show-id | Target show ID or URI (default: most recent show) | | --summary | Episode description | | --image | Cover image file (.jpg/.png, max 1 MB) | | --language | Language code |
timeline
Add chapter markers and in-player companion content to saved episodes. A timeline can include chapters, images, external links, and Spotify-native references via spotify_entity. Prefer spotify_entity whenever the thing you want listeners to open already exists on Spotify, prefer full spotify:... URIs over bare IDs or open.spotify.com URLs, and note that a link and a spotify_entity can both appear in the same timeline when both destinations are useful.
# Get timeline items for an episode
save-to-spotify timeline get <episode-id>
# Set (replace all) timeline items from a JSON file
save-to-spotify timeline set --episode-id <episode-id> --from-file timeline.json
# Delete all timeline items
save-to-spotify timeline delete <episode-id>
Example timeline.json:
{
"items": [
{ "chapter": { "title": "Introduction", "start_time_ms": 0 } },
{ "chapter": { "title": "Key Concepts", "start_time_ms": 45000 } },
{ "spotify_entity": { "start_time_ms": 60000, "duration_ms": 30000, "uri": "spotify:track:abc123" } },
{ "link": { "start_time_ms": 95000, "duration_ms": 20000, "url": "https://example.com/slides" } },
{ "chapter": { "title": "Summary", "start_time_ms": 120000 } }
]
}
Timeline rules:
chapter: optional, but if present there must be at least 2; the first must start at0.image: requires a positiveduration_msand a local.jpg/.pngfile path or animage_token.link: requires a positiveduration_msand an HTTP(S) URL.spotify_entity: requires a fullspotify:...URI;duration_msis optional but must be positive when set.- Companion items (
image,link,spotify_entity) must not overlap in time, but alinkand aspotify_entitymay share the same segment/chapter as long as their windows do not overlap.
list
Alternative command for listing your content.
# List episodes for the most recent show
save-to-spotify list
# List episodes for a specific show
save-to-spotify list episodes --show-id spotify:show:abc123
# List all shows
save-to-spotify list shows
Other commands
| Command | Description | |---|---| | update | Check for and install CLI updates | | token | Print access token to stdout (for piping to curl, etc.) | | version | Print version and commit hash |
Global flags
| Flag | Description | |---|---| | --json | Output results as JSON (for scripting and automation) | | --timeout <duration> | API request timeout (default: 30s, e.g. 1m, 90s) |
Documentation
- Agent integration — JSON mode, typical workflow, error handling
- CI / Automation — GitHub Actions setup, headless auth, credential persistence
Environment variables
| Variable | Purpose | Default | |---|---|---| | SAVE_TO_SPOTIFY_AUTH_TOKEN | Bearer token override (expires in ~1 hour, no auto-refresh — see CI guide) | — | | SAVE_TO_SPOTIFY_BACKEND_URL | Override the backend API URL | https://saveto.spotify.com | | SAVE_TO_SPOTIFY_TIMEOUT | API request timeout (e.g. 30s, 2m) | 30s | | SAVE_TO_SPOTIFY_CLIENT_ID | OAuth client ID override | built-in | | SAVE_TO_SPOTIFY_NO_UPDATE_CHECK | Disable passive update checks | off | | SAVE_TO_SPOTIFY_HEADERS | Additional headers for backend requests (JSON object of header name/value pairs; only X-STS-* headers are accepted) | — |











