Instantly share code, notes, and snippets.

What would you like to do?
Working version of Photo Uploads for Tumblr API V2
import urllib,hmac,time,hashlib,base64,httplib,sys,json,urlparse
## This is just a simple example that is self contained.
## You will need to modified it to make it work
## creds - need to be filled out
## blognmae - needs to be defined
## reads in image files from the command line and posts to your blog
## Your example body will contain something like:
## data%5B0%5D= => data[0]=<start binary>
## So from here you can gather that it's data[0], data[1] and so on.
## This code works for single photos as well as multiple photos
## Code was tested usings Python 2.7, no guarantees it'll work for others.
## Usage:
## python example1.jpg [example2.jpg example3.jpg ...]
class TumblrAPI:
def __init__(self, cred):
self.consumer_key = cred['consumer_key']
self.secret_key = cred['secret_key']
self.oauth_token_secret= cred['oauth_token_secret']
self.oauth_token = cred['oauth_token']
def parse(self,url):
p = urlparse.urlparse(url)
return (p.netloc,p.netloc,p.path)
def oauth_sig(self,method,uri,params):
Creates the valid OAuth signature.
#eg: POST&
s = method + '&'+ urllib.quote(uri).replace('/','%2F')+ '&' + '%26'.join(
#escapes all the key parameters, we then strip and url encode these guys
[urllib.quote(k) +'%3D'+ urllib.quote(params[k]).replace('/','%2F') for k in sorted(params.keys())]
s = s.replace('%257E','~')
return urllib.quote(base64.encodestring( + "&"+self.oauth_token_secret,s,hashlib.sha1).digest()).strip())
def oauth_gen(self,method,url,iparams,headers):
Creates the oauth parameters we're going to need to sign the body
params = dict([(x[0], urllib.quote(str(x[1])).replace('/','%2F')) for x in iparams.iteritems()])
params['oauth_consumer_key'] = self.consumer_key
params['oauth_nonce'] = str(time.time())[::-1]
params['oauth_signature_method'] = 'HMAC-SHA1'
params['oauth_timestamp'] = str(int(time.time()))
params['oauth_version'] = '1.0'
params['oauth_token']= self.oauth_token
params['oauth_signature'] = self.oauth_sig(method,'http://'+headers['Host'] + url, params)
headers['Authorization' ] = 'OAuth ' + ', '.join(['%s="%s"' %(k,v) for k,v in params.iteritems() if 'oauth' in k ])
def _postOAuth(self,url,params={}):
Does the actual posting. Content-type is set as x-www-form-urlencoded
Everything url-encoded and data is sent through the body of the request.
(machine,host,uri) = self.parse(url)
headers= {'Host': host,"Content-type": 'application/x-www-form-urlencoded'}
conn = httplib.HTTPConnection(machine)
#URL Encode the paramers and make sure and kill any trailing slashes.
return conn.getresponse()
def createPost(self,id,params={}):
url = '' %id
return self._resp(self._postOAuth(url,params),201);
def _resp(self,resp,code=200):
if resp.status != code:
raise Exception('response code is %d - %s' % (resp.status,;
return json.loads(['response']
cred = { "consumer_key" : 'your-consumer-key',
'secret_key' : 'your-consumer-secret',
'oauth_token' : 'access-token',
'oauth_token_secret' : 'access-token-secret'}
blogname = ''
api = TumblrAPI(cred)
params = {}
params['type'] = 'photo'
for x in range(1,len(sys.argv)):
params['data[%d]' % (x-1) ] = file(sys.argv[x]).read()
print api.createPost(blogname,params);

This comment has been minimized.

boppyer commented Jan 24, 2013

I get this error popping up, any ideas what I might be doing wrong?

Traceback (most recent call last): File "", line 97, in print api.createPost(blogname,params); File "", line 77, in createPost return self._resp(self._postOAuth(url,params),201); File "", line 81, in _resp raise Exception('response code is %d - %s' % (resp.status,; Exception: response code is 401 - {"meta":{"status":401,"msg":"Not Authorized"},"response":[]}

This comment has been minimized.

theasta commented Jan 25, 2013

I got exactly the same error as @boppyer


This comment has been minimized.

VilemKurz commented Feb 26, 2013

exactly the same error here too :( python 2.7.2


This comment has been minimized.


codingjester commented Feb 27, 2013

I removed an extraneous & which was leftover by accident. Sorry. Gist works now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment