ExCurlImpersonate CI Hex.pm Hexdocs.pm

View Source

Elixir bindings for curl-impersonate using Zig's C interoperability: make HTTP requests with the TLS (JA3/JA4) and HTTP/2/HTTP/3 fingerprints of real browsers.

[!NOTE] This is a fork of ex_curl by Daniel Rudnitski, adapted to link against curl-impersonate's libcurl-impersonate instead of the system libcurl. On top of the original API it adds browser impersonation (:impersonate), HTTP version selection including HTTP/3 (:http_version) and every curl-impersonate fingerprint option. Kerberos (:http_auth_negotiate) was removed because libcurl-impersonate is built without GSS-API.

Installation

def deps do
  [
    {:ex_curl_impersonate, "~> 0.2.0"}
  ]
end

Compiling requires Zig 0.16 (or let Zigler fetch it). No system curl is needed: the pinned libcurl-impersonate v2.3.0 release for your platform is downloaded at compile time, checksum-verified and shipped in priv/lib/ next to the NIF. On Linux x86_64 (glibc) the release comes from our fork, which adds the chrome153–chrome155 targets (plus _macos and _linux variants).

Zigler prefers its own toolchain in ~/.cache/zigler and otherwise falls back to the first zig on your PATH. If an older Zig is on PATH you will see errors like build.zig.zon: error: expected string literal; fix it by running mix zig.get once (downloads Zig 0.16 into Zigler's cache) or mise exec zig@0.16.0 -- mix compile.

Supported platforms: Linux x86_64/aarch64 (glibc and musl), macOS arm64/x86_64. For other platforms or offline builds, set CURL_IMPERSONATE_DIR to a directory containing include/curl/curl.h and libcurl-impersonate.so (or .dylib).

The bundled library looks for CA certificates in /etc/ssl/certs/ca-certificates.crt (Linux) and /etc/ssl/cert.pem (macOS). On distros that keep them elsewhere (RHEL, Amazon Linux, minimal containers) pass :cainfo, e.g. cainfo: "/etc/pki/tls/certs/ca-bundle.crt" (or :capath).

Usage

ExCurlImpersonate.get("https://tls.peet.ws/api/all", impersonate: "chrome136")

# HTTP/3
ExCurlImpersonate.get("https://cloudflare-quic.com/", impersonate: "chrome136", http_version: :http3_only)

# fine-tune the fingerprint
ExCurlImpersonate.get("https://example.com", impersonate: "chrome136", tls_grease: false, httpbaseheader: [])

See the ExCurlImpersonate module docs for all options.

HTTP Clients and Shared Defaults

# create clients and set default options
defmodule CustomClient do
  use ExCurlImpersonate.Client, defaults: [headers: %{"User-Agent" => "custom-user-agent"}]
end

# all requests using CustomClient will use the custom User-Agent header
CustomClient.get("https://httpbin.org/get")
# => {:ok, %ExCurlImpersonate.Response{status_code: 200, body: ".."}}

You can use ExCurlImpersonate.Client on any module to create HTTP Clients. Below is an example of a GitHub API client based on the great Tesla documentation:

defmodule GitHubClient do
  use ExCurlImpersonate.Client, defaults: [base_url: "https://api.github.com"]

  def user_repos(username), do: get("/users/#{username}/repos")
end

# we can now call the GitHubClient with the pre-defined HTTP verb functions:
GitHubClient.get("/users/danielrudn/repos")
# => {:ok, %ExCurlImpersonate.Response{status_code: 200, body: "{....}"}}

# or call our custom defined functions:
GitHubClient.user_repos("danielrudn")
# => {:ok, %ExCurlImpersonate.Response{status_code: 200, body: "{....}"}}

More examples and details on using ExCurlImpersonate.Client are available on the ExCurlImpersonate.Client module documentation page.

Licensing

This package is MIT licensed. The downloaded libcurl-impersonate bundles curl, BoringSSL, nghttp2, nghttp3, ngtcp2, brotli, zstd, zlib and libidn2 (LGPL-3.0/GPL-2.0); their license notices ship in priv/lib/curl-impersonate-licenses/.

Documentation

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_curl_impersonate.

Acknowledgements

This library is built on top of curl-impersonate as a NIF using Zigler and Zig's C interoperability.

Inspired by Tesla, Katipo, and Req.