This first part will focus on creating an anonymous API (Application programming interface) key for your script to use, and then upload an image to imgur.com. For this, and any following tutorals, I will be using Windows 7×64, with Python 2.7.
You will need to create an anonymous API key at http://imgur.com/register/api_anon, it’s really simple, just feed the name of your app, so the dudes over at Imgur know what your intentions are, and then your personal information and finally, the reCaptcha, to verify you’re not a bot. The next page will give you your developer API key; this is important. This key will allow you to interact with the Imgur API. Since this is the limited Anonymous API, you will only have access to basic functions, like uploading images from your computer or from another website, and getting gallery and image information. Luckily, that’s enough for our purposes here.
The documentation for the Imgur API that we’ll be using today is found here: http://api.imgur.com/resources_anon, and the requests module documentation is here http://docs.python-requests.org/en/latest/user/quickstart/. Now, open up your favourite text editor for coding. I like using Wing IDE Professional, but I’m sure notepad++ and any other one would work just as nicely. This is where the fun part begins. Hit the jump!
Tinypaste of the full working code!
I’m going to go through the code line by line, and then post it all together at the end, so you can try it out yourself after. Don’t forget to drop me a line on reddit, on twitter (@tankorsmash) or in the comments if you run into any trouble
First, here’s the imports that I like to have for basic networking code. I’ve written about them more in detail here, but basically pprint (pretty print) prints out a nicely formatted string when told to print a long dict or list.
Requests is the module that will be doing most of the heavy lifting. It’ll be the one we’re going to use to interact with the APIs here.
json is the module that takes JSON objects (javascript objects) and turns them into `dicts` and vice versa
#basic networking imports from pprint import pprint import requests import json
Base64 is a module for encoding and decoding data into a universal base64 format. Unfortunately, I don’t know much more than that. The docs are here though, if you care to learn more yourself.
#image handling imports import base64
The next line is simply the string representation of the anonymous api key we received from the imgur.com address earlier in the post.
#your api key will go here, as a string, don't steal mine! api_key = '5abcdefghi123456123111'
Same with the url, it’s the url we found in the documentation here. Don’t forget to append ‘,json’ at the end of it like I have, otherwise you’ll get an xml formatted response, and that isn’t what we want to deal with, for this tutorial at least.
#here's the API url that you'll need to POST to url = r'http://api.imgur.com/2/upload.json'
This is the location of the image on your hard drive. It can be the exact location as well: ‘C:/documents/1.jpg’, but so long as it’s in the same folder as the .py file you’re running you should be okay. To find the folder you’re currently running in, try using the `os` module’s function getcwd: `os.getcwd()`.
#full image path image_path = '1.jpg'
Simply open the file object at the path we just referenced above, and open it as ‘rb’ which is read-only in binary mode, over text mode.
#open binary data, instead of regular read f = open(image_path, 'rb')
This next part looks a bit complicated because of the base64 module, but just think of it as the base64 encoding turns the data into a format that the computer can read, whether or not it has python in it or not. Imgur expects data, rather than a python object, after all.
What happens if that we read the binary data from `f` and save it to the variable `binary_data` and then encode that data into base64 and save that to another variable called `b64image` which we’ll send to imgur, through the API
#encode image file for transfer binary_data = f.read() #again, not string data, but binary data b64image = base64.b64encode(binary_data)
We need to create a dictionary full of data that we can send to imgur, in order for it to be seen by anyone on the internet. It takes a minimum of two argument: ‘key’ and ‘image’, where key is the api key from earlier, and the image data, which we have already in b64 format.
You can also set the title, caption , and type of the image (url or base64) using keys with the same name, as strings, don’t forget.
#data to send with the POST request payload = {'key' : api_key, 'image': b64image, 'title': 'apitest',} #title of image as seen on imgur.com
Now, time to interact with the API. We do that by sending HTTP requests. There are several format, but the most common that we’ll use are GET, and POST. In order to properly POST an image to the API, we’ll need that payload with the image and api_key so the site knows what we’re trying to upload.
We’ll also receive some information from the API as a JSON, which we’ll save to the ‘r’ variable. The most human readable way to check that response is its text attribute, but there’s also its ‘content’ and ‘raw’ attributes. You can also see the type of status code you get from the API by checking its ‘status_code’ whether it’s a 404 error or note. You can see all its variations here. requests also has a function that will raise an error, should the status not be 200, but you can read on that later here
So, back on topic: send the POST requests with the data in the payload dict, and save the HTTP response as ‘r’.
#make the POST request, with the attached data of payload r = requests.post(url, data=payload)
Here we use the `json` module to transform the JSON string we got in the response from the API into a python Dict we can play around with. Fairly simple stuff.
If you get an error that there isn’t a JSON object to decode, try printing out the r.text to make sure you’re not running into any errors in the POST. That’s been the main way that the next line has been failing for me. It’s also possible that you forgot to set the url to a ‘.json’ ending, meaning you’re getting an xml object.
#turn the returned json into a python dict j = json.loads(r.text)
Once you’ve properly decoded the JSON as the dict `j`, there’s only one thing left to do: print it out. You could simply `print j`, but that’s ugly. Use the following to try to print it out in such a way that you can all the key:value pairs.
#print it out cleanly pprint(j) #The image url, assuming everything went according to plan, is at pprint(j['upload']['links']['imgur_page']
And there you have it! You’ve successfully uploaded an image to imgur without using your browser, beyond the first key retrieval. Congratulations!
Here’s the full code:
#basic networking imports from pprint import pprint import requests import json #image handling imports import base64 #your api key will go here, as a string api_key = '5a5141ca9354bf7929b98a1d7a4c26ae' #here's the API url that you'll need to POST to url = r'http://api.imgur.com/2/upload.json' #full image path image_path = '1.jpg' #open binary data, instead of regular read f = open(image_path, 'rb') #encode image file for transfer binary_data = f.read() #again, not string data, but binary data b64image = base64.b64encode(binary_data) #data to send with the POST request payload = {'key' : api_key, 'image': b64image, 'title': 'apitest',} #title of image as seen on imgur.com #make the POST request, with the attached data of payload r = requests.post(url, data=payload) #turn the returned json into a python dict j = json.loads(r.text) #print it out cleanly pprint(j)
You have provided an awesome resource.
edited by Tankor Smash
Unfortunately the guys at imgur have completely changed their API so none of this woks.
They’ve, for whatever reason, also removed all example code they once had making this an impossible challenge.
Currently looking into other services.
Man, that sucks. A quick look makes it seem like the changes’d be mostly changing the URL and login. I guess it needs to happen at some point or another.
Here’s how it works with imgur’s API version 3:
import json, requests
r = requests.post(
‘https://api.imgur.com/3/image’,
data = { ‘image’: open(‘test.jpg’, ‘rb’).read(), ‘type’: ‘file’ },
headers = {‘Authorization’: ‘Client-ID YOUR_CLIENT_ID’}
)
print r.json()
You have to register an app on imgur, but it’s free and doesn’t require OAuth2 for uploading anonymously.
That seems a lot easier!
I think you should do a post what a callback URL is. Imgur has an option that doesn’t require a callback URL, but I’m here trying to learn API stuff in general so I’ve been scouring google for an explanation.
The top five results on Google are for a stackexchange question and four specific service implementations that require a callback URL. The stackexchange question gives only this as a definition for a callback URL:
“A callback URL will be invoked by the API method you’re calling after it’s done. So if you call
POST /api.example.com/foo?callbackURL=http://my.server.com/bar
Then when /foo is finished, it sends a request to http://my.server.com/bar. The contents and method of that request are going to vary – check the documentation for the API you’re accessing.”
That’s not much. I’ve gone through Mozilla’s (robust) explanation of HTTP, so I understand requests, but even this stackexchange question takes for granted that I know why I would want to POST anything after the initial request. The other four search results are service specific to that particular API and also take an understanding of APIs for granted.
Yeah so I’m going to just leave my feedback as I go through these tutorials. Might be spammy, as I’m intending it as a stream of whatever I’m getting hung up on as I parse through. May be helpful to know the thought process of a newb.
– I didn’t know what the ‘r’ did before the string in ” url = r’http://api.imgur.com/2/upload.json’ “. Quick googling solved it, and it’s actually quite clever. I feel like I’m going to use that everywhere since APIs involved deeply involve URIs.
– As another commenter mentioned, imgur seems to want https now instead of http (sortof? confused on when they say otherwise; HTTPS seems to be concerned with OAUTH). Not familiar with HTTPS yet. Let’s see how it goes. Need to switch to api.imgur.com/3/ instead of api.imgur.com/2/ also (I think).
Thanks a lot man 🙂