API Spelunking with Org Babel

Occasionally I need to interact with an API and store their responses. For that ob-shell+curl does the job. Every now the responses are large enough that I need massage them with jq. That's where I use ob-jq. Recently I had to look for id of the latest fedora image Hetzner provides. Using that as an example I'll show how can one use ob-shell & ob-jq to explore JSON API responses.

You may be asking why use ob-shell+curl when there other packages like ob-restclient, walkman, etc. Shelling out to curl has the advantage of being easy to share the invocation with ~~infidels~~non-emacs users.

Encrypting the token

First a detour on storing credentials. APIs usually require a credential. We should shouldn't store any authentication token in plain text. Emacs provides auth-source to retrieve your secrets. auth-source can fetch the data from any number of sources, including your system's keyring. I prefer to store API tokens in an encrypted file, ~/.authinfo.gpg, which is encrypted using GPG. Each entry in the autinfo file should conform to the following format:

machine api.hetzner.cloud password 🎩t0psecret🎩

We can retrieve the token with the following invocation

(auth-source-pick-first-password :host "api.hetzner.cloud")

By using the :var header argument. This will let us bind an environment variable to the value of an elisp expression. All together now:

#+name: list-system-images.json
#+begin_src shell :var token=(auth-source-pick-first-password :host "api.hetzner.cloud") :results output
curl -H "Authorization: Bearer $token" "https://api.hetzner.cloud/v1/images?type=system"
#+end_src

Note that we gave a name to the src block. This is important so that we can refer to its output in another src block.

Massaging the response using jq

The API retrieves a lot of fields which are of little interest to me. So I can use jq-mode's org-babel integration to do so.

#+begin_src jq :stdin list-system-images.json
.images[] | select(.name | match("fedora")) | pick(.id, .name, .os_flavor, .os_version)
#+end_src

Next steps

Note that although the example it can be used for commands that output a large amount JSON, like cargo-metadata.