An asynchronous Python wrapper and WebSocket server for controlling the Spotify desktop client via Spicetify and the spicetify-connect-api extension.
- ⚡ 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.
Python 3.10 or higher is required.
pip install spicetify-websocketTo use this library, ensure you have:
- Spotify Desktop Client installed.
- Spicetify CLI installed and configured.
- The spicetify-connect-api extension enabled in Spicetify.
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.
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())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. |
