# Mint: low-level HTTP/1 and HTTP/2 for Elixir

> Mint gives Elixir applications direct, immutable access to HTTP connections over TCP and SSL, including HTTP/1 and HTTP/2. It fits code that needs connection control, but leaves pooling, process management, WebSockets, and response collection to the application or a higher-level library.

**elixir-mint/mint** — Functional HTTP client for Elixir with support for HTTP/1 and HTTP/2 🌱

- Repository: https://github.com/elixir-mint/mint
- Stars: 1,417 · Forks: 128
- Language: Elixir
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/elixir-mint-mint

## Why Mint exposes Elixir HTTP connections directly

Mint solves the problem of implementing HTTP behavior close to the BEAM's networking primitives. It is intended for Elixir developers who need control over HTTP/1 or HTTP/2 traffic, especially when a generic process-based HTTP client would impose ownership and supervision decisions that do not fit the application. The project describes itself as a functional, low-level client, and that boundary matters. It is closer in concept to `:gen_tcp` or `:ssl` with HTTP protocol handling layered on top than to an application client that hides sockets and manages connections for the caller.

The central design choice is a process-less architecture. A Mint connection is an immutable data structure wrapping a TCP or SSL socket. The application can place that value inside any suitable architecture, including a process that owns several connections or several processes that each handle a connection differently. Mint does not require one library-managed process per HTTP connection. This is useful when an application already has an actor, cache, gateway, or service abstraction whose ownership model conflicts with a conventional HTTP client pool.

That control also creates implementation work. Socket messages still belong to the receiving Elixir process, and the caller must pass them into Mint in the correct order. The connection value changes as requests, responses, and protocol state advance. Connection reuse, error handling, and message routing therefore need to fit the application's process design. Mint resolves protocol mechanics, not the full operational model around them.

## Mint's immutable connection and HTTP message flow

A request begins with `Mint.HTTP.connect/3`. The scheme and port determine whether Mint opens a plain TCP connection or an SSL connection. According to the README, this call transparently chooses between HTTP/1 and HTTP/2 rather than requiring the caller to select the protocol in the basic example. The returned connection is then passed to `Mint.HTTP.request/5`, which accepts a method, path, headers, and request body and returns a connection, request reference, or an error.

The socket uses active mode with `active: :once`. The caller receives raw TCP or SSL messages, but the README explicitly says not to inspect those messages. Instead, each message is passed to `Mint.HTTP.stream/2`. Mint interprets the bytes and returns zero or more response parts associated with the request reference. Documented parts include `{:status, request_ref, status}`, `{:headers, request_ref, headers}`, `{:data, request_ref, body}`, and `{:done, request_ref}`. A response body can therefore arrive across several calls and several socket messages rather than as one complete body string.

This distinction has an important consequence for control flow. A matching receive clause is not proof that a complete response is available. `Mint.HTTP.stream/2` might return no response parts on a particular call, while later socket messages complete the response. Code must accumulate parts until it sees `:done`, and it must match request references when supporting concurrent requests. The README example prints the parts produced from one message; it does not document a body accumulator or a higher-level response function.

The connection is stateless from the API caller's perspective. Every successful function may return an updated connection, which must replace the old value. Reusing an old connection discards protocol state represented by the newer value. This requirement applies to requests and streaming calls, not merely connection establishment, and is the most direct implementation constraint in the basic workflow.

## Installing Mint and issuing the first HTTPS request

The README instructs users to add Mint as a Mix dependency. The following snippet uses the current `~> 1.0` requirement shown in the installation section. The `~>` requirement is a version constraint, so dependency resolution remains subject to the compatible releases available through Hex.pm.

```elixir
defp deps do
  [
    {:mint, "~> 1.0"}
  ]
end
```

Fetch dependencies from the directory containing the Mix project:

```sh
mix deps.get
```

A first real request can connect to `httpbin.org` over HTTPS, send a `GET` request for `/`, and pass the next socket message to `Mint.HTTP.stream/2`. The receive loop should continue because one socket message does not necessarily contain the complete response.

```elixir
{:ok, conn} = Mint.HTTP.connect(:https, "httpbin.org", 443)
{:ok, conn, request_ref} = Mint.HTTP.request(conn, "GET", "/", [], "")

receive do
  message ->
    {:ok, conn, responses} = Mint.HTTP.stream(conn, message)
    IO.inspect(responses)
end
```

This example prints the returned state instead of using it for a subsequent request, so it is an inspection exercise rather than a complete client loop. A real caller must preserve the new `conn`, continue receiving messages until `{:done, ^request_ref}` appears, and define what to do with error tuples. The README does not document a built-in receive loop, body collection function, timeout policy, or graceful shutdown sequence in this tutorial.

Mint supports both plain HTTP and TLS. For the plain connection shown in the conceptual example, use the `:http` scheme and port `80`. For HTTPS, use `:https` and port `443`. Those examples communicate with a public service and are suitable for illustrating the API, but the README's test documentation separately warns that DNS-based blockers can interfere with integration sites.

## TLS certificates, WebSockets, and missing higher-level features

Mint has a specific certificate boundary. When an SSL connection is opened without a caller-supplied CA certificate store, Mint uses the system certificate store if Erlang/OTP 25 or later is in use. If neither condition applies, the README instructs developers to add `:castore` to their dependencies. That requirement matters when defining the application's supported OTP versions, especially in CI images and deployment platforms that are not aligned with local development.

