HTTP Clients in Ruby

· 4 min read · Updated March 31, 2026 · intermediate
ruby http net-http httparty faraday guides

Ruby gives you several ways to make HTTP requests. You can go with the standard library’s Net::HTTP, pull in the versatile Faraday gem with its middleware stack, or use HTTParty for quick one-liners. Each has a place depending on your needs.

This guide compares the three main approaches and shows you when to use which.

Net::HTTP — The Standard Library Workhorse

Net::HTTP ships with Ruby. No gems needed, no dependencies to manage. It handles everything from simple GET requests to persistent connections with custom headers and timeouts.

Basic GET Request

require 'net/http'
require 'uri'

uri = URI('https://api.github.com/repos/ruby/ruby')
response = Net::HTTP.get_response(uri)

puts response.code       # => "200"
puts response['content-type']  # => "application/json; charset=utf-8"
puts response.body

For HTTPS, enable SSL:

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER

response = http.get(uri)

Setting Headers

Pass a hash to add headers:

request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer your-token'
request['Accept'] = 'application/json'

response = http.request(request)

Timeouts

http.open_timeout = 5   # seconds to wait for a connection
http.read_timeout = 10  # seconds to wait for a response

response = http.get(uri)

Persistent Connections

Reuse the same connection across multiple requests:

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.start

# Connection stays open across these requests
10.times do
  response = http.get('/users/1')
  puts response.code
end

http.finish

POST with Body

require 'json'

uri = URI('https://httpbin.org/post')
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request.body = JSON.generate({ key: 'value' })

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts response.code  # => "200"

Faraday — Composable Middleware

Faraday wraps HTTP clients behind a uniform interface. Its real power is the middleware stack — you can add retry logic, JSON encoding, authentication, and logging as separate components.

Installation

# Gemfile
gem 'faraday'

Basic Usage

require 'faraday'

conn = Faraday.new(url: 'https://api.github.com') do |f|
  f.response :json
  f.adapter Faraday.default_adapter
end

response = conn.get('/repos/ruby/ruby')
puts response.body['description']

Middleware

Middleware pieces compose to add behavior:

conn = Faraday.new(url: 'https://api.github.com') do |f|
  f.response :json
  f.request :retry, max: 3, interval: 0.5
  f.adapter Faraday.default_adapter
end

Common middleware gems:

  • faraday-retry — automatic retries with backoff
  • faraday-follow_redirects — follow HTTP redirects
  • faraday-multipart — file uploads
  • faraday-oauth — OAuth authentication

Adapters

Faraday delegates to an underlying adapter. Swap adapters without changing your code:

# Uses Net::HTTP by default, but you can choose:
Faraday.new adapter: :patron    # libcurl bindings
Faraday.new adapter: :httpclient # Java-based HTTP client

Custom Headers

conn = Faraday.new(url: 'https://api.github.com') do |f|
  f.headers['Authorization'] = 'token YOUR_TOKEN'
  f.headers['Accept'] = 'application/vnd.github.v3+json'
end

response = conn.get('/user')

HTTParty — One-Liners

HTTParty is the quickest way to get going. Make a GET request in a single line:

require 'httparty'

response = HTTParty.get('https://api.github.com/repos/ruby/ruby')
puts response.code
puts response.parsed_response['description']

Class-Based Setup

Register a base URL and default options on a class:

require 'httparty'

class GitHub
  include HTTParty
  base_uri 'https://api.github.com'
  headers 'Accept' => 'application/vnd.github.v3+json'
end

# Now each call uses the base URI
repos = GitHub.get('/users/ruby/repos')
puts repos.first['name']

Authentication

# Basic Auth
response = HTTParty.get(
  'https://api.github.com/user',
  basic_auth: { username: 'user', password: 'token' }
)

# API Token
response = HTTParty.get(
  'https://api.github.com/user',
  headers: { 'Authorization' => 'token YOUR_TOKEN' }
)

Query Parameters

response = HTTParty.get(
  'https://api.github.com/search/code',
  query: { q: 'language:ruby', sort: 'stars' }
)

Error Handling

All three libraries raise exceptions on network failures. Handle them consistently:

require 'net/http'
require 'uri'

def fetch(url)
  uri = URI(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.open_timeout = 5
  http.read_timeout = 10

  http.start
  response = http.get(uri)
  http.finish

  case response
  when Net::HTTPSuccess
    response.body
  when Net::HTTPRedirection
    fetch(response['Location'])  # follow redirect
  else
    raise "Unexpected response: #{response.code}"
  end
rescue Net::OpenTimeout, Net::ReadTimeout => e
  puts "Timeout: #{e.message}"
  nil
rescue SocketError, Errno::ECONNREFUSED => e
  puts "Connection error: #{e.message}"
  nil
end

With Faraday, add the middleware and handle exceptions after the fact:

conn = Faraday.new(url: 'https://api.github.com') do |f|
  f.response :raise_error
  f.adapter Faraday.default_adapter
end

begin
  response = conn.get('/repos/ruby/ruby')
rescue Faraday::Error => e
  puts "Request failed: #{e.message}"
end

Retry Logic

Net::HTTP Manual Retry

def fetch_with_retry(url, max_attempts: 3)
  uri = URI(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  max_attempts.times do |attempt|
    begin
      http.open_timeout = 5
      http.read_timeout = 10
      http.start
      return http.get(uri)
    rescue Net::OpenTimeout, Net::ReadTimeout => e
      puts "Attempt #{attempt + 1} failed: #{e.message}"
      sleep 2 ** attempt  # exponential backoff
    ensure
      http.finish if http.started?
    end
  end

  raise "Failed after #{max_attempts} attempts"
end

Faraday Retry Middleware

require 'faraday'
require 'faraday/retry'

conn = Faraday.new(url: 'https://api.github.com') do |f|
  f.response :raise_error
  f.request :retry,
    max: 3,
    interval: 0.5,
    backoff_factor: 2,
    exceptions: [Faraday::Error::TimeoutError, Errno::ECONNREFUSED]
  f.adapter Faraday.default_adapter
end

Which Should You Use?

LibraryBest For
Net::HTTPScripts, one-off requests, minimal dependencies
FaradayComposable HTTP layers, testing, reusable clients
HTTPartyQuick API calls, prototyping, class-based clients

For a small script or DevOps automation, Net::HTTP is fine. When you need to add logging, retries, or authentication, Faraday pays off. Reach for HTTParty when you want to set up a class-based API client in minutes.

See Also