Version 3 of wavelink has brought about many changes. This short guide should help you get started when moving from version 2.

Some things may be missing from this page. If you see anything wrong or missing please make an issue on GitHub.

Key Notes#

  • Version 3 is now fully typed and compliant with pyright strict.

  • Version 3 only works on Lavalink v4+.

  • Version 3 now uses black and isort formatting, for consistency.

  • Version 3 now better uses positional-only and keyword-only arguments in many places.

  • Version 3 has better error handling with requests to your Lavalink nodes.

  • Version 3 has Lavalink websocket completeness. All events have been implemented, with corresponding payloads.

  • Version 3 has an experimental LFU request cache, saving you requests to YouTube and Spotify etc.


  • Spotify Extension
    • Wavelink no longer has it’s own Spotify Extension. Instead it has native support for LavaSrc and other source plugins.

    • Using LavaSrc with Wavelink 3 is just as simple as using the built-in Lavalink search types.

  • Removed all Track Types E.g. (YouTubeTrack, SoundCloudTrack)
  • wavelink.TrackSource removed Local and Unknown




Connecting in version 3 is similar to version 2.


It is recommended to use discord.py setup_hook to connect your nodes.

async def setup_hook(self) -> None:
    nodes = [wavelink.Node(uri="...", password="...")]

    # cache_capacity is EXPERIMENTAL. Turn it off by passing None
    await wavelink.Pool.connect(nodes=nodes, client=self, cache_capacity=100)

When your node connects you will recieve the wavelink.NodeReadyEventPayload via wavelink.on_wavelink_node_ready().

Searching and Playing#

Searching and playing tracks in version 3 is different, though should feel quite similar but easier.

# Search for tracks, with the default "ytsearch:" prefix.
tracks: wavelink.Search = await wavelink.Playable.search("Ocean Drive")
if not tracks:
    # No tracks were found...

# Search for tracks, with a URL.
tracks: wavelink.Search = await wavelink.Playable.search("https://www.youtube.com/watch?v=KDxJlW6cxRk")

# Search for tracks, using Spotify and the LavaSrc Plugin.
tracks: wavelink.Search = await wavelink.Playable.search("4b93D55xv3YCH5mT4p6HPn", source="spsearch")

# Search for tracks, using Spotify and the LavaSrc Plugin, with a URL.
# Notice we don't need to pass a source argument with URL based searches...
tracks: wavelink.Search = await wavelink.Playable.search("https://open.spotify.com/track/4b93D55xv3YCH5mT4p6HPn")

# Search for a playlist, using Spotify and the LavaSrc Plugin.
# or alternatively any other playlist URL from another source like YouTube.
tracks: wavelink.Search = await wavelink.Playable.search("https://open.spotify.com/playlist/37i9dQZF1DWXRqgorJj26U")

wavelink.Search should be used to annotate your variables. .search always returns a list[wavelink.Playable] or wavelink.Playlist, if no tracks were found this method will return an empty list which should be checked, E.g:

tracks: wavelink.Search = await wavelink.Playable.search(query)
if not tracks:
    # No tracks were found...

if isinstance(tracks, wavelink.Playlist):
    # tracks is a playlist...
    added: int = await player.queue.put_wait(tracks)
    await ctx.send(f"Added the playlist **`{tracks.name}`** ({added} songs) to the queue.")
    track: wavelink.Playable = tracks[0]
    await player.queue.put_wait(track)
    await ctx.send(f"Added **`{track}`** to the queue.")

when playing a song from a command it is advised to check whether the Player is currently playing anything first, with wavelink.Player.playing

if not player.playing:
    await player.play(track)

You can skip adding any track to your history queue in version 3 by passing add_history=False to wavelink.Player.play().

Wavelink does not advise using the wavelink.on_wavelink_track_end() event in most cases. Use this event only when you plan to not use AutoPlay at all. Since version 3 implements wavelink.AutoPlayMode.partial, a setting which skips fetching and recommending tracks, using this event is no longer recommended in most use cases.

To send track updates or do player updates, consider using wavelink.on_wavelink_track_start() instead.

async def on_wavelink_track_start(self, payload: wavelink.TrackStartEventPayload) -> None:
    player: wavelink.Player | None = payload.player
    if not player:

    original: wavelink.Playable | None = payload.original
    track: wavelink.Playable = payload.track

    embed: discord.Embed = discord.Embed(title="Now Playing")
    embed.description = f"**{track.title}** by `{track.author}`"

    if track.artwork:

    if original and original.recommended:
        embed.description += f"\n\n`This track was recommended via {track.source}`"

    if track.album.name:
        embed.add_field(name="Album", value=track.album.name)

    # Send this embed to a channel...
    # See: simple.py example on GitHub.


Please read the AutoPlay section for advice on how to properly use version 3 with AutoPlay.


Version 3 optimized AutoPlay and how it recommends tracks.

Available are currently 3 different AutoPlay modes. See: wavelink.AutoPlayMode

Setting wavelink.Player.autoplay to wavelink.AutoPlayMode.enabled will allow the player to fetch and recommend tracks based on your current listening history. This currently works with Spotify, YouTube and YouTube Music. This mode handles everything including looping, and prioritizes the Queue over the AutoQueue.

Setting wavelink.Player.autoplay to wavelink.AutoPlayMode.partial will allow the player to handle the automatic playing of the next track but will NOT recommend or fetch recommendations for playing in the future. This mode handles everything including looping.

Setting wavelink.Player.autoplay to wavelink.AutoPlayMode.disabled will stop the player from automatically playing tracks. You will need to use wavelink.on_wavelink_track_end() in this case.

AutoPlay also implements error safety. In the case of too many consecutive errors trying to play a track, AutoPlay will stop attempting until manually restarted by playing a track E.g. with wavelink.Player.play().

Pausing and Resuming#

Version 3 slightly changes pausing behaviour.

All logic is done in wavelink.Player.pause() and you simply pass a bool (True to pause and False to resume).

await player.pause(not player.paused)


Version 3 made some internal changes to wavelink.Queue.

The most noticeable is wavelink.Queue.mode which allows you to turn the Queue to either, wavelink.QueueMode.loop, wavelink.QueueMode.loop_all or wavelink.QueueMode.normal.


Version 3 has reworked the filters to hopefully be easier to use and feel more intuitive.

See: Filters. See: filters See: set_filters() See: play()

Some common recipes:

# Create a brand new Filters and apply it...
# You can use player.set_filters() for an easier way to reset.
filters: wavelink.Filters = wavelink.Filters()
await player.set_filters(filters)

# Retrieve the payload of any Filters instance...
filters: wavelink.Filters = player.filters

# Set some filters...
# You can set and reset individual filters at the same time...
filters: wavelink.Filters = player.filters
filters.timescale.set(pitch=1.2, speed=1.1, rate=1)

await player.set_filters(filters)

# Reset a filter...
filters: wavelink.Filters = player.filters

await player.set_filters(filters)

# Reset all filters...
filters: wavelink.Filters = player.filters

await player.set_filters(filters)

# Reset and apply filters easier method...
await player.set_filters()