Accessing the files API using our new CLI tool

The new release of the pythonanywhere helper scripts package introduces new commands covering our files API.


Assuming you’re on the latest system image (and you haven’t changed your default python command to run Python below 3.6) you can use the basic pip install pythonanywhere --user command, which will install our helper scripts for your default Python version. If you installed the package previously, add -U flag to the command.

To use our helper scripts you need to have a valid API token, which you can obtain on your Account page in the API Token tab.

Introducing a new style of invoking commands

Our files API wrappers are the first which come only with the new style command subcommand options and not as standalone scripts to run as were used before (e.g. pa_delete_scheduled_task). All previously released scripts will work for backwards compatibility, but it’s preferable to use the new style ones from now on. The main command to use is, as you may expect, pa.

$ pa --help

  This is a new experimental PythonAnywhere cli client.

  It was build with typer & click under the hood.

  --install-completion [bash|zsh|fish|powershell|pwsh]
                                  Install completion for the specified shell.
  --show-completion [bash|zsh|fish|powershell|pwsh]
                                  Show completion for the specified shell, to
                                  copy it or customize the installation.

  --help                          Show this message and exit.

  django    Makes Django Girls tutorial projects deployment easy
  path      Perform some operations on files
  schedule  Manage scheduled tasks
  webapp    Everything for web apps

To get an overview of the subcommands’ options, run:

$ pa path --help
Usage: pa path [OPTIONS] COMMAND [ARGS]...

  Perform some operations on files

  --help  Show this message and exit.

  delete   Delete file or directory at PATH.
  get      Get contents of PATH.
  share    Create a sharing link to a file at PATH or check its sharing...
  tree     Show preview of directory contents at PATH in tree-like format

  unshare  Disable sharing link for a file at PATH.
  upload   Upload CONTENTS to file at PATH.

If you’re familiar with the git command’s interface, ours works in a similar way.

path commands overview

Let’s assume there’s a PythonAnywhere user called “username” and they have default files plus some newly created ones:

$ mkdir -p ~/foo/bar; touch ~/foo/{baz,quux}

Common options for all commands

Running any (sub)command with --help will print its usage message. By default all commands will show our API error messages when one is received. That can be suppressed by adding -q (--quiet) flag.

$ pa path get ~/asdf

/                                                                         \
| GET to fetch contents of   |
| rname/files/path/home/username/asdf failed, got <Response [404]>: No such |
| file or directory: /home/username/asdf                                    |
\                                                                         /
$ if pa path get ~/asdf -q; then echo exists; else echo does not exist; fi
does not exist

get – show file contents or list directory


Now, when username runs pa path get ~ they will see contents of their HOME directory, including default (hidden) files and recently created directory foo. The output consists of two columns – first indicates the type (F for files, D for directories).

$ pa path get ~
F  .bash_history
F  .bashrc
D  .cache
F  .gitconfig
D  .local
F  .profile
F  .vimrc
D  .virtualenvs
D  foo

By default, the list is sorted alphabetically. Providing the -r flag will reverse the ordering, the -d flag will filter only by directories and -f will show only files (non-directories, to be strict, as in Linux everything is basically a file).

We can use also a -r (--raw) option which will dump the JSON API response to the output, if parsing it with other CLI utilities (like jq) is preferred. This has effect only when you’re accessing directories (because the output for a file is its contents – see later).

$ pa path get ~/foo --raw
{"bar": {"type": "file", "url": ""}, "baz": {"type": "file", "url": ""}, "quux": {"type": "directory", "url": ""}}
$ pa path get ~/foo -r | jq
  "bar": {
    "type": "file",
    "url": ""
  "baz": {
    "type": "file",
    "url": ""
  "quux": {
    "type": "directory",
    "url": ""

When the argument passed to pa path get is a file (non-directory), its contents will be displayed:

$ pa path get ~/README.txt
# vim: set ft=rst:

See (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.


The get command provides a list of the contents of a given path. To get an overview of the directory contents in a tree-like structure, we can use the tree command.

$ pa path tree ~
├── README.txt
└── foo/
    ├── bar
    ├── baz
    └── quux/

This command however is not a full implementation of its Linux counterpart – since path API calls are expensive, we limit the tree levels shown to 2, and no hidden files are listed.

delete file or directory

It does what it means – mind that it’s a dangerous command, since it will delete contents recursively and permanently! Let’s use our newly created directory foo:

$ pa path delete ~/foo

< /home/username/foo deleted! >
$ ls ~/foo
ls: cannot access '/home/username/foo': No such file or directory

upload contents of a file

We can upload the contents of a file to given path, or stream the contents directly to the standard input. If the path contains non-existent directories, they will be created. Mind that when the path points to an existing file, it will be overwritten!

$ echo "This is my new file" > file.txt
$ pa path upload --contents file.txt ~/somewhere/something

< Content successfully uploaded to /home/username/somewhere/something! >
10:12 ~ $ cat ~/somewhere/something
This is my new file

If we want to stream the contents directly from the command line, the syntax is a bit tricky:

$ pa path upload ~/somewhere/something -c - <<< "New contents stream"

< /home/pafkpa/somewhere/something successfully updated! >
10:18 ~ $ cat ~/somewhere/something
New contents stream

Using echo "Stream" | pa path upload ~/where -c - is also valid.

That, obviously, would probably not have much sense on PythonAnywhere, but we could imagine some useful scenarios while using pa commands from local machine (see: Running scripts on your local machine).

Sharing and unsharing files (share and unshare commands)

PythonAnywhere provides a feature to share your code with people online. That currently works only for files (and the path to the file must not be a symbolic link).

$ echo 'print("Hello, world!")' >
$ pa path share --check ~/

< /home/username/ has not been shared >
    ~<:>>>>>>>>> is not shared yet – we can easily change that:

$ pa path share ~/
/                                                                         \
| /home/username/ successfully shared at https://www.pythonanywher |
|             |
\                                                                         /

That SnakeSay dialog is quite effective, but we may prefer to get a raw link to the shared file instead:

$ pa path share --porcelain ~/

To unshare that file, simply run:

$ pa path unshare ~/
< /home/username/ is no longer shared! >

Running scripts on your local machine

While some of our helper scripts were meant to be run explicitly on PythonAnywhere (see: pa django --help), others can be run from outside of your PythonAnywhere environment. With the file API wrappers it may make even more sense to run some commands from your local machine than from PythonAnywhere, where you can easily perform file operations without doing extra calls to the API.

In order to run pa commands locally, apart from installing the pythonanywhere package, you need to provide two extra bits of information to the scripts:

  • the API token
  • your PythonAnywhere username (if it’s different than your machine’s user log name)

Using our example (of a user called “username”), that can be achieved by using LOGNAME and API_TOKEN environment variables, like this (the example is for a Linux environment):

$ export LOGNAME=username
$ export API_TOKEN='thisismytokenOneone1111!'

You can go even a step further and have a shim script for our API wrappers, which could look like this:


export LOGNAME=username
export API_TOKEN='thisismytokenOneone1111!'

# Below is the path to `pa` command -- it can be located elsewhere,
# depending on how you installed our pythonanywhere package
/home/mylocaluser/.virtualenvs/helperscripts/bin/pa "$@"

Then you’d run the script like this (assuming that the script is called “my_pa” and is located in the current working directory):

$ ./my_pa path get "/home/username/README.txt"

Further reading

comments powered by Disqus