oauth authentication: Request contains an invalid argument
Recommended workaround: use browser based auth instead of oauth.
Discussed in https://github.com/sigma67/ytmusicapi/discussions/812
Originally posted by rawinkler September 1, 2025
- [x] I confirm that I have read the FAQ
Bug description
Since last Friday, August 29th 2025, suddenly my code stopped working. I use ytmusicapi version 1.11.0. I get the error:
File "/usr/local/lib/python3.13/site-packages/ytmusicapi/mixins/search.py", line 182, in search response = self._send_request(endpoint, body) File "/usr/local/lib/python3.13/site-packages/ytmusicapi/ytmusic.py", line 241, in _send_request raise YTMusicServerError(message + error) ytmusicapi.exceptions.YTMusicServerError: Server returned HTTP 400: Bad Request. Request contains an invalid argument.
To Reproduce
Minimal example:
YT_CLIENT = \
YTMusic("/usr/src/app/oauth.json",
oauth_credentials=OAuthCredentials(
client_id=environ["GOOGLE_YOUTUBE_API_CLIENT_ID"],
client_secret=environ["GOOGLE_YOUTUBE_API_CLIENT_SECRET"]
)
)
search_results = YT_CLIENT.search('Oasis Wonderwall')
Is it possible that YouTube changed something that causes ytmusicapi to break?
Thanks for any help!
PR welcome
PR welcome
Hey @sigma67 , Ok so im not entirely sure if this pr will help but I've forked your project and looked into it and i was able to test everything there is.
If you want, please feel free to take a look at this pr, Hopefully this helps but if not, please feel free to delete it. Thanks
https://github.com/sigma67/ytmusicapi/pull/815
Thanks for trying @Goldenfreddy0703 The issue is certainly affecting lots of users. It also breaks the entire setup for me in Germany.
Oh hey anytime @KoljaWindeler , hey so I actually got some good news, today i decided to try again and i was actually doing some coding for a whole day and what I've been doing is I've been working on the ytmusic api and getting everything to work using the ios client. I have about a third of it completed but I need to get Playlist, podcast, and uploads done and then I probably need to test every function. So far, all the other stuff is working and getting the correct params and thanks to this documentation, it's been a huge help.
https://ytmusicapi.readthedocs.io/en/stable/index.html
Hopefully by tomorrow or the next few days, I can make a pr that will pass almost every test there is.
FWIW I just used Goldenfreddy's branch to replicate some playlists on YTM and it's working nicely. Thanks!
Not sure if related, but the Authorization header has changed as well apparently: https://github.com/sigma67/ytmusicapi/issues/813
FWIW I just used Goldenfreddy's branch to replicate some playlists on YTM and it's working nicely. Thanks!
Any update on this? I use the API to extract my playlist metadata... Curious if it would be duplicated effort for me to try to learn the API and find a solution... A bit overwhelmed I'm a C# .net front end developer with data model and data manipulation experience.
I am also facing the same issue.
ytmusicapi.exceptions.YTMusicServerError: Server returned HTTP 400: Bad Request.
I am trying to fetch the user library
Hi all, the current solution attempt by Goldenfreddy does not work reproducibly for my account.
I still get the same error noted in the issue title. Therefore we cannot merge this right now.
Other attempts at solving this problem are in no way duplicate effort as I do not consider this problem solved right now.
Furthermore we do not know if there is currently a region-specific rollout or if the problem is the same for all regions.
I am also experiencing this issue.
Im UK based and have the same issue unauthenticated works:
ytmusic = YTMusic()
search_results = ytmusic.search('Oasis Wonderwall')
Fails:
ytmusic = YTMusic('oauth.json', oauth_credentials=OAuthCredentials(client_id=client_id, client_secret=client_secret))
search_results = ytmusic.search('Oasis Wonderwall')
ytmusicapi.exceptions.YTMusicServerError: Server returned HTTP 400: Bad Request.
Request contains an invalid argument.
Same problem here in The Netherlands. What is the best workaround?
There is no solution unfortunately: https://github.com/sigma67/ytmusicapi/pull/817#issuecomment-3358576031
You must use the browser based auth variant.
You can try sending a note to Google support that they should provide some way for us to use their oauth API with YouTube Music.
But so far they've only made life harder for us over the past months, so I'm not even hoping for anything at this point.
One solution I'm thinking of doing for my app is having the user connecting to a local HTTP server via localhost and having an oath of some kind to generate the cookie and all so maybe that's something I can work on tomorrow.
EDIT: Apparently, its not that simple thanks to browser security and google. ughhhhhhh.....
I have tested it a bit and noticed following - if I change the clientName/version to
YT_CLIENT.context["context"]["client"]["clientName"] = "TVHTML5"
YT_CLIENT.context["context"]["client"]["clientVersion"] = "6.20240925.00.00"
the oauth login works and we get a result, but its always the same and has no search results:
{
"responseContext": {
"serviceTrackingParams": [
{
"service": "GFEEDBACK",
"params": [
{
"key": "has_unlimited_entitlement",
"value": "True"
},
{
"key": "has_premium_lite_entitlement",
"value": "False"
},
{
"key": "logged_in",
"value": "1"
}
]
}
],
"maxAgeSeconds": 7200
},
"estimatedResults": "4826902",
"trackingParams": "CAAQxxxxIU0="
}
Changing it to
YT_CLIENT.context["context"]["client"]["clientName"] = "TVHTML5"
YT_CLIENT.context["context"]["client"]["clientVersion"] = "7.20240925.00.00"
also works and even returns results, but in a different format than the current lib is expecting:
search_results = YT_CLIENT.search('Oasis Wonderwall')
returns an empty array, but the underlying response from youtube has results:
grep -A5 -i oasis response.txt
"simpleText": "Oasis - Wonderwall (Official Video)"
},
"lines": [
{
"lineRenderer": {
"items": [
--
"simpleText": "Oasis"
}
}
}
]
}
--
"simpleText": "Oasis - Wonderwall (Official Video)"
},
"subtitle": {
"simpleText": "Oasis"
},
"menu": {
"menuRenderer": {
"items": [
{
--
"simpleText": "Oasis"
}
}
}
]
}
--
"simpleText": "Oasis"
...
Im not sure what needs to be adapted to correctly parse this format
@DanielWeigl thanks for checking and confirming that the API is different. It was already noted here by @sgvictorino : https://github.com/sigma67/ytmusicapi/pull/817#issuecomment-3349552129
It seems the API is more limited than what we need so I'm not sure it's worth implementing the parsers for a limited API just for the sake of having oauth at all.
Yeah TVHTML5 is basically the music section from the main youtube so wouldn't be so good. When IOS_MUSIC was working, i was actually working on the parser for it but than Google decided to be dumb so yeah.
I know it doesn't help with this particular API or bug but for me it was really important to get my YouTube music metadata so I can keep track of my songs and my playlists...
If you go to Google takeout you can download your YouTube data and it'll have a list of all your playlists and another file of all your library songs/videos. You can cross reference with the video ID from the two files and parse the content into a clean data structure...
You can't really automate the extraction of Google take out and I wouldn't recommend abusing it because if they remove album or artist info it would suck major... But it helps in a pinch.
Hi! I'm having the same problem when requesting my YouTube Music account history:
ytmusic = YTMusic(auth='oauth.json', oauth_credentials=OAuthCredentials(client_id=os.getenv("YTMUSIC_CLIENT_ID"), client_secret=os.getenv("YTMUSIC_CLIENT_SECRET"))) history = ytmusic.get_history()
The log output: ytmusic-dev | [2025-10-09 14:39:16,572] (INFO): Script start ytmusic-dev | [2025-10-09 14:39:17,112] (ERROR): Error getting song from YouTube Music: Server returned HTTP 400: Bad Request. ytmusic-dev | Request contains an invalid argument. ytmusic-dev | [2025-10-09 14:39:17,112] (INFO): No song data to send. ytmusic-dev | [2025-10-09 14:39:17,112] (INFO): Script end
After I would spend a few hours trying to fix this I came up with reading this Github issue. What would you say would be the alternative to try to workaround it? Or it does not have to do with the auth flow you use but rather the API being changed on Youtube's side?
I am having the same problem, I cannot use my app to generate playlists through the ytmusicapi anymore. I'm getting: "Failed to create playlist: Server returned HTTP 400: Bad Request. Request contains an invalid argument."
My app was working fine until recently, and the authentication itself succeeds - but playlist creation fails.
Hi @thosch6 , have you tried the workaround to use browser-based authentication? It should work just fine.
On the oauth front we are unfortunately blocked due to YouTube Music server not accepting our requests.
The workaround to use browser-based authentication does not work either. I tried this approach twice, the first time with the AI help of the new Gemini 3.0, a few days later with Claude Sonnet 4.5. Even though authentication poses no problem, generating playlists fails always. "Failed to create playlist: Server returned HTTP 400: Bad Request. Request contains an invalid argument."
That is unlikely, you might be using the wrong file by accident. That error message only occurs when using oauth (or your browser credentials are corrupt).
Did you follow the browser instructions in the documentation? Could you elaborate?
So I tried the 3rd time creating a playlist with the latest YouTubeMusicApi version 1.11.3. It fails with error 401. Here's the detailed report assembled with Anthropic Opus 4.5:
Report: Playlist Creation Failing with Browser Authentication
Summary
Playlist creation via ytmusicapi with browser authentication returns HTTP 401 (Unauthorized), even though search and other read operations work fine with the same credentials.
Environment
- macOS 15.5
- Python 3.14
- ytmusicapi (latest version 1.11.3)
- Chrome 143
Observed Behavior
Operation | Result -- | -- Search (songs, albums) | ✅ Works Browse library | ✅ Works Create playlist | ❌ 401 Unauthorized Add items to playlist | ❌ 401 Unauthorized
Root Cause Analysis
After extensive debugging, I found that direct HTTP requests to YouTube Music's API work perfectly when using headers copied directly from Chrome DevTools. This indicates the issue is in how ytmusicapi handles or regenerates the authorization header.
Key Finding 1: Authorization Header Format Changed
YouTube Music now uses a 3-part authorization header with _u suffix:
SAPISIDHASH <timestamp>_<hash>_u SAPISID1PHASH <timestamp>_<hash>_u SAPISID3PHASH <timestamp>_<hash>_uInstead of the previous single-part format:
SAPISIDHASH <timestamp>_<hash>Key Finding 2: Hash Generation Algorithm Differs
I tested the documented hash formula:
hash_input = f"{timestamp} {SAPISID} {origin}"
hash_value = hashlib.sha1(hash_input.encode()).hexdigest()Result: The generated hash does NOT match what Chrome produces.
I tested multiple variations (different separators, different orders, SHA-256, etc.) - none matched Chrome's hash. This suggests YouTube may have changed the hash algorithm or is using additional inputs.
Key Finding 3: Cookies Rotate Frequently
These cookies change with every few requests and must be fresh:
SIDCC__Secure-1PSIDCC__Secure-3PSIDCC__Secure-1PSIDTS__Secure-3PSIDTS
Working Workaround
Direct API calls work when using the exact headers copied from a successful Chrome request (e.g. when creating a playlist manually while logged into the YouTube Music account) :
import requests
headers = {
"authorization": "<copied from Chrome>",
"cookie": "<copied from Chrome>",
"content-type": "application/json",
"origin": "https://music.youtube.com",
"x-goog-authuser": "1",
"x-youtube-client-name": "67",
"x-youtube-client-version": "1.20251203.02.00",
}
response = requests.post(
"https://music.youtube.com/youtubei/v1/playlist/create?prettyPrint=false",
headers=headers,
json={
"context": {
"client": {
"clientName": "WEB_REMIX",
"clientVersion": "1.20251203.02.00",
}
},
"title": "Test Playlist",
"privacyStatus": "PRIVATE"
}
)
# Returns 200 OK with playlistIdSuggested Investigation Areas
- Authorization header generation - The 3-part format with
_usuffix may need to be implemented - Hash algorithm - Chrome may be using a different input format or algorithm than documented
- Cookie handling - Some session cookies may need to be refreshed or passed through differently for write operations
- Endpoint differences - Read endpoints (search, browse) may have different auth requirements than write endpoints (playlist/create, edit_playlist)
How to Reproduce
- Set up browser authentication with
ytmusicapi - Verify search works:
ytmusic.search("test")→ ✅ - Try creating a playlist:
ytmusic.create_playlist("Test")→ ❌ 401