[GH-ISSUE #1319] Possible ratelimiting introduced by Spotify #599

Closed
opened 2026-02-27 19:31:30 +03:00 by kerem · 20 comments
Owner

Originally created by @x528491x on GitHub (Aug 31, 2024).
Original GitHub issue: https://github.com/librespot-org/librespot/issues/1319

When shuffling between tracks too fast, I get the error message below while fetching the audio_key for decryption:

Error { kind: Unavailable, error: AesKey }

I had not run into such errors a few days ago, and I have seen other people report the same error recently on discord servers.

Originally created by @x528491x on GitHub (Aug 31, 2024). Original GitHub issue: https://github.com/librespot-org/librespot/issues/1319 When shuffling between tracks too fast, I get the error message below while fetching the audio_key for decryption: `Error { kind: Unavailable, error: AesKey }` I had not run into such errors a few days ago, and I have seen other people report the same error recently on discord servers.
kerem 2026-02-27 19:31:30 +03:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@x528491x commented on GitHub (Sep 8, 2024):

Spotify might be sending a retry after header like it does for most rate limited endpoints.

The header, if it is sent, needs to be exposed.

<!-- gh-comment-id:2336538506 --> @x528491x commented on GitHub (Sep 8, 2024): Spotify might be sending a retry after header like it does for most rate limited endpoints. The header, if it is sent, needs to be exposed.
Author
Owner

@roderickvd commented on GitHub (Sep 8, 2024):

IIRC I did implement "retry after" in dev, no?

Edit: but only for HTTP and getting the key goes through Mercury instead.

<!-- gh-comment-id:2336631618 --> @roderickvd commented on GitHub (Sep 8, 2024): IIRC I did implement "retry after" in dev, no? Edit: but only for HTTP and getting the key goes through Mercury instead.
Author
Owner

@x528491x commented on GitHub (Sep 8, 2024):

A retry mechanism for the audio keys is direly needed as the rate limiting almost makes my entire project unusable.

<!-- gh-comment-id:2336645305 --> @x528491x commented on GitHub (Sep 8, 2024): A retry mechanism for the audio keys is direly needed as the rate limiting almost makes my entire project unusable.
Author
Owner

@roderickvd commented on GitHub (Sep 8, 2024):

If you're a developer yourself, a PR would be welcome.

Edit: curious, what is your situation that it's unusable today?

<!-- gh-comment-id:2336646125 --> @roderickvd commented on GitHub (Sep 8, 2024): If you're a developer yourself, a PR would be welcome. Edit: curious, what is your situation that it's unusable today?
Author
Owner

@x528491x commented on GitHub (Sep 8, 2024):

If you're a developer yourself, a PR would be welcome.

I'm a golang developer and my first and only rust project is using this library 😅

I do intend to learn rust and contribute to this project, but for now I'll wait for someone more knowledgeable and well versed with the project's codebase to work on it.

<!-- gh-comment-id:2336647586 --> @x528491x commented on GitHub (Sep 8, 2024): > If you're a developer yourself, a PR would be welcome. I'm a golang developer and my first and only rust project is using this library 😅 I do intend to learn rust and contribute to this project, but for now I'll wait for someone more knowledgeable and well versed with the project's codebase to work on it.
Author
Owner

@michaelherger commented on GitHub (Sep 8, 2024):

Is there a good reason for the rate limiting? Or could the reason for the rate limiting be addressed instead of adding retry?

<!-- gh-comment-id:2336651570 --> @michaelherger commented on GitHub (Sep 8, 2024): Is there a good reason for the rate limiting? Or could the reason for the rate limiting be addressed instead of adding retry?
Author
Owner

@x528491x commented on GitHub (Sep 8, 2024):

Is there a good reason for the rate limiting? Or could the reason for the rate limiting be addressed instead of adding retry?

My project revolves around curating music after experiencing the "vibe" of a song, almost in a speedrun manner. (Might be the most cringe description)

To do so, I let the user quickly shuffle between tracks in a playlist, and automatically jump to a random position (somewhere around the middle of the track) and play it for a couple of seconds, giving the user the option to add it to their playlist should they have liked the preview.

The whole idea is that time is precious and a playlist can never be long enough ;)

As to why Spotify introduced the rate limiting that did not exist before, I do not know why. Probably they just discovered the lapse while pushing the other changes they had made to the API lately.

I cannot really "address" the cause of my project being rate limited due to the very nature of it. However, I'll still be happy to have it working as before while complying to Spotify's rate limit policies which I have yet to see stated with actual limits anywhere on their documentation.

<!-- gh-comment-id:2336657519 --> @x528491x commented on GitHub (Sep 8, 2024): > Is there a good reason for the rate limiting? Or could the reason for the rate limiting be addressed instead of adding retry? > My project revolves around curating music after experiencing the "vibe" of a song, almost in a speedrun manner. (Might be the most cringe description) To do so, I let the user quickly shuffle between tracks in a playlist, and automatically jump to a random position (somewhere around the middle of the track) and play it for a couple of seconds, giving the user the option to add it to their playlist should they have liked the preview. The whole idea is that time is precious and a playlist can never be long enough ;) As to why Spotify introduced the rate limiting that did not exist before, I do not know why. Probably they just discovered the lapse while pushing the other changes they had made to the API lately. I cannot really "address" the cause of my project being rate limited due to the very nature of it. However, I'll still be happy to have it working as before while complying to Spotify's rate limit policies which I have yet to see stated with actual limits anywhere on their documentation.
Author
Owner

@michaelherger commented on GitHub (Sep 9, 2024):

Ok, sounds like a good reason to be rate limited 😄. Your application certainly is not typical behaviour, and from their point of view it might look like someone trying to mass download music files. Why else would a normal user try to get tracks in quick succession? Your application is out of the ordinary.

Whether they changed rate limiting recently or not, we can only speculate. But if I were a service provider, I'd probably block this kind of pattern, too.

<!-- gh-comment-id:2337036434 --> @michaelherger commented on GitHub (Sep 9, 2024): Ok, sounds like a good reason to be rate limited 😄. Your application certainly is not typical behaviour, and from their point of view it might look like someone trying to mass download music files. Why else would a normal user try to get tracks in quick succession? Your application is out of the ordinary. Whether they changed rate limiting recently or not, we can only speculate. But if I were a service provider, I'd probably block this kind of pattern, too.
Author
Owner

@x528491x commented on GitHub (Sep 9, 2024):

Ok, sounds like a good reason to be rate limited 😄. Your application certainly is not typical behaviour, and from their point of view it might look like someone trying to mass download music files. Why else would a normal user try to get tracks in quick succession? Your application is out of the ordinary.

Whether they changed rate limiting recently or not, we can only speculate. But if I were a service provider, I'd probably block this kind of pattern, too.

I do know that my application is in the grey area in regards to spamming requests for tracks one after the other in short intervals.

But that is... kind of the appeal of it. To help the user take shortcuts in the process and save some time.

Sure, I get how Spotify might view my application as a batch downloader of sorts, and I admit that my use case may be mildly abusive, but I want to adhere to their rate limits so as to not face failed requests nor get my account banned.

Retry-After would fix my application:

Once the user requests a shuffle, I'll send a request to fetch the next song,

and if I'm already rate limited, I'll wait for the Retry-After interval to be reached, and until then I'll continue playing the current track and notify the user that the shuffle will be done in x seconds.

<!-- gh-comment-id:2337051602 --> @x528491x commented on GitHub (Sep 9, 2024): > Ok, sounds like a good reason to be rate limited 😄. Your application certainly is not typical behaviour, and from their point of view it might look like someone trying to mass download music files. Why else would a normal user try to get tracks in quick succession? Your application is out of the ordinary. > > Whether they changed rate limiting recently or not, we can only speculate. But if I were a service provider, I'd probably block this kind of pattern, too. I do know that my application is in the grey area in regards to spamming requests for tracks one after the other in short intervals. But that is... kind of the appeal of it. To help the user take shortcuts in the process and save some time. Sure, I get how Spotify might view my application as a batch downloader of sorts, and I admit that my use case may be mildly abusive, but I want to adhere to their rate limits so as to not face failed requests nor get my account banned. Retry-After would fix my application: Once the user requests a shuffle, I'll send a request to fetch the next song, and if I'm already rate limited, I'll wait for the Retry-After interval to be reached, and until then I'll continue playing the current track and notify the user that the shuffle will be done in x seconds.
Author
Owner

@roderickvd commented on GitHub (Sep 9, 2024):

In Mercury, which is just TCP-based, there is no retry after header. Until someone adds rate limiting there in librespot (you could mostly copy it from the way it's done with HTTP) I suggest that you do it on the caller end (your end).

<!-- gh-comment-id:2337176959 --> @roderickvd commented on GitHub (Sep 9, 2024): In Mercury, which is just TCP-based, there is no retry after header. Until someone adds rate limiting there in librespot (you could mostly copy it from the way it's done with HTTP) I suggest that you do it on the caller end (your end).
Author
Owner

@kingosticks commented on GitHub (Sep 9, 2024):

There are some rate limiting error codes in the esdk ( kSpErrorAPIRateLimited, kSpErrorPlaybackRateLimited, kSpErrorSkipLimitReached), and they've been there for a while, but I can't tell if it was before switching to http media. Sadly I also couldn't find any explicit description of those limits in the public esdk docs. Perhaps there are some hints in old firmware blobs you could try.

Have you considered using their unencrypted (30 second?) song previews instead? Might that work? I think it could be considered a breach of their ToS but maybe it's a friendlier thing to do?

<!-- gh-comment-id:2337298727 --> @kingosticks commented on GitHub (Sep 9, 2024): There are some rate limiting error codes in the esdk ( kSpErrorAPIRateLimited, kSpErrorPlaybackRateLimited, kSpErrorSkipLimitReached), and they've been there for a while, but I can't tell if it was before switching to http media. Sadly I also couldn't find any explicit description of those limits in the public esdk docs. Perhaps there are some hints in old firmware blobs you could try. Have you considered using their unencrypted (30 second?) song previews instead? Might that work? I think it could be considered a breach of their ToS but maybe it's a friendlier thing to do?
Author
Owner

@x528491x commented on GitHub (Sep 9, 2024):

There are some rate limiting error codes in the esdk ( kSpErrorAPIRateLimited, kSpErrorPlaybackRateLimited, kSpErrorSkipLimitReached), and they've been there for a while, but I can't tell if it was before switching to http media. Sadly I also couldn't find any explicit description of those limits in the public esdk docs. Perhaps there are some hints in old firmware blobs you could try.

Have you considered using their unencrypted (30 second?) song previews instead? Might that work? I think it could be considered a breach of their ToS but maybe it's a friendlier thing to do?

Oh, my first prototype used the 30 second previews and I had thought that using the previews was the easy way to go until I ran into availability issues - not every track has a preview file. The availability is further deteriorated for indie artists and less popular languages.

Here is a Spotify employee confirming the availability issues

And apparently Spotify gives the artists the option to select what portion of the track they want to list for preview.

And if the artist had skipped generating a preview, the Spotify employee who approves the listing does it manually.

Source for the above

This is a hit or miss in two ways - The employee might skip generating the preview too, leaving no preview for the track at all,

or select a random portion of the track that has no real accoustic significance.

This is why I changed my approach and took it upon myself to generate a preview off of the actual track which would always be available, no matter what.

I too could add ratelimiting or a retry mechanism in my app. However it would be a better fix if we could get the Retry-After header as the implementation would be an educated one instead of it being a guessing game.

And while my use case is probably very unique, I reckon that addressing it in librespot would benefit its other users too as this rate limit is sometimes surprisingly easy to hit, especially when the application gives the control of playback to many users.

<!-- gh-comment-id:2337472562 --> @x528491x commented on GitHub (Sep 9, 2024): > There are some rate limiting error codes in the esdk ( kSpErrorAPIRateLimited, kSpErrorPlaybackRateLimited, kSpErrorSkipLimitReached), and they've been there for a while, but I can't tell if it was before switching to http media. Sadly I also couldn't find any explicit description of those limits in the public esdk docs. Perhaps there are some hints in old firmware blobs you could try. > > Have you considered using their unencrypted (30 second?) song previews instead? Might that work? I think it could be considered a breach of their ToS but maybe it's a friendlier thing to do? Oh, my first prototype used the 30 second previews and I had thought that using the previews was the easy way to go until I ran into availability issues - not every track has a preview file. The availability is further deteriorated for indie artists and less popular languages. [Here is a Spotify employee confirming the availability issues](https://community.spotify.com/t5/Spotify-for-Developers/30-second-song-preview-not-showing-up-for-song/m-p/4992092/highlight/true#M680) And apparently Spotify gives the artists the option to select what portion of the track they want to list for preview. And if the artist had skipped generating a preview, the Spotify employee who approves the listing does it manually. [Source for the above](https://www.reddit.com/r/spotify/s/jG1B8kxY1e) This is a hit or miss in two ways - The employee might skip generating the preview too, leaving no preview for the track at all, or select a random portion of the track that has no real accoustic significance. This is why I changed my approach and took it upon myself to generate a preview off of the actual track which would always be available, no matter what. I too could add ratelimiting or a retry mechanism in my app. However it would be a better fix if we could get the Retry-After header as the implementation would be an educated one instead of it being a guessing game. And while my use case is probably very unique, I reckon that addressing it in librespot would benefit its other users too as this rate limit is sometimes surprisingly easy to hit, especially when the application gives the control of playback to many users.
Author
Owner

@kingosticks commented on GitHub (Sep 9, 2024):

a better fix if we could get the Retry-After header

What header? From where? There are no headers for Mercury requests like this.

I don't recall any rate limited playback issues here but perhaps these are downstream problems from projects doing weird things that we don't hear about.

<!-- gh-comment-id:2337529608 --> @kingosticks commented on GitHub (Sep 9, 2024): > a better fix if we could get the Retry-After header What header? From where? There are no headers for Mercury requests like this. I don't recall any rate limited playback issues here but perhaps these are downstream problems from projects doing weird things that we don't hear about.
Author
Owner

@x528491x commented on GitHub (Sep 9, 2024):

a better fix if we could get the Retry-After header

What header? From where? There are no headers for Mercury requests like this.

I don't recall any rate limited playback issues here but perhaps these are downstream problems from projects doing weird things that we don't hear about.

Apologies. I missed roderick's earlier message stating that there are no Retry-After headers.

In that case, I guess I'll just have to modify my code to fetch the preview files and fall back to the actual track if the preview file is not found.

Feel free to close this issue if any retry implementation is not planned for such edge cases.

<!-- gh-comment-id:2337555508 --> @x528491x commented on GitHub (Sep 9, 2024): > > a better fix if we could get the Retry-After header > > What header? From where? There are no headers for Mercury requests like this. > > I don't recall any rate limited playback issues here but perhaps these are downstream problems from projects doing weird things that we don't hear about. Apologies. I missed roderick's earlier message stating that there are no Retry-After headers. In that case, I guess I'll just have to modify my code to fetch the preview files and fall back to the actual track if the preview file is not found. Feel free to close this issue if any retry implementation is not planned for such edge cases.
Author
Owner

@roderickvd commented on GitHub (Sep 16, 2024):

I do think you should handle it on the caller side.

<!-- gh-comment-id:2353541389 --> @roderickvd commented on GitHub (Sep 16, 2024): I do think you should handle it on the caller side.
Author
Owner

@michaelherger commented on GitHub (Sep 25, 2024):

FWIW: I'm seeing this here, as I'm trying to skip through a playlist. I could actually imagine this would happen to "real users", too: play playlist; "don't like that track - next"; repeat previous step a few times.

<!-- gh-comment-id:2375330768 --> @michaelherger commented on GitHub (Sep 25, 2024): FWIW: I'm seeing this here, as I'm trying to skip through a playlist. I could actually imagine this would happen to "real users", too: play playlist; "don't like that track - next"; repeat previous step a few times.
Author
Owner

@kingosticks commented on GitHub (Sep 25, 2024):

Can someone give me an example of how much skipping needs to be done? I was trying to reproduce this earlier by manually skipping and I failed.

<!-- gh-comment-id:2375366421 --> @kingosticks commented on GitHub (Sep 25, 2024): Can someone give me an example of how much skipping needs to be done? I was trying to reproduce this earlier by manually skipping and I failed.
Author
Owner

@michaelherger commented on GitHub (Sep 26, 2024):

Took me 32 skips. Thought it was fewer yesterday. All I tried was to jump to the end of the list, and clicking the last item in the queue wouldn't do the trick for whatever reason.

Now I'm wondering how long it'll take before I can skip again.

<!-- gh-comment-id:2375989066 --> @michaelherger commented on GitHub (Sep 26, 2024): Took me 32 skips. Thought it was fewer yesterday. All I tried was to jump to the end of the list, and clicking the last item in the queue wouldn't do the trick for whatever reason. Now I'm wondering how long it'll take before I can skip again.
Author
Owner

@x528491x commented on GitHub (Sep 26, 2024):

FWIW: I'm seeing this here, as I'm trying to skip through a playlist. I could actually imagine this would happen to "real users", too: play playlist; "don't like that track - next"; repeat previous step a few times.

As I had mentioned in my earlier messages, "real users" are also likely to hit this issue, and everyone would benefit some kind of workaround for this.

However, it has been impossible to decipher the ratelimit. Although, I too hit my errors somewhere around the 32 track mark.

<!-- gh-comment-id:2376021237 --> @x528491x commented on GitHub (Sep 26, 2024): > FWIW: I'm seeing this here, as I'm trying to skip through a playlist. I could actually imagine this would happen to "real users", too: play playlist; "don't like that track - next"; repeat previous step a few times. As I had mentioned in my earlier messages, "real users" are also likely to hit this issue, and everyone would benefit some kind of workaround for this. However, it has been impossible to decipher the ratelimit. Although, I too hit my errors somewhere around the 32 track mark.
Author
Owner

@PandaWill commented on GitHub (Nov 12, 2024):

FWIW: real user here, just hit this issue while manually skipping around tracks with ncspot

<!-- gh-comment-id:2470766900 --> @PandaWill commented on GitHub (Nov 12, 2024): FWIW: real user here, just hit this issue while manually skipping around tracks with ncspot
Sign in to join this conversation.
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/librespot#599
No description provided.