An asynchronous Python wrapper and WebSocket server for controlling the Spotify desktop client via Spicetify and the spicetify-connect-api extension.
✨ Features
- ⚡ Real-time Push Events: Instant updates for song changes, volume, seeking, shuffle, repeat, heart/like status, and ping heartbeats.
- 🌐 Wildcard State Decorator: Listen to every state update event with a single
@server.on_state_changeddecorator. - 🎮 Full Playback Control: Play, pause, skip, seek, volume, repeat, shuffle, and ping latency checks.
- 🔑 API Key Security: Optional token authorization for securing command execution and event streaming.
- 🔒 Secure WebSockets (WSS): Built-in SSL/TLS support via
ssl_contextorcertfile/keyfile. - 🏷️ Rich & Fully Typed Models: Complete Pydantic V2 models.
- 🔄 Async & Non-blocking: Built on
asyncioandwebsocketsfor maximum performance.
⚙️ Installation
Python 3.10 or higher is required.
pip install spicetify-websocket
📋 Prerequisites
To use this library, ensure you have:
- Spotify Desktop Client installed.
- Spicetify CLI installed and configured.
- The spicetify-connect-api extension enabled in Spicetify.
📚 Documentation & Guides
Explore the official documentation for detailed references and setup guides:
- 📖 API Reference – Full documentation for
SpotifyServer, models, decorators, and exceptions. - 💡 Code Examples – Runnable scripts for basic usage, API key auth, and WSS encryption.
- 🛠️ Deployment Guides – Step-by-step guides for local WSS setups and production VPS deployment.
🚀 Example Usage
import asyncio
from spicetify import PlayerState, RepeatMode, SpotifyServer
async def main():
async with SpotifyServer() as server:
# Event handler for song changes
@server.on_song_changed
def on_song_changed(state: PlayerState):
track = state.track
if track:
print("New song is playing:", track.title)
print("Artist/s:", ", ".join(artist.name for artist in track.artists))
# Wait until Spicetify client connects
await server.wait_for_connection()
# Playback State Query
is_playing: bool = await server.get_is_playing()
print("Is Spotify playing:", is_playing)
# Playback Controls
await server.play_url(url="https://open.spotify.com/intl-de/track/55pBIZO1cqoldeqpp5WR7H")
await server.set_volume(percent=75)
await server.set_repeat(mode=RepeatMode.TRACK)
# Keep the server running to receive events
await asyncio.Event().wait()
if __name__ == "__main__":
asyncio.run(main())
📻 Events Reference
All state-related event handlers receive a comprehensive PlayerState object containing the full player snapshot.
| Event Name | Convenience Decorator | Callback Payload Type | Description |
|---|---|---|---|
* (Wildcard) |
@server.on_state_changed |
PlayerState |
Fired on every player state update event. |
InitialState |
@server.on_initial_state |
PlayerState |
Fired immediately when Spicetify connects. |
SongChanged |
@server.on_song_changed |
PlayerState |
Fired when a new track starts playing. |
PlayPauseChanged |
@server.on_play_pause_changed |
PlayerState |
Fired when playback state changes (play/pause). |
VolumeChanged |
@server.on_volume_changed |
PlayerState |
Fired when volume level changes. |
RepeatChanged |
@server.on_repeat_changed |
PlayerState |
Fired when repeat mode changes (OFF, CONTEXT, TRACK). |
ShuffleChanged |
@server.on_shuffle_changed |
PlayerState |
Fired when shuffle mode is toggled. |
SeekChanged |
@server.on_seek_changed |
PlayerState |
Fired when timeline position is manually changed. |
HeartChanged |
@server.on_heart_changed |
PlayerState |
Fired when the active track's heart/like status changes. |
Ping |
@server.on_ping |
datetime (UTC) |
Fired on periodic heartbeat pings from Spicetify. |
Metadata
Release files for spicetify-websocket 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spicetify_websocket-0.3.0.tar.gz | 24.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spicetify_websocket-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.3 kB
Release files / spicetify_websocket-0.3.0.tar.gz
| Download URL | spicetify_websocket-0.3.0.tar.gz |
|---|---|
| Size | 24.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
084caf80f97f1bf35abb7a7712f349f1cbb7841b5d56a0678b504df06f8f60c3
|
|
BLAKE2b-256 checksum How to use checksums |
2cea9c74c85b35714389d783e6bc568fc9040815a2f0fd7ba6b0b0c5b0a48a56
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency logRelease files / spicetify_websocket-0.3.0-py3-none-any.whl
| Download URL | spicetify_websocket-0.3.0-py3-none-any.whl |
|---|---|
| Size | 17.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f126db137fdae2b30018e09af7d2564dafa6b229f70443ba81560a8f877946da
|
|
BLAKE2b-256 checksum How to use checksums |
27759058816b706398ab22d3d8aaaeee16e629a4bab25edf01228b2a009b082b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency log