Using our file API


Our API supports lots of common PythonAnywhere operations, like creating and managing consoles, scheduled and always-on tasks, and websites. We recently added support for reading/writing files; this blog post gives a brief overview of how you can use it to do that.

The first step when using the API is to get an API token – this is what you use to authenticate yourself with our servers when using it. To do that, log in to PythonAnywhere, and go to the “Account” page using the link at the top right. Click on the “API token” tab; if you don’t already have a token, it will look like this:

Click the “Create a new API token” button to get your token, and you’ll see this:

That string of letters and numbers (d870f0cac74964b27db563aeda9e418565a0d60d in the screenshot) is an API token, and anyone who has it can access your PythonAnywhere account and do stuff – so keep it secret. If someone does somehow get hold of it, you can revoke it on this page by clicking the red button – that stops it from working in the future, and creates a new one for you to use.

Now let’s use that token to access some files.

Preparing for using the API

On a machine with Python and requests installed, start a Python interpreter and run this:

from pprint import pprint
import requests
from urllib.parse import urljoin

api_token = "YOUR TOKEN HERE"
username = "YOUR USERNAME HERE"
pythonanywhere_host = "www.pythonanywhere.com"

api_base = "https://{pythonanywhere_host}/api/v0/user/{username}/".format(
    pythonanywhere_host=pythonanywhere_host,
    username=username,
)

…replacing "YOUR TOKEN HERE" and "YOUR USERNAME HERE" with the appropriate stuff. If you’re on our EU-based system, you should also replace www.pythonanywhere.com with eu.pythonanywhere.com.

If you’re using Python 2.7, the line

from urllib.parse import urljoin

should be

from urlparse import urljoin

Listing files

Now let’s make a request to see what files you have:

resp = requests.get(
    urljoin(api_base, "files/path/home/{username}/".format(username=username)),
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

You should get a 200 status code:

>>> print(resp.status_code)
200

…and the JSON data in the response should be a reasonably-understandable description of the files and directories in your home directory – note the type field that tells you what is what.

>>> pprint(resp.json())
{u'.bashrc': {u'type': u'file',
              u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/.bashrc'},
 u'.gitconfig': {u'type': u'file',
                 u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/.gitconfig'},
 u'.local': {u'type': u'directory',
             u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/.local'},
 u'.profile': {u'type': u'file',
               u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/.profile'},
 u'.pythonstartup.py': {u'type': u'file',
                        u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/.pythonstartup.py'},
 u'.vimrc': {u'type': u'file',
             u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/.vimrc'},
 u'README.txt': {u'type': u'file',
                 u'url': u'https://www.pythonanywhere.com/api/v0/user/yourusername/files/path/home/yourusername/README.txt'}}

So that shows how to get a directory listing.

Downloading a file

Let’s try downloading a file:

resp = requests.get(
    urljoin(api_base, "files/path/home/{username}/README.txt".format(username=username)),
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

Again, the status code should be 200:

>>> print(resp.status_code)
200

…and the content of the response will be the contents of the file:

>>> print(resp.content)
# vim: set ft=rst:

See https://help.pythonanywhere.com/ (or click the "Help" link at the top
right) for help on how to use PythonAnywhere, including tips on copying and
pasting from consoles, and writing your own web applications.

Uploading a file

Let’s upload a file. We’ll put the text hello, world into a file called foo.txt in your home directory:

resp = requests.post(
    urljoin(api_base, "files/path/home/{username}/foo.txt".format(username=username)),
    files={"content": "hello world"},
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

This time, the status code should be 201 (which means “created”):

>>> print(resp.status_code)
201

Now you can go to the “Files” page on PythonAnywhere, and you’ll see the file; click on the filename to open an editor and see its contents:

The API will also show the new contents if you use it to get the file:

resp = requests.get(
    urljoin(api_base, "files/path/home/{username}/foo.txt".format(username=username)),
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

You’ll get a 200 status code:

>>> print(resp.status_code)
200

And the expected content:

>>> print(resp.content)
hello world

Updating an existing file

You can update your file with new contents:

resp = requests.post(
    urljoin(api_base, "files/path/home/{username}/foo.txt".format(username=username)),
    files={"content": "some new contents"},
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

This time, the status code should be 200 (which means “successfully updated” in this context):

>>> print(resp.status_code)
200

…and if you refresh your editor window, you’ll see the updated contents.

Deleting a file

Finally we can delete the file:

resp = requests.delete(
    urljoin(api_base, "files/path/home/{username}/foo.txt".format(username=username)),
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

And we get a “204” status code, meaning “successfully deleted”:

>>> print(resp.status_code)
204

Refresh the editor page again, and you’ll see a 404 not found error:

And if we try to download it using the API:

resp = requests.get(
    urljoin(api_base, "files/path/home/{username}/foo.txt".format(username=username)),
    headers={"Authorization": "Token {api_token}".format(api_token=api_token)}
)

We get the same status:

>>> print(resp.status_code)
404

Conclusion

That’s been a quick high-level overview of our new file API. Let us know what you think! And if you have any questions, just leave a comment below.

comments powered by Disqus