The certificate example in the README uses `{:castore, "~> 1.0.0"}` and shows `{:mint, "~> 0.4.0"}`. That is inconsistent with the main installation section, which shows `{:mint, "~> 1.0"}`. The older Mint constraint should not be copied into a new Mint 1.x project without checking dependency compatibility. The relevant project-specific verification is the version constraint in `mix.exs`, the selected `:castore` constraint, and the OTP version used by each runtime.

WebSockets are outside Mint's protocol scope. The README says Mint does not support the WebSocket protocol, although it can serve as a foundation for a WebSocket implementation. It directs developers to `mint_web_socket` when that capability is needed. This makes Mint the wrong dependency when the only requirement is a complete WebSocket client, because a separate project provides the missing protocol layer.

Connection management presents another boundary. The README identifies Mint as low-level and points to Finch for connection management, pooling, metrics, and related higher-level features. Developers choosing between these projects should decide whether protocol control justifies writing socket-message handling and connection-state discipline. The built-in documentation is strongest for requests and response parts, not for the operational policies an application normally expects from a managed client.

## Why Mint differs from Finch and process-owned clients

Finch is the concrete alternative named by Mint's README. Finch is built on Mint and adds higher-level concerns such as pooling, connection management, and metrics. The architectural difference is the issue, not a claim that one always produces better code. Mint starts with an immutable connection and explicit socket-message streaming. Finch places those mechanics inside a higher-level client abstraction, which the README identifies as providing pooling and related operational features.

This distinction is useful when comparing API shape. An application that needs HTTP/2 or direct transport access may prefer Mint so its own service process can define message routing. An application that primarily needs outbound requests, reuse, and predictable pool behavior may prefer Finch because it delegates those concerns. Mint's process-less design also permits one process to handle multiple connections, but that flexibility is not automatic connection management. The developer remains responsible for wrapping and retaining the connection struct.

Finch is not documented here as a drop-in behavioral replacement. Its exact configuration, API, compatibility constraints, and release state cannot be established from Mint's README. The supported comparison is narrower: the Mint project itself says that Finch is built on top of Mint and provides connection management, pooling, and metrics. Evaluate the requested transport control first, then inspect the higher-level project's current documentation and dependency compatibility before selecting it.

For teams familiar with Elixir libraries that own a process per client, the migration cost is likely to be more visible than the `mix deps.get` step. Mint requires explicit handling of `Mint.HTTP.stream/2`, request references, immutable connection updates, and incoming TCP or SSL messages. Those responsibilities can be appropriate inside an existing service architecture, but they should not be mistaken for conveniences supplied by the library.

## Testing, maintenance, and Apache-2.0 implications

The repository includes unit and integration tests, with integration cases that reach real websites tagged `:requires_internet_connection`. The README provides separate commands for the default suite and local execution without internet-connected tests. Proxy tests are tagged `:proxy`, excluded by default, and require Docker Compose or Podman Compose services from the project root. This test setup is relevant to contributors because the proxy configuration includes HTTP, HTTPS, and H2C endpoints backed by local containers.

```sh
mix test
```

The README says this runs the default tests, including tests tagged `:requires_internet_connection`, but excluding `:proxy`. For an environment without internet access, it documents:

```sh
mix test --exclude requires_internet_connection
```

Proxy tests require local services. The documented container port variables are `TINYPROXY_PORT`, `TINYPROXY_AUTH_PORT`, `HTTPBIN_HTTP_PORT`, `HTTPBIN_HTTPS_PORT`, and `HTTPBIN_H2C_PORT`. The README lists defaults of `8888`, `8889`, `8080`, `8443`, and `8081` respectively. A contributor who changes these ports must pass the same values to both service startup and the test command, as in the documented `TINYPROXY_PORT=8887` example. The Docker configuration also defines `SQUID_HTTPS_PORT` with default `8890`, although that variable is not included in the README's list of available port variables.

The repository is not archived, and the last push was on 2026-10-16. There are no GitHub releases in the repository record, so release cadence cannot be inferred from GitHub release entries. Upgrade planning should therefore account for Mix dependency constraints and `CHANGELOG.md`, rather than assuming tagged releases are available. Mint is licensed under Apache-2.0. The repository includes the license text and copyright notices; the stated license permits use under its terms, subject to the license's own conditions and the application's compliance obligations.

## Conclusion

Adopt Mint when the application needs direct control over immutable HTTP connection state, protocol parsing, or HTTP/2 behavior. Do not adopt it alone when the requirement is WebSockets, automatic pooling, metrics, or a conventional process-owned client abstraction. Before adoption, verify the required OTP version, CA certificate setup, request and response APIs in `mix.exs` and the Hex documentation, and the operational policy for retaining the connection value returned by every API call.

## FAQ

### Does Mint support HTTP/2?

Yes. The project description and README state that Mint supports HTTP/1 and HTTP/2, and the basic `Mint.HTTP.connect/3` example says it transparently chooses between them.

### Does Mint handle WebSockets?

No. Mint explicitly does not implement the WebSocket protocol. Its README points to `mint_web_socket` for WebSocket support and says Mint can provide a foundation for such a client.

### Does Mint provide connection pooling and metrics?

Mint is a low-level client and leaves those features to the surrounding application. The README points to Finch for connection management, pooling, metrics, and related higher-level features.

## Sources

- [elixir-mint/mint on GitHub](https://github.com/elixir-mint/mint)
- [Issues](https://github.com/elixir-mint/mint/issues)
- [License: Apache-2.0](https://github.com/elixir-mint/mint/blob/main/LICENSE)
- [README](https://github.com/elixir-mint/mint/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/elixir-mint-mint
