I am sure that all 11 views of this question (at the time of writing) is me, but I am going to post an answer just in case this helps someone in the future:
There were no problems with my code. The problem was that I initially created my refresh_token for this account.
For the uninitiated data APIs, YouTube (v3) does not support the “service accounts” that elsewhere in the Google API ecosystem are the way you usually configure the OAuth2 auth'd client when you are the only client. A workaround is what you need to do manually. Follow these steps:
First go to the Google API Console. There, in the "Access to API" section, you need to "Create a client identifier" for the "installed application". This will give you a Client ID , a Client secret and a Redirect URI (you will need a non- localhost ). Write them down.
Then you need to manually obtain the authorization code by visiting the URL similar to the following in your favorite web browser: when you log in to the same account, you just created the client ID:
https://accounts.google.com/o/oauth2/auth ?client_id={client_id} &redirect_uri={redirect_uri} &scope={space separated scopes} &response_type=code &access_type=offline
Of course, you need to enter the client_id , redirect_uri and scope request parameters. In my case, I was mistaken . When I did this manual step, I had to set the scope parameter as:
https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.upload
But instead, I just did https://www.googleapis.com/auth/youtube.upload , which is not enough to update / delete a video!
Finally, you need to get refresh_token by selecting this URL:
https://accounts.google.com/o/oauth2/token ?code={authorization_code} &client_id={client_id} &client_secret={client_secret} &redirect_uri={redirect_uri} &grant_type=authorization_code
And twisting with a command like:
$ curl https://accounts.google.com/o/oauth2/token -d "code=..."
This will return a JSON response containing your refresh_token , which is then used to authorize your request programmatically through the Google API.