Goodreads Developers discussion

Python example of how to use OAuth

Comments Showing 1-32 of 32 (32 new)    post a comment »
dateDown arrow    newest »

message 1: by [deleted user] (new)

I have noticed that the API documentation features just a single OAuth example, written in Ruby, with more languages (Python, PHP) promised "soon". Lately I had the chance to program against the GoodReads API in Python, so I thought it wise to share a small script showing the basic OAuth workflow and how to access one of the API methods to modify one of the user's shelf.

The code lives at:

The OAuth library used is:

Please note that it is a complete script, not a step-by-step guide like the Ruby example provided in the API documentation. Nonetheless, I think it could be a nice starting point to write a Python example good enough to be included in the official documentation, and I hope it will be useful to some of you.

message 2: by Michael (new)

Michael Economy (michaeleconomy) Awesome!!! Thanks Giulio!

message 3: by Abdallah (new)

Abdallah | 1 comments Thanks .

message 4: by Ettore (new)

Ettore Pasquini Grazie Giulio!

message 5: by Sandro (last edited Jun 19, 2012 10:49AM) (new)

Sandro (sbadame) | 2 comments Thank you for the example! The example is working perfectly for me.

I'm trying to expand on this snippet but I'm having trouble getting the user's id. I changed the method from POST to GET and send an empty dictionary '{}' as the parameter, but I keep on getting a 401 when I try to do ANYTHING except adding a book to shelf. Any ideas why? (api/auth_user and updates/friends.xml for example, both respond with 401)
Thank you!

message 6: by Michael (new)

Michael Economy (michaeleconomy) Sounds like an oath issue, if you want to paste your code here we might be able to help.

message 7: by Sandro (new)

Sandro (sbadame) | 2 comments I figured it out. I was using the request token instead of the access token to make the API calls. *doh*. All seems to be working now. Thank you!

message 8: by Michael (new)

Michael Economy (michaeleconomy) Glad we could help. :)

message 9: by Oleg (new)

Oleg Linkin (maledictus) | 60 comments This example doesn't work for me. It returns exception with 401 status code. Why and how to fix it?

message 10: by William (new)

William Bert (sandinmyjoints) | 2 comments I'm getting the same error. Has anything changed recently?

message 11: by Oleg (new)

Oleg Linkin (maledictus) | 60 comments it's good news for me. It means that the problem on the server's side. Where is somebody from goodreads developer team?

message 12: by Cliff (new)

Cliff Dyer (jcdyer) | 5 comments I'm not from the developer team, but I'm trying to get this working, and I was getting a 401 trying get a request token until I changed the URL from to without the closing /. If I encounter any other problems, I'll try to update here.

message 13: by Cliff (new)

Cliff Dyer (jcdyer) | 5 comments Everything else worked like a charm. Just take the closing / off each URL.

message 14: by William (new)

William Bert (sandinmyjoints) | 2 comments Works for me! Thanks for posting that solution. What made you think to try that?

message 15: by Cliff (new)

Cliff Dyer (jcdyer) | 5 comments I had a python script working based on the ruby example, but that didn't get me far enough to authenticate a user with Oauth. Steps that were already working broke when I tried to use the code shown above, so I started looking for differences.

message 16: by Kyle (new)

Kyle | 6 comments I'm trying to develop a little app that will update my goodreads reading status based on another script that collects my kindle reading percentage.

I'm trying to use this script by making a small modification but I can't get it to work. I get the following error.

Have you authorized me? (y/n) y
Traceback (most recent call last):
File "", line 35, in
raise Exception('Invalid response: %s' % response['status'])
Exception: Invalid response: 401

Can anyone assist me?

message 17: by Cliff (new)

Cliff Dyer (jcdyer) | 5 comments help us help you. Have you tried the suggestions above? did you get a different error message with and without those fixes?

message 18: by Cliff (new)

Cliff Dyer (jcdyer) | 5 comments Can you share a simple but complete code example that exhibits your problem?

message 19: by Kyle (new)

Kyle | 6 comments Thanks for the response. I did try the suggestions above. I've gone through several versions of code trying to fix it. I also tried using Ruby and get the same 401 error. Here is the current complete python code.


import oauth2 as oauth
import urlparse

request_token_url = ''
authorize_url = ''
access_token_url = ''

consumer_key = "jXXXXXXXXXXXXXXXXXg"
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)

resp, content = client.request(request_token_url, "POST")
if resp['status'] != '200':
raise Exception("Invalid response %s." % resp['status'])

request_token = dict(urlparse.parse_qsl(content))

authorize_link = '%s?oauth_token=%s' % (authorize_url,
print "Request Token:"
print " - oauth_token = %s" % request_token['oauth_token']
print " - oauth_token_secret = %s" % request_token['oauth_token_secret']

print authorize_link
accepted = 'n'
while accepted.lower() == 'n':
# you need to access the authorize_link via a browser,
# and proceed to manually authorize the consumer
accepted = raw_input('Have you authorized me? (y/n) ')

token = oauth.Token(request_token['oauth_token'],

client = oauth.Client(consumer, token)
response, content = client.request(access_token_url, 'POST')
if response['status'] != '200':
raise Exception('Invalid response: %s' % response['status'])

access_token = dict(urlparse.parse_qsl(content))

print "Access Token:"
print " - oauth_token = %s" % access_token['oauth_token']
print " - oauth_token_secret = %s" % access_token['oauth_token_secret']
print "You may now access protected resources using the access tokens above."

# this is the token you should save for future uses
token = oauth.Token(access_token['oauth_token'],

# let's add a status

import urllib

client = oauth.Client(consumer, token)
# the book is: 1776
body = urllib.urlencode({'user_status[book_id]':1067, 'user_status[percent]':38})
headers = {'content-type': 'application/x-www-form-urlencoded'}
response, content = client.request('',
'POST', body, headers)
# check that the new resource has been created


I am getting oath_token and oauth_token_secret returned. The 401 error is coming right after answering Y to have you authorized me.

Thanks for the help!

message 20: by Xuefan (new)

Xuefan Zhang | 5 comments ^ I am experiencing the same problem.

message 21: by Kyle (new)

Kyle | 6 comments bump? I assume there is nothing wrong with the API itself and that the above referenced code is the issue, can someone confirm this?

message 22: by Jon (new)

Jon (divisionbyzero) | 21 comments we are working on this issue right now, i'll update the thread when we've resolved it.

message 23: by Jon (last edited Aug 02, 2013 11:30AM) (new)

Jon (divisionbyzero) | 21 comments here's a working python example. the oauth2 library doesn't seem to handle the oauth 1.0a protocol in the way we'd expect so i used rauth instead:

let me know if you still have issues.

message 24: by Slim (new)

Slim cui | 1 comments I am having the same problem right now, and I found this post from 2013, Is there anyone still having the same trouble.

message 25: by Jeffrey (new)

Jeffrey (jeffwong) | 74 comments Mod

Sorry for the late reply, but were you able to get the Jon's python Oauth example working? Were you able to get the session or did it fail on the add to shelf call?

I just tried it and it appears to be up-to-date. I was able to add two books to my to-read shelf for my test user.

message 26: by Siddharth (new)

Siddharth Joshi (sidsavenue) | 2 comments I use the code provided by Kyle but keep getting this error. authorize_link = '%s?oauth_token=%s' % (authorize_url,request_token['oauth_token'])
KeyError: 'oauth_token' . I am new to Python and Oauth, so pardon my ignorance. Please help. thanks

message 27: by Ash (new)

Ash | 1 comments Siddharth wrote: "I use the code provided by Kyle but keep getting this error. authorize_link = '%s?oauth_token=%s' % (authorize_url,request_token['oauth_token'])
KeyError: 'oauth_token' . I am new to Python and Oau..."

Consider using this example from official documentation. I can confirm it's working.

message 28: by Siddharth (new)

Siddharth Joshi (sidsavenue) | 2 comments thanks for the quick reply Ash. I did try that and it works. thanks a ton.

message 29: by Systems (last edited Apr 05, 2016 01:58PM) (new)

Systems Lib | 6 comments I am working with the API in Python, and have run into an odd error:

Following the directions above using rauth, I have created a session with a stored access_token and access_token_secret. When I run a session.get for any OAuth materials (like owned books, which is my primary use for the API), I get a 401 error.

Here's the code I'm using:

from rauth.service import OAuth1Service, OAuth1Session
import xmltodict


gr_service = OAuth1Service(
consumer_key = XXXXXXXXXXXXXX,
name = 'goodreads',
request_token_url = BASE_URL + 'oauth/request_token',
authorize_url = BASE_URL + 'oauth/authorize',
access_token_url = BASE_URL + 'oauth/access_token',
base_url = BASE_URL,

request_token, request_token_secret = gr_service.get_request_token(header_auth=True)

<-- snip the "yes to confirm" bit -->

temp_session = gr_service.get_auth_session(request_token, request_token_secret)

ACCESS_TOKEN = temp_session.access_token
ACCESS_TOKEN_SECRET = temp_session.access_token_secret

session = OAuth1Session(
consumer_key = XXXXXXXXXXXXXX,
consumer_secret = xxxxxxxxxxxxxxxxxxxxxxx,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_TOKEN_SECRET,

get_user_url = ""

resp = session.get(get_user_url)
user_dict = xmltodict(resp.content)['GoodreadsResponse']

get_owned_books_url = "" % user_dict['user']['@id']

book_resp = session.get(get_owned_books_url) that point, if I check the book_resp, it's a 401 and throws an error if run through xmltodict.

message 30: by Feiyu (new)

Feiyu Yin (sanchishui) | 11 comments Hi,

Do you mean that when trying this API you get a 401 error?

Is this the only endpoint that has this problem? Are you able to get this example working ?


message 31: by Systems (new)

Systems Lib | 6 comments Yes, when attempting that API, I get an error. I get the error on most endpoints, and I'm using that example as a starting point.

message 32: by Aulon (new)

Aulon | 1 comments Hi Feiyu,
I managed to get access to my profile and I can add a book to my list. However, I am having issues pulling the entire list of my bookshelf (which is the reason I wanted the API in the first place).

The session code works
session = goodreads.get_auth_session(request_token, request_token_secret)

I tried accessing the list of my books

The only output I get is:

How do I get the actual data?

Thank you!

back to top