I'm putting together an app, and want to be able to test the api end points. I figured Emacs would be a cool way to go - especially if JSON responses could be parsed and the return data used in subsequent tests.

Any ideas on how to go about this, or is this just crazy?

  • Easiest way is to invoke a curl shell command, and parse results with json read.
    – Malabarba
    Commented Oct 22, 2014 at 17:25

6 Answers 6

  • restclient is the most "interactive" mode.

    This is a tool to manually explore and test HTTP REST webservices. Runs queries from a plain-text query sheet, displays results as a pretty-printed XML, JSON and even images.


    You can check a workflow example at http://jakemccrary.com/blog/2014/07/04/using-emacs-to-explore-an-http-api/.

  • request.el - HTTP swiss knife.

     :params '(("key" . "value") ("key2" . "value2"))
     :parser 'json-read
     :success (function*
               (lambda (&key data &allow-other-keys)
                 (message "I sent: %S" (assoc-default 'args data)))))
  • emacs-web - "The idea is to always use callbacks to collect the response."

    The JSON callback form allows just data to be collected:

    ;; -*- lexical-binding: t -*-
    (require 'web)
      (lambda (data &rest stuff)
         (message "%S" data))
      :url "https://httpbin.org/post")

Old question, yes... But in the event that anyone Googles this; another option is using Org Babel and ob-http... (Can be installed from melpa.)

With org-babel, you can make an .org file containing "http" code blocks. When these blocks are evaluated, they will make the HTTP request and return the response as the result.

If these blocks have a "#+NAME:" attribute, you can use their results in any other org-babel block. This allows for some pretty neat literate programming using the results of HTTP requests.

For example, here is a little Org document demonstrating making an HTTP request and parsing the returned JSON in Ruby:

* The request
The following is the example request shown on [[https://github.com/zweifisch/ob-http][ob-http's Github page]]...

It isn't anything fancy, but it is a REST API request, and returns
JSON, so it works for the sake of this demonstration:

#+BEGIN_SRC http :pretty
  GET https://api.github.com/repos/zweifisch/ob-http/languages

: {
:   "Emacs Lisp": 7034
: }

Notice how this has a "=#+NAME:=" attribute? This is a name we can use
in other =org-babel= blocks. (As you will see below)

* Using the request
Now that I have an =http= request in an org block with a name... Lets
write something in a completely different language and use our HTTP
request's response:

#+BEGIN_SRC ruby :var langs=ob-languages
  require 'json'
  JSON.parse(langs)['Emacs Lisp']

: 7034

The =:var= keyword allowed me to assign the "=langs=" variable in the
Ruby block to the result of the =ob-languages= block [[The request][above]].

This didn't have to be in Ruby, this could have been any language,
including another =http= block.

This is what this looks like in org-mode: ob-http in org-mode

Hitting C-c C-c on the bottom block (The Ruby one) will automagically evaluate the top one for its dependency (That's the :var bit in the block's header.). This will mean that the http request is made first, and then the results are passed to Ruby for further processing.

You can do this with as many blocks as you like and with as many languages.

If this suits your needs, ob-http requires a little bit of manual tweaking after you install it to get it working. (Don't worry, it isn't that much)

After installing ob-http, you need to customize two variables: org-src-lang-modes, and org-babel-load-languages.

So, by running M-x customize-variable, you can customize each to include the following:

org-src-lang-modes: You will want to customize this variable to include one more language mapping, so you can insert one more value to the end of the list:

String: http
Symbol: ob-http

Then you can C-x C-s to save that customization.

org-babel-load-languages: You will want to add one more item to the list of enabled org languages. "http".

Though, you might need to add it to the list manually if the option is not in the possible options, you can do that by clicking "State" and choosing ":" to show the Lisp expression... Then you would add the following just before the last closing parenthesis:

(http . t)

After that, a quick C-x C-s and M-x org-reload should be all you need..!

  • 1
    how can ob-http be used for posting files? Commented Dec 24, 2018 at 7:43

I wrote httprepl for exactly this purpose. It gives you a comint-based repl where you can issue http requests.


You can easily add functions that consume the result of requests. These are usually used for pretty printing etc but parsing to an elisp structure should be simple using the builtin json library.


I recently wrote a package for this:


It's inspired on the restclient package, but tries to improve upon some of its core ideas. The requests are written in an Org buffer, where requests found in higher level headings inherit properties from the ones in lower levels. This way, you can avoid writing common properties shared across many requests multiple times. It also has integration with Babel.


Mixing two interesting aproaches, restclient and org-babel, there is also the ob-restclient package. Interestingly, as an org code block can get arguments from other code blocks or variables, and in the header arguments can embed jq postprocessing commands.

In this example the project-id in the second query comes from the first query result. There are also some globally defined tags:

Some ob-restclient examples


It's perfectly possible to do because I do almost exactly that same thing myself. There are a couple of caveats though. First of all I have a locally patch xmlrpc.el library. This solves two problems, an incompatibility with modern emacs and the ability to pass authentication headers in the request. The data I'm processing is JSON in XML but you may be able to skip the XML step depending on your setup.

All the heavy RPC lifting is done in lava-rpc.el but it shouldn't be hard to follow. You can see the call to submit a stringified piece of json here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.