[GH-ISSUE #33] In client mode, clienttoken POST looks very different than Spotify web client #22

Closed
opened 2026-02-27 20:07:44 +03:00 by kerem · 2 comments
Owner

Originally created by @tomballgithub on GitHub (Dec 26, 2025).
Original GitHub issue: https://github.com/misiektoja/spotify_monitor/issues/33

When doing POST to https://clienttoken.spotify.com/v1/clienttoken, I see this from the Spotify web client:

POST /v1/clienttoken HTTP/1.1
Host: clienttoken.spotify.com
Connection: keep-alive
Content-Length: 280
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
accept: application/json
sec-ch-ua: "Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"
content-type: application/json
sec-ch-ua-mobile: ?0
Origin: https://open.spotify.com
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://open.spotify.com/
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9

{
  "client_data": {
    "client_version": "1.2.80.333.g6d8aabfd",
    "client_id": "d8a5ed958d274c2e8ee717e6a4b0971d",
    "js_sdk_data": {
      "device_brand": "unknown",
      "device_model": "unknown",
      "os": "windows",
      "os_version": "NT 10.0",
      "device_id": "f84c52d20acf6d2d5c9baa0fea01837e",
      "device_type": "computer"
    }
  }
}

This is from spotify_monitor:

POST /v1/clienttoken HTTP/1.1
User-Agent: Spotify/123661855 (Linux; x86_64)
Accept-Encoding: gzip, deflate, br, zstd
Accept: application/x-protobuf
Connection: keep-alive
Host: clienttoken.spotify.com
Pragma: no-cache
Cache-Control: no-cache, no-store, max-age=0
Content-Type: application/x-protobuf
Origin: https://clienttoken.spotify.com
Accept-Language: en-Latn-GB,en-GB;q=0.9,en;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: empty
Content-Length: 126

<Binary Data>

Although origin is different and referrer is missing, I am really focusing on the protobuf data showing up as binary rather than clear text. It looks purposeful in the code, so not sure if something has changed on the Spotify side and the code needs updating, or what. A major difference like this could be a clear indicator of a non-Spotify app using this API

Image

@Thereallo1026 had noted that the format was as follows, which matches what I see the official client doing:

export interface Payload {
  client_data: {
    client_version: string;
    client_id: string;
    js_sdk_data: JsSdkData;
  };
}

interface JsSdkData {
  device_brand: string;
  device_model: string;
  os: string;
  os_version: string;
  device_id: string;
  device_type: string;
}
Originally created by @tomballgithub on GitHub (Dec 26, 2025). Original GitHub issue: https://github.com/misiektoja/spotify_monitor/issues/33 When doing POST to https://clienttoken.spotify.com/v1/clienttoken, I see this from the Spotify web client: ``` POST /v1/clienttoken HTTP/1.1 Host: clienttoken.spotify.com Connection: keep-alive Content-Length: 280 sec-ch-ua-platform: "Windows" User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0 accept: application/json sec-ch-ua: "Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24" content-type: application/json sec-ch-ua-mobile: ?0 Origin: https://open.spotify.com Sec-Fetch-Site: same-site Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://open.spotify.com/ Accept-Encoding: gzip, deflate, br, zstd Accept-Language: en-US,en;q=0.9 { "client_data": { "client_version": "1.2.80.333.g6d8aabfd", "client_id": "d8a5ed958d274c2e8ee717e6a4b0971d", "js_sdk_data": { "device_brand": "unknown", "device_model": "unknown", "os": "windows", "os_version": "NT 10.0", "device_id": "f84c52d20acf6d2d5c9baa0fea01837e", "device_type": "computer" } } } ``` This is from spotify_monitor: ``` POST /v1/clienttoken HTTP/1.1 User-Agent: Spotify/123661855 (Linux; x86_64) Accept-Encoding: gzip, deflate, br, zstd Accept: application/x-protobuf Connection: keep-alive Host: clienttoken.spotify.com Pragma: no-cache Cache-Control: no-cache, no-store, max-age=0 Content-Type: application/x-protobuf Origin: https://clienttoken.spotify.com Accept-Language: en-Latn-GB,en-GB;q=0.9,en;q=0.8 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: no-cors Sec-Fetch-Dest: empty Content-Length: 126 <Binary Data> ``` Although origin is different and referrer is missing, I am really focusing on the protobuf data showing up as binary rather than clear text. It looks purposeful in the code, so not sure if something has changed on the Spotify side and the code needs updating, or what. A major difference like this could be a clear indicator of a non-Spotify app using this API <img width="811" height="215" alt="Image" src="https://github.com/user-attachments/assets/53d2c4b1-fecd-4e04-9c2c-430087be96e9" /> @Thereallo1026 had noted that the format was as follows, which matches what I see the official client doing: ``` export interface Payload { client_data: { client_version: string; client_id: string; js_sdk_data: JsSdkData; }; } interface JsSdkData { device_brand: string; device_model: string; os: string; os_version: string; device_id: string; device_type: string; } ```
kerem closed this issue 2026-02-27 20:07:44 +03:00
Author
Owner

@misiektoja commented on GitHub (Dec 27, 2025):

Yes, this is expected because in client mode we are simulating Spotify desktop client behavior, not the Spotify web client. This accounts for the differences. It might be that something should be updated, but since it is a really tedious task, I would suggest we leave it for an update once it stops working.

<!-- gh-comment-id:3693535876 --> @misiektoja commented on GitHub (Dec 27, 2025): Yes, this is expected because in `client` mode we are simulating Spotify desktop client behavior, not the Spotify web client. This accounts for the differences. It might be that something should be updated, but since it is a really tedious task, I would suggest we leave it for an update once it stops working.
Author
Owner

@tomballgithub commented on GitHub (Dec 27, 2025):

Got it. For future purposes, I'll just document some code I made for build_clienttoken_request_protobuf(), although some client data was hard-coded:

        data = {
  "client_data": {
    "client_version": "1.2.80.333.g6d8aabfd",
    "client_id": f"{SP_CACHED_CLIENT_ID}",
    "js_sdk_data": {
      "device_brand": "unknown",
      "device_model": "unknown",
      "os": "windows",
      "os_version": "NT 10.0",
      "device_id": f"{DEVICE_ID}",
      "device_type": "computer"
    }
  }
}

And then using json=body rather than data=body in spotify_get_client_token()
response = req.post(CLIENTTOKEN_URL, headers=headers, json=body, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)

<!-- gh-comment-id:3693548331 --> @tomballgithub commented on GitHub (Dec 27, 2025): Got it. For future purposes, I'll just document some code I made for build_clienttoken_request_protobuf(), although some client data was hard-coded: ``` data = { "client_data": { "client_version": "1.2.80.333.g6d8aabfd", "client_id": f"{SP_CACHED_CLIENT_ID}", "js_sdk_data": { "device_brand": "unknown", "device_model": "unknown", "os": "windows", "os_version": "NT 10.0", "device_id": f"{DEVICE_ID}", "device_type": "computer" } } } ``` And then using json=body rather than data=body in spotify_get_client_token() ` response = req.post(CLIENTTOKEN_URL, headers=headers, json=body, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)`
Sign in to join this conversation.
No labels
Stale
bug
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/spotify_monitor#22
No description provided.