Modern HTTP for modern Ruby.

Fiber-native concurrency, HTTP/1.1 + HTTP/2, and production-grade resilience in one Ruby HTTP client — without ever exposing its transport in the public API.

One client. Modern concurrency. Production-ready HTTP. Butler is the modern Ruby HTTP client built for concurrent, resilient applications — it owns its own public API (Client, Request, Response, Headers) built around Ruby's Fiber::Scheduler rather than one Thread per in-flight request. That's the whole point of the design: real HTTP/2 multiplexing, structured concurrency you can actually reason about (client.async { |tasks| ... }), and a resilience layer (timeouts, deadlines, retries with backoff+jitter, a circuit breaker) that's part of the client, not four extra gems bolted onto Net::HTTP.

gem install butler-http
# HTTParty-style, zero setup:
Butler.get("https://api.example.com/users").json

# Or a configured Client, for connection pooling/retries/middleware tuned per-API:
client = Butler::Client.new(base_url: "https://api.example.com")

client.async do |tasks|
  users  = tasks.async { client.get("/users") }
  orders = tasks.async { client.get("/orders") }
  { users: users.wait.json, orders: orders.wait.json }
end

What it does

HTTP/1.1 + HTTP/2 today. HTTP/3 architecture-ready. Early internal QUIC packet-protection groundwork already exists (RFC 9000/9001-vector-verified), but it's not a usable transport yet — no http_version: :http3 today, on purpose, until it actually is one.

How it compares

CapabilityButlerFaradayExconNet::HTTP
Fiber-native concurrency✓——limited
HTTP/2 (ALPN, multiplexed)✓ (first-class)adapter-dependentlimiteddepends
Connection pooling✓adapter✓manual
Retries + backoff/jitterbuilt-inmiddlewarelimitedmanual
Circuit breakerbuilt-inexternal gemexternal gemexternal gem
Deadlines (total budget across retries)✓———
OpenTelemetryfirst-class, optionalexternalexternalexternal
Native request stubbing✓via WebMockvia WebMockvia WebMock

Faraday gives you an ecosystem. Butler gives you a modern HTTP runtime. Async::HTTP gives you the foundation. Butler gives you the application-facing client. That's the one architectural rule that keeps this from rotting into "Faraday but slower": Butler owns its public abstractions. async/async-http implement the real transport underneath, but nothing outside lib/butler/transport.rb ever touches those types — the public API doesn't change if the transport underneath it ever does. Full breakdown, including Excon/HTTParty and a real Sequential/Concurrent benchmark matrix, in the README.

Competitor versions checked 2026-09-02: faraday 2.14.3, excon 1.7.1. Capability cells describe design intent, not an independent re-test of every release — re-check before quoting externally.

Guides

Links