The HTTP client for Elixir

License: Unlicense

Language: Elixir

Keywords: client, elixir, http, ibrowse, rest version downloads API Docs Build Status unlicense


HTTP client for Elixir, based on ibrowse. Continues the HTTPun tradition of HTTParty, HTTPretty, HTTParrot and HTTPie.


Add HTTPotion to your project's dependencies in mix.exs:

  defp deps do
      {:httpotion, "~> 3.1.0"}

  def application do
    [ applications: [:httpotion] ]
    # Application dependency auto-starts it, otherwise: HTTPotion.start

And fetch your project's dependencies:

$ mix deps.get


Note: You can load HTTPotion into the Elixir REPL by executing this command from the root of your project:

$ iex -S mix

Note: HTTPotion now enables certificate verification by default, using a few default CA bundle paths (/etc/ssl/cert.pem etc.) or the certifi package (which is not a mandatory dependency).

Some basic examples:

iex> response = HTTPotion.get ""
  body: "{\n",
  headers: %HTTPotion.Headers{ hdrs: %{"connection" => "keep-alive", …} },
  status_code: 200

iex> HTTPotion.Response.success?(response)

# Response headers are wrapped to allow case-insensitive access (and to support both atoms and strings)
iex> response.headers[:sErvEr]

iex> response.headers["ConTenT-TyPe"]

# Response headers can have multiple values
iex> response = HTTPotion.get ""
  body: "{\n",
  headers: %HTTPotion.Headers{ hdrs: %{"foo" => ["1", "2"], "bar" => "1" …} },
  status_code: 200

# You can provide a map for the query string
iex> HTTPotion.get("", query: %{page: 2})
%HTTPotion.Response{body: "", headers: …, status_code: 200}

# Follow redirects
iex> HTTPotion.get("", follow_redirects: true)
%HTTPotion.Response{body: "…<title>Example Domain</title>…", headers: …, status_code: 200}

# Send form data
iex> "", [body: "hello=" <> URI.encode_www_form("w o r l d !!"),
  headers: ["User-Agent": "My App", "Content-Type": "application/x-www-form-urlencoded"]]
%HTTPotion.Response{body: "", headers: …, status_code: 200}

# Use a custom method
iex> HTTPotion.request :propfind, "", [body: "I have no idea what I'm doing"]
%HTTPotion.Response{body: "", headers: …, status_code: 405}

# Send Basic auth credentials
iex> HTTPotion.get "", [basic_auth: {"foo", "bar"}]
  body: "",
  headers: %HTTPotion.Headers { hdrs: %{"Access-Control-Allow-Credentials": "true", …} },
  status_code: 200

# Pass options to ibrowse (note that it usually takes char_lists, not elixir strings)
iex> HTTPotion.get "", [ ibrowse: [ ssl_options: [ versions, [:'tlsv1.1'] ] ] ]
%HTTPotion.Response{body: "…TLS SNI: present - Check TLS - (https,tls1.1,ipv4)…", headers: …, status_code: 200}

# Change the timeout (default is 5000 ms)
iex> HTTPotion.get "", [timeout: 10_000]

# If there is an error a `HTTPotion.ErrorResponse` is returned
iex> HTTPotion.get "http://localhost:1"
%HTTPotion.ErrorResponse{message: "econnrefused"}

# You can also raise `HTTPError` with the `bang` version of request
iex> HTTPotion.get! "http://localhost:1"
** (HTTPotion.HTTPError) econnrefused

The Response is a struct, you can access its fields like: response.body.

response.headers is a struct (HTTPotion.Headers) that wraps a map to provide case-insensitive access (so you can use response.headers[:authorization] and it doesn't matter if the server returned AuThOrIZatIOn or something).

HTTPError is an exception that happens when a bang request (request! / get! / …) fails.

Available options and their default values:

  body: "",                # Request's body contents, e.g. "{json: \"string\"}"
  headers: [],             # Request's headers, e.g. [Accept: "application/json"]
  query: nil,              # Query string, e.g. %{page: 1}
  timeout: 5000,           # Timeout in milliseconds, e.g. 5000
  basic_auth: nil,         # Basic auth credentials, e.g. {"username", "password"}
  stream_to: nil,          # A process to stream the response to when performing async requests
  direct: nil,             # An ibrowse worker for direct mode
  ibrowse: [],             # ibrowse options
  auto_sni: true,          # Whether TLS SNI should be automatically configured (does URI parsing)
  follow_redirects: false, # Whether redirects should be followed

Metaprogramming magic

You can extend HTTPotion.Base to make cool HTTP API wrappers (this example uses Poison for JSON):

defmodule GitHub do
  use HTTPotion.Base

  def process_url(url) do
    "" <> url

  def process_request_headers(headers) do
    Dict.put headers, :"User-Agent", "github-potion"

  def process_response_body(body) do
    body |> Poison.decode!
iex> GitHub.get("users/myfreeweb").body["public_repos"]

Read the source to see all the hooks.

Keep in mind that process_response_body and process_response_chunk get iodata. By default, they call IO.iodata_to_binary. But efficient parsers like Poison can work directly on iodata.

Asynchronous requests

You can get the response streamed to your current process asynchronously:

iex> HTTPotion.get "", [stream_to: self]
%HTTPotion.AsyncResponse{id: -576460752303419903}

iex> flush
  id: -576460752303419903,
  status_code: 200,
  headers: %HTTPotion.Headers{ hdrs: %{"connection" => "keep-alive", …} }
  id: -576460752303419903,
  chunk: "{\n"
  id: -576460752303419903

Note that instead of process_response_body, process_response_chunk is called on the chunks before sending them out to the receiver (the stream_to process).

Direct access to ibrowse workers

ibrowse allows you to use its separate worker processes directly. We expose this functionality through the direct option.

Don't forget that you have to pass the URL to the worker process, which means the worker only communicates with one server (domain!)

iex> {:ok, worker_pid} = HTTPotion.spawn_worker_process("")

iex> HTTPotion.get "", [direct: worker_pid]
%HTTPotion.Response{body: "", headers: ["Connection": "close", …], status_code: 200}

You can even combine it with async!

iex> {:ok, worker_pid} = HTTPotion.spawn_worker_process("")

iex> "", [direct: worker_pid, stream_to: self, headers: ["User-Agent": "hello it's me"]]
%HTTPotion.AsyncResponse{id: {1372,8757,656584}}

Type analysis

HTTPotion contains typespecs so your usage can be checked with dialyzer, probably via dialyxir or elixir-ls.

HTTPotion's tests are checked with dialyxir.


Please feel free to submit pull requests!

By participating in this project you agree to follow the Contributor Code of Conduct.

The list of contributors is available on GitHub.


This is free and unencumbered software released into the public domain.
For more information, please refer to the UNLICENSE file or

Project Statistics

Sourcerank 15
Repository Size 210 KB
Stars 717
Forks 107
Watchers 19
Open issues 7
Dependencies 10
Contributors 48
Tags 18
Last updated
Last pushed

Top Contributors See all

myfreeweb Aleksei Magusev Dave Thomas Adam Kittelson Ookami Guillermo Iguaran‮ Cameron Currie ben Sumeet Singh Jonas Adler parroty Maciej Szlosarczyk Hugo Ribeira Charles Treatman Daniel Berkompas Andrew Forward Everton Ribeiro d0rc Bradley D Smith Eito Katagiri

Packages Referencing this Repo

Fancy HTTP client for Elixir, based on ibrowse.
Latest release 3.1.3 - Updated - 717 stars

Recent Tags See all

v3.1.2 May 03, 2019
v3.1.1 January 23, 2019
v3.1.0 February 10, 2018
v3.0.3 August 08, 2017
v3.0.2 October 01, 2016
v3.0.1 August 10, 2016
v3.0.0 June 08, 2016
v2.2.2 February 28, 2016
v2.2.1 February 26, 2016
v2.2.0 February 11, 2016
v2.1.0 May 21, 2015
v2.0.0 January 31, 2015
v1.0.0 December 13, 2014
v0.2.4 July 02, 2014
v0.2.3 May 12, 2014

Interesting Forks See all

The HTTP client for Elixir
Elixir - WTFPL - Last pushed - 1 stars
The HTTP client for Elixir
Elixir - WTFPL - Last pushed - 1 stars
The HTTP client for Elixir
Elixir - WTFPL - Updated - 1 stars

Something wrong with this page? Make a suggestion

Last synced: 2019-08-18 10:52:57 UTC

Login to resync this repository