edgurgel/httpoison
> The long-serving default HTTP client of the Elixir ecosystem — a thin, ergonomic Elixir wrapper over Erlang's hackney.
GitHub repo · Hex package · License: MIT
Overview
HTTPoison is an HTTP client library for Elixir, started by Eduardo Gurgel in 2013 as a port of HTTPotion (which wrapped ibrowse) onto hackney, benoitc's Erlang HTTP client[^1]. For most of Elixir's first decade it was the de facto answer to "how do I make an HTTP request" — {:httpoison, "~> x.y"} appears in an enormous number of tutorials, books, and legacy codebases, and at ~2,300 GitHub stars it remains the most-starred Elixir HTTP client.
Its design is deliberately thin: HTTPoison provides idiomatic Elixir function heads (get/3, post/4, bang variants), response structs you can pattern-match on, and an overridable HTTPoison.Base behaviour for building API clients — and delegates everything hard (connection pooling, TLS, proxies, multipart encoding, chunked transfer) to hackney. That thinness is both the appeal and the defining constraint: HTTPoison inherits hackney's capabilities, defaults, bugs, and limits wholesale, including the absence of HTTP/2 support.
The project is mature and still maintained — v3.0.0 shipped in June 2026 to track hackney 4.0[^2], and the last push was July 2026 — but it is in a conservative maintenance mode rather than active feature development. The ecosystem's center of gravity for new projects has shifted to the Mint-based stack (Finch, and Req on top of it), which is pure Elixir, supports HTTP/2, and integrates telemetry natively. HTTPoison today is best understood as the stable incumbent you already have, not the client you would pick fresh.
Getting Started
# mix.exs — HTTPoison 3.x requires hackney 4.0, i.e. Erlang/OTP 27+ / Elixir 1.17+
def deps do
[{:httpoison, "~> 3.0"}]
end
case HTTPoison.get("https://api.example.com/users/1",
[{"Accept", "application/json"}],
recv_timeout: 10_000) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
Jason.decode!(body)
{:ok, %HTTPoison.Response{status_code: 404}} ->
{:error, :not_found}
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
end
Note the two-step error model: a 404 is a successful request ({:ok, response}); only transport-level failures (:econnrefused, :timeout, :nxdomain) return {:error, %HTTPoison.Error{}}. Status-code handling is always your job.
Architecture / How It Works
HTTPoison is a small facade in front of hackney:
HTTPoison.Base— ause-able macro module that defines the whole client surface.HTTPoisonitself is justuse HTTPoison.Basewith no overrides. Your own modules canuse HTTPoison.Baseand overrideprocess_request_url/1,process_request_headers/1,process_response_body/1, etc. to build declarative API clients (base URL injection, automatic JSON decoding)[^1]. This pre-dates and is cruder than Tesla's middleware stacks or Req's step pipeline — the hooks are compile-time function overrides, not composable runtime plugins.- Response structs —
%HTTPoison.Response{status_code, body, headers, request_url},%HTTPoison.AsyncResponse{},%HTTPoison.Error{}. Pattern matching on these is the idiomatic control flow. - Async streaming — pass
stream_to: pidand hackney delivers%HTTPoison.AsyncStatus{},%HTTPoison.AsyncHeaders{},%HTTPoison.AsyncChunk{},%HTTPoison.AsyncEnd{}as plain messages to that process's mailbox. With no backpressure by default, a fast server can flood the receiver;async: :onceplus explicitHTTPoison.stream_next/1is the flow-controlled variant[^1]. - Everything else is hackney passthrough — options under the
hackney:keyword (pools, cookies, proxy,insecure) go straight to hackney;:ssloptions go to the Erlangsslmodule. Multipart bodies use hackney's tuple format verbatim.
The coupling story is the whole story. Connection pooling is hackney's pool model: named pools, checkout/checkin, keepalive reuse per host. TLS defaults (certificate verification via certifi + ssl_verify_fun) are hackney's. HTTP/1.1-only is hackney's. When hackney 4.0 raised its floor to OTP 27, HTTPoison had to cut a major version (3.0) purely to follow[^2]. HTTPoison cannot fix or feature past its engine.
Production Notes
- The v2.0 SSL semantics change is the one to know. Before 2.0, passing any
:ssloption replaced hackney's default SSL options entirely — so adding an innocuouscertfilecould silently disable certificate verification. Since 2.0,:ssloptions merge into hackney's secure defaults, with:ssl_overriderestoring the old replace behaviour explicitly[^3]. Audit any pre-2.0 code you migrate. - Pool exhaustion is the classic failure mode. hackney's default pool caps concurrent connections (per its own defaults) and slow upstreams hold checkouts; under load you get
:checkout_timeout/:connect_timeouterrors that look like network failures but are self-inflicted. Size dedicated pools per upstream with:hackney_pool.start_pool/2(or as a supervisedchild_spec) rather than sharing:default[^4]. Historically, some hackney versions leaked connections back into pools on timeout — pin recent hackney and monitor pool stats. recv_timeoutdefaults to 5000 ms — routinely too short for slow third-party APIs and file downloads, and the first knob to turn when you see spurious:timeouterrors.- No transparent gzip. hackney does not auto-decompress
Content-Encoding: gzipresponses; if you sendAccept-Encodingyourself you must inflate the body manually (:zlib.gunzip/1). A recurring gotcha inHTTPoison.Base.process_response_body/1callbacks. - No HTTP/2, no built-in telemetry. Services that require HTTP/2 (some gRPC-adjacent APIs, APNs) need Mint/Finch or gun. Instrumentation means wrapping calls yourself or dropping to
:hackney_tracefor low-level wire logging. - Upgrade cliffs are OTP-driven. 3.x requires OTP 27+/Elixir 1.17+[^2]; teams on older Erlang must stay on 2.2.x. The 1.x → 2.0 jump is the SSL merge change above. Otherwise the API has been remarkably stable since 1.0 (2018) — which is exactly why so much legacy code still runs on it untouched.
When to Use / When Not
Use when:
- You maintain an existing Elixir codebase already on HTTPoison — the API is stable and migration buys little unless you need HTTP/2 or telemetry.
- You want a battle-tested HTTP/1.1 client with a decade of Stack Overflow answers and library integrations behind it.
- A dependency you use (older SDKs, ex_aws adapters) already pulls in hackney anyway, so HTTPoison adds near-zero weight.
Avoid when:
- You are starting a new Elixir project — wojtekmach/req is the current community default and strictly more capable for the common cases.
- You need HTTP/2, connection multiplexing, or fine-grained backpressure — hackney offers none of these.
- You want first-class telemetry, retries, and JSON handling without hand-rolling them in
HTTPoison.Baseoverrides.
Alternatives
- wojtekmach/req — batteries-included client on Finch/Mint (auto JSON, retries, compression, telemetry); use it for any new Elixir project.
- sneako/finch — pooled, performance-focused client on Mint with HTTP/2; use it when you control high-throughput service-to-service calls.
- elixir-mint/mint — low-level, processless HTTP/1.1+HTTP/2 client; use it when building your own pooling/streaming abstractions.
- elixir-tesla/tesla — middleware-based client with swappable adapters (including hackney); use it when you want composable request pipelines or adapter portability.
- ninenines/gun — Erlang HTTP/1.1, HTTP/2, and Websocket client; use it when you need persistent connections or Websockets from Erlang/Elixir.
History
| Version | Date | Notes | |---------|------|-------| | 0.1 | 2013-09 | Initial release; HTTPotion API ported onto hackney[^1]. | | 0.8.0 | 2015-11 | Maturing pre-1.0 line; async and multipart support established. | | 1.0.0 | 2018-01 | API stabilized after ~4.5 years of 0.x[^5]. | | 1.8.0 | 2021-01 | Last 1.x feature release; long maintenance tail follows. | | 2.0.0 | 2023-01 | :ssl options merge with hackney defaults instead of replacing them; :ssl_override added[^3]. | | 2.2.0 | 2023-11 | Maintenance line for OTP < 27 users. | | 2.3.0 | 2025-11 | Final 2.x release[^5]. | | 3.0.0 | 2026-06 | Tracks hackney 4.0; requires Erlang/OTP 27+ / Elixir 1.17+[^2]. |
References
[^1]: HTTPoison README. https://github.com/edgurgel/httpoison#readme [^2]: HTTPoison v3.0.0 release (2026-06-14) and README requirements: hackney 4.0, Erlang/OTP 27+, Elixir 1.17+. https://github.com/edgurgel/httpoison/releases/tag/v3.0.0 [^3]: "Merge ssl options" — edgurgel/httpoison PR #466, the motivating change for v2.0. https://github.com/edgurgel/httpoison/pull/466 [^4]: hackney README — connection pools, default pool behaviour. https://github.com/benoitc/hackney#use-the-default-pool [^5]: HTTPoison releases (version dates). https://github.com/edgurgel/httpoison/releases
Tags
elixir, erlang, http-client, hackney, networking, api-client, otp, library, http1