Last updated on November 12, 2023
Since the official Soulseek client and server is proprietary software, this documentation has been compiled thanks to years of reverse engineering efforts. To preserve the health of the Soulseek network, please do not modify or extend the protocol in ways that negatively impact the network.
If you find any inconsistencies, errors or omissions in the documentation, please report them.
Number |
---|
1 Byte |
Number |
---|
2 Bytes (little-endian) |
Number |
---|
4 Bytes (little-endian) |
Number |
---|
8 Bytes (little-endian) |
Number |
---|
1 Byte (0 or 1) |
Length of String | String |
---|---|
uint32 | bytes |
Type | Connection |
---|---|
P | Peer To Peer |
F | File Transfer |
D | Distributed Network |
Reason | Description |
---|---|
INVALIDUSERNAME | Username is longer than 30 characters or contains invalid characters (non-ASCII) |
INVALIDPASS | Password for existing user is incorrect |
INVALIDVERSION | Client version is outdated |
Code | Status |
---|---|
0 | Offline |
1 | Away |
2 | Online |
Code | Direction |
---|---|
0 | Download from Peer |
1 | Upload to Peer |
String | Comments |
---|---|
Banned | SoulseekQt uses ‘File not shared.’ instead |
Cancelled | |
Complete | |
File not shared. | Note: Ends with a dot |
File read error. | Note: Ends with a dot |
Pending shutdown. | Note: Ends with a dot |
Queued | |
Too many files | |
Too many megabytes |
String | Comments |
---|---|
Blocked country | Exclusive to Nicotine+, no longer used in Nicotine+ >=3.2.0 |
Disallowed extension | Sent by Soulseek NS for filtered extensions |
File not shared | Exclusive to Nicotine+, no longer used in Nicotine+ >=3.1.1 |
Remote file error | Sent by Soulseek NS in response to legacy download requests |
User limit of x megabytes exceeded | Exclusive to Nicotine+, no longer used in Nicotine+ >=3.1.1 |
User limit of x files exceeded | Exclusive to Nicotine+, no longer used in Nicotine+ >=3.1.1 |
Code | Attribute (unit) |
---|---|
0 | Bitrate (kbps) |
1 | Duration (seconds) |
2 | VBR (0 or 1) |
3 | Encoder (unused) |
4 | Sample Rate (Hz) |
5 | Bit Depth (bits) |
These combinations are actively used by clients. Other combinations are discouraged, unless the official client makes changes.
Send | Receive |
---|---|
Send to Server | Receive from Server |
Server messages are used by clients to interface with the server. In Nicotine+, these messages are defined in slskmessages.py.
If you want a Soulseek server, check out Soulfind. Soulfind is obviously not exactly the same as the official proprietary Soulseek server, but it handles the protocol well enough (and can be modified).
Message Length | Code | Message Contents |
---|---|---|
uint32 | uint32 | … |
We send this to the server right after the connection has been established. Server responds with the greeting message.
Message:
Data | Message Length | Message Code | Username Length | Username | Password Length | Password |
---|---|---|---|---|---|---|
Human | 72 | 1 | 8 | username | 8 | password |
Hex | 48 00 00 00 | 01 00 00 00 | 08 00 00 00 | 75 73 65 72 6e 61 6d 65 | 08 00 00 00 | 70 61 73 73 77 6f 72 64 |
… continued:
Data | Version | Hash Length | Hash | Minor Version |
---|---|---|---|---|
Human | 160 | 32 | d51c9a7e9353746a6020f9602d452929 | 1 |
Hex | a0 00 00 00 | 20 00 00 00 | 64 35 31 63 39 61 37 65 39 33 35 33 37 34 36 61 36 30 32 30 66 39 36 30 32 64 34 35 32 39 32 39 | 01 00 00 00 |
Message as a Hex Stream:
We send this to the server to indicate the port number that we listen on (2234 by default).
If this value is set to zero, or the message is not sent upon login (which defaults the listen port to 0), remote clients handling a ConnectToPeer message will fail to properly purge the request. Confirmed in SoulseekQt 2020.3.12, but probably impacts most or all other versions.
We send this to the server to ask for a peer’s address (IP address and port), given the peer’s username.
Used to be kept updated about a user’s stats. When a user’s stats have changed, the server sends a GetUserStats response message with the new user stats.
Used when we no longer want to be kept updated about a user’s stats.
The server tells us if a user has gone away or has returned.
Either we want to say something in the chatroom, or someone else did.
We send this message to the server when we want to join a room. If the room doesn’t exist, it is created.
Server responds with this message when we join a room. Contains users list with data on everyone.
We send this to the server when we want to leave a room.
The server tells us someone has just joined a room we’re in.
The server tells us someone has just left a room we’re in.
Either we ask server to tell someone else we want to establish a connection with them, or server tells us someone wants to connect with us. Used when the side that wants a connection can’t establish it, and tries to go the other way around (direct connection has failed).
See also: Peer Connection Message Order
Chat phrase sent to someone or received by us in private.
We send this to the server to confirm that we received a private message. If we don’t send it, the server will keep sending the chat phrase to us.
OBSOLETE, use RoomSearch server message
We send this to the server when we search for something in a room.
We send this to the server when we search for something. Alternatively, the server sends this message outside the distributed network to tell us that someone is searching for something, currently used for UserSearch and RoomSearch requests.
The token is a number generated by the client and is used to track the search results.
We send our new status to the server. Status is a way to define whether we’re available (online) or busy (away).
1 = Away
2 = Online
We send this to the server at most once per minute to ensure the connection stays alive.
Nicotine+ uses TCP keepalive instead.
OBSOLETE, no longer used
OBSOLETE, use SendUploadSpeed server message
We used to send this after a finished download to let the server update the speed statistics for a user.
We send this to server to indicate the number of folder and files that we share.
The server sends this to indicate a change in a user’s statistics, if we’ve requested to watch the user in WatchUser previously. A user’s stats can also be requested by sending a GetUserStats message to the server, but WatchUser should be used instead.
OBSOLETE, no longer sent by the server
The server sends this to indicate if someone has download slots available or not.
The server sends this if someone else logged in under our nickname, and then disconnects us.
We send this to the server when we search a specific user’s shares. The token is a number generated by the client and is used to track the search results.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We send this to the server when we add an item to our likes list.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We send this to the server when we remove an item from our likes list.
DEPRECATED, used in Soulseek NS but not SoulseekQt
The server sends us a list of personal recommendations and a number for each.
DEPRECATED, used in Soulseek NS but not SoulseekQt
The server sends us a list of global recommendations and a number for each.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We ask the server for a user’s liked and hated interests. The server responds with a list of interests.
OBSOLETE
We send this to the server to run an admin command (e.g. to ban or silence a user) if we have admin status on the server.
OBSOLETE, use PlaceInQueueResponse peer message
The server sends this to indicate change in place in queue while we’re waiting for files from another peer.
OBSOLETE, no longer sent by the server
The server tells us a new room has been added.
OBSOLETE, no longer sent by the server
The server tells us a room has been removed.
The server tells us a list of rooms and the number of users in them. When connecting to the server, the server only sends us rooms with at least 5 users. A few select rooms are also excluded, such as nicotine and The Lobby. Requesting the room list yields a response containing the missing rooms.
OBSOLETE, no results even with official client
We send this to search for an exact file name and folder, to find other sources.
A global message from the server admin has arrived.
OBSOLETE, no longer used
We send this to get a global list of all users online.
OBSOLETE, no longer used
Server message for tunneling a chat message.
The server sends us a list of privileged users, a.k.a. users who have donated.
We inform the server if we have a distributed parent or not. If not, the server eventually sends us a PossibleParents message with a list of possible parents to connect to. If no candidates are found, no such message is sent by the server, and we eventually become a branch root.
DEPRECATED, sent by Soulseek NS but not SoulseekQt
We send the IP address of our parent to the server.
The server informs us about the minimum upload speed required to become a parent in the distributed network.
The server sends us a speed ratio determining the number of children we can have in the distributed network. The maximum number of children is our upload speed divided by the speed ratio.
OBSOLETE, no longer sent by the server
OBSOLETE, no longer sent by the server
OBSOLETE, no longer sent by the server
OBSOLETE, no longer sent by the server
OBSOLETE, no longer sent by the server
The server sends us the username of a new privileged user, which we add to our list of global privileged users.
We ask the server how much time we have left of our privileges. The server responds with the remaining time, in seconds.
The server sends us an embedded distributed message. The only type of distributed message sent at present is DistribSearch (distributed code 3). If we receive such a message, we are a branch root in the distributed network, and we distribute the embedded message (not the unpacked distributed message) to our child peers.
We tell the server if we want to accept child nodes.
The server send us a list of max 10 possible distributed parents to connect to. Messages of this type are sent to us at regular intervals, until we tell the server we don’t need more possible parents with a HaveNoParent message.
The received list always contains users whose upload speed is higher than our own. If we have the highest upload speed on the server, we become a branch root, and start receiving SearchRequest messages directly from the server.
We send the server one of our wishlist search queries at each interval.
The server tells us the wishlist search interval.
This interval is almost always 12 minutes, or 2 minutes for privileged users.
DEPRECATED, used in Soulseek NS but not SoulseekQt
The server sends us a list of similar users related to our interests.
DEPRECATED, used in Soulseek NS but not SoulseekQt
The server sends us a list of recommendations related to a specific item, which is usually present in the like/dislike list or an existing recommendation list.
DEPRECATED, used in Soulseek NS but not SoulseekQt
The server sends us a list of similar users related to a specific item, which is usually present in the like/dislike list or recommendation list.
The server returns a list of tickers in a chat room.
Tickers are customizable, user-specific messages that appear on chat room walls.
The server sends us a new ticker that was added to a chat room.
Tickers are customizable, user-specific messages that appear on chat room walls.
The server informs us that a ticker was removed from a chat room.
Tickers are customizable, user-specific messages that appear on chat room walls.
We send this to the server when we change our own ticker in a chat room. Sending an empty ticker string removes any existing ticker in the room.
Tickers are customizable, user-specific messages that appear on chat room walls.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We send this to the server when we add an item to our hate list.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We send this to the server when we remove an item from our hate list.
We send this to the server to search files shared by users who have joined a specific chat room.
The token is a number generated by the client and is used to track the search results.
We send this after a finished upload to let the server update the speed statistics for ourselves.
DEPRECATED, use WatchUser and GetUserStatus server messages
We ask the server whether a user is privileged or not.
We give (part of) our privileges, specified in days, to another user on the network.
DEPRECATED, sent by Soulseek NS but not SoulseekQt
DEPRECATED, no longer used
We tell the server what our position is in our branch (xth generation) on the distributed network.
We tell the server the username of the root of the branch we’re in on the distributed network.
DEPRECATED, sent by Soulseek NS but not SoulseekQt
We tell the server the maximum number of generation of children we have on the distributed network.
The server asks us to reset our distributed parent and children.
The server sends us a list of room users that we can alter (add operator abilities / dismember).
We send this to inform the server that we’ve added a user to a private room.
We send this to inform the server that we’ve removed a user from a private room.
We send this to the server to remove our own membership of a private room.
We send this to the server to stop owning a private room.
OBSOLETE, no longer used
Unknown purpose
The server sends us this message when we are added to a private room.
The server sends us this message when we are removed from a private room.
We send this when we want to enable or disable invitations to private rooms.
We send this to the server to change our password. We receive a response if our password changes.
We send this to the server to add private room operator abilities to a user.
We send this to the server to remove private room operator abilities from a user.
The server send us this message when we’re given operator abilities in a private room.
The server send us this message when our operator abilities are removed in a private room.
The server sends us a list of operators in a specific room, that we can remove operator abilities from.
Sends a broadcast private message to the given list of online users.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We ask the server to send us messages from all public rooms, also known as public room feed.
DEPRECATED, used in Soulseek NS but not SoulseekQt
We ask the server to stop sending us messages from all public rooms, also known as public room feed.
DEPRECATED, used in Soulseek NS but not SoulseekQt
The server sends this when a new message has been written in the public room feed (every single line written in every public room).
OBSOLETE, server sends empty list as of 2018
The server returns a list of related search terms for a search query.
We send this to say we can’t connect to peer after it has asked us to connect. We receive this if we asked peer to connect and it can’t do this. This message means a connection can’t be established either way.
See also: Peer Connection Message Order
Server tells us a new room cannot be created.
This message only seems to be sent if you try to create a room with the same name as an existing private room. In other cases, such as using a room name with leading or trailing spaces, only a private message containing an error message is sent.
Send | Receive |
---|---|
Send to Peer | Receive from Peer |
Peer init messages are used to initiate a ‘P’, ‘F’ or ‘D’ connection to a peer. In Nicotine+, these messages are defined in slskmessages.py.
Message Length | Code | Message Contents |
---|---|---|
uint32 | uint8 | … |
Code | Message |
---|---|
0 | Pierce Firewall |
1 | Peer Init |
Used by SoulseekQt, Nicotine+ 3.2.1 and later, Soulseek.NET-based clients (slskd, Seeker)
Unlike SoulseekQt, Nicotine+ and Soulseek.NET-based clients skip step 5 in favor of letting the connection attempt time out on User A’s end.
Used by Soulseek NS, Nicotine+ 3.2.0 and earlier, Museek+, soulseeX
This message is sent in response to an indirect connection request from another user. If the message goes through to the user, the connection is ready. The token is taken from the ConnectToPeer server message.
See also: Peer Connection Message Order
This message is sent to initiate a direct connection to another peer. The token is apparently always 0 and ignored.
See also: Peer Connection Message Order
Send | Receive |
---|---|
Send to Peer | Receive from Peer |
Peer messages are sent to peers over a ‘P’ connection. Only a single active connection to a peer is allowed. In Nicotine+, these messages are defined in slskmessages.py.
Message Length | Code | Message Contents |
---|---|---|
uint32 | uint32 | … |
Code | Message | Status |
---|---|---|
1 | Private Message | Obsolete, contents unknown |
4 | Shared File List Request | |
5 | Shared File List Response | |
8 | File Search Request | Obsolete |
9 | File Search Response | |
10 | Room Invitation | Obsolete, contents unknown |
14 | Cancelled Queued Transfer | Obsolete, contents unknown |
15 | User Info Request | |
16 | User Info Response | |
33 | Send Connect Token | Obsolete, contents unknown |
34 | Move Download To Top | Obsolete, contents unknown |
36 | Folder Contents Request | |
37 | Folder Contents Response | |
40 | Transfer Request | |
41 | Download Response | Deprecated |
41 | Upload Response | |
42 | Upload Placehold | Obsolete |
43 | Queue Upload | |
44 | Place In Queue Response | |
46 | Upload Failed | |
47 | Exact File Search Request | Obsolete, contents unknown |
48 | Queued Downloads | Obsolete, contents unknown |
49 | Indirect File Search Request | Obsolete, contents unknown |
50 | Upload Denied | |
51 | Place In Queue Request | |
52 | Upload Queue Notification | Deprecated |
We send this to a peer to ask for a list of shared files.
A peer responds with a list of shared files after we’ve sent a SharedFileListRequest.
OBSOLETE, use UserSearch server message
We send this to the peer when we search for a file. Alternatively, the peer sends this to tell us it is searching for a file.
A peer sends this message when it has a file search match. The token is taken from original FileSearch, UserSearch or RoomSearch server message.
We ask the other peer to send us their user information, picture and all.
A peer responds with this after we’ve sent a UserInfoRequest.
We ask the peer to send us the contents of a single folder.
A peer responds with the contents of a particular folder (with all subfolders) after we’ve sent a FolderContentsRequest.
This message is sent by a peer once they are ready to start uploading a file to us. A TransferResponse message is expected from the recipient, either allowing or rejecting the upload attempt.
This message was formerly used to send a download request (direction 0) as well, but Nicotine+ >= 3.0.3, Museek+ and the official clients use the QueueUpload peer message for this purpose today.
DEPRECATED, use QueueUpload to request files
Response to TransferRequest
We (or the other peer) either agrees, or tells the reason for rejecting the file download.
Response to TransferRequest
We (or the other peer) either agrees, or tells the reason for rejecting the file upload.
OBSOLETE, no longer used
This message is used to tell a peer that an upload should be queued on their end. Once the recipient is ready to transfer the requested file, they will send a TransferRequest to us.
The peer replies with the upload queue placement of the requested file.
This message is sent whenever a file connection of an active upload closes. Soulseek NS clients can also send this message when a file cannot be read. The recipient either re-queues the upload (download on their end), or ignores the message if the transfer finished.
This message is sent to reject QueueUpload attempts and previously queued files. The reason for rejection will appear in the transfer list of the recipient.
This message is sent when asking for the upload queue placement of a file.
DEPRECATED, sent by Soulseek NS but not SoulseekQt
This message is sent to inform a peer about an upload attempt initiated by us.
Send | Receive |
---|---|
Send to Peer | Receive from Peer |
File messages are sent to peers over a ‘F’ connection, and do not have messages codes associated with them.
Message Contents |
---|
… |
Message |
---|
File Transfer Init |
File Offset |
We send this to a peer via a ‘F’ connection to tell them that we want to start uploading a file. The token is the same as the one previously included in the TransferRequest peer message.
Note that slskd and Nicotine+ <= 3.0.2 use legacy download requests, and send this message when initializing our file upload connection from their end.
We send this to the uploading peer at the beginning of a ‘F’ connection, to tell them how many bytes of the file we’ve previously downloaded. If nothing was downloaded, the offset is 0.
Note that Soulseek NS fails to read the size of an incomplete download if more than 2 GB of the file has been downloaded, and the download is resumed. In consequence, the client sends an invalid file offset of -1.
Send | Receive |
---|---|
Send to Node | Receive from Node |
Distributed messages are sent to peers over a ‘D’ connection, and are used for the distributed search network. Only a single active connection to a peer is allowed. In Nicotine+, these messages are defined in slskmessages.py.
Message Length | Code | Message Contents |
---|---|---|
uint32 | uint8 | … |
Code | Message | Status |
---|---|---|
0 | Ping | Deprecated |
3 | Search Request | |
4 | Branch Level | |
5 | Branch Root | |
7 | Child Depth | Deprecated |
93 | Embedded Message |
DEPRECATED, sent by Soulseek NS but not SoulseekQt
We ping distributed children every 60 seconds.
Search request that arrives through the distributed network. We transmit the search request to our child peers.
We tell our distributed children what our position is in our branch (xth generation) on the distributed network.
If we receive a branch level of 0 from a parent, we should mark the parent as our branch root, since they won’t send a DistribBranchRoot message in this case.
We tell our distributed children the username of the root of the branch we’re in on the distributed network.
This message should not be sent when we’re the branch root.
DEPRECATED, sent by Soulseek NS but not SoulseekQt
We tell our distributed parent the maximum number of generation of children we have on the distributed network.
A branch root sends us an embedded distributed message. We unpack the distributed message and distribute it to our child peers.
The only type of distributed message sent at present is DistribSearch (distributed code 3).
This documentation exists thanks to efforts from the following projects: