# Consuming APIs with Ruby

Ruby provides excellent tools for API consumption through its built-in libraries and gems. The `net/http` standard library handles basic HTTP operations, while gems like `httparty` and `faraday` offer more convenient interfaces for complex API interactions.

This tutorial covers consuming REST APIs with Ruby, including authentication, error handling, and real-world examples.

## Prerequisites

Ruby 2.7 or later includes the modern HTTP client improvements covered in this guide.

Basic familiarity with Ruby classes, modules, and exception handling will help you apply the API consumption patterns demonstrated here.

## Ruby's API consumption approach

Ruby treats HTTP requests as objects with rich APIs for configuration and response handling. The standard library provides low-level control, while popular gems offer high-level convenience methods and automatic JSON parsing.

Core Ruby HTTP tools:

- **Net::HTTP**: Built-in HTTP client library
- **URI**: URL parsing and manipulation  
- **JSON**: Response parsing and generation
- **HTTParty**: Popular gem for simplified API consumption

Create a working directory:

```command
mkdir ruby-api-demo && cd ruby-api-demo
```

## Making your first API call

Ruby's `Net::HTTP` provides the foundation for API consumption. The library offers both simple one-line methods and detailed request configuration for complex scenarios.

For your first example, you'll call the Random User Generator API to fetch sample user data:

```ruby
[label basic_api_call.rb]
require 'net/http'
require 'json'

def fetch_random_user
  uri = URI('https://randomuser.me/api/')
  response = Net::HTTP.get_response(uri)
  
  puts "Status: #{response.code} #{response.message}"
  
  if response.code == '200'
    data = JSON.parse(response.body)
    user = data['results'].first
    name = user['name']
    puts "Generated user: #{name['first']} #{name['last']}"
    puts "Email: #{user['email']}"
    puts "Location: #{user['location']['city']}, #{user['location']['country']}"
  else
    puts "Request failed with status #{response.code}"
  end
end

fetch_random_user
```

The `Net::HTTP.get_response` method returns a response object containing both status information and body content. Ruby's `JSON.parse` converts the response string into native Ruby objects for easy manipulation.

Run the basic API example:

```command
ruby basic_api_call.rb
```

```text
[output]
Status: 200 OK
Generated user: Emma Johnson
Email: emma.johnson@example.com
Location: Manchester, United Kingdom
```

This demonstrates Ruby's straightforward approach to HTTP requests where response data becomes immediately accessible through standard Ruby hash operations.

While the basic example works perfectly for simple scenarios, production applications often need deeper insights into request and response details for debugging and monitoring purposes.


## Understanding requests and responses

Ruby's HTTP objects provide comprehensive access to request and response data. Understanding these objects helps you debug API calls and handle different response scenarios effectively.

Let's enhance our existing `basic_api_call.rb` to add detailed request and response inspection:

```ruby
[label basic_api_call.rb]
require 'net/http'
require 'json'

def fetch_random_user
  uri = URI('https://randomuser.me/api/')
  [highlight]
  # Create HTTP object for detailed control
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  
  # Create request object with custom headers
  request = Net::HTTP::Get.new(uri)
  request['User-Agent'] = 'Ruby-API-Demo/1.0'
  
  puts "=== Request Details ==="
  puts "Method: #{request.method}"
  puts "URI: #{uri}"
  puts "Headers: #{request.to_hash}"
  
  # Make the request
  response = http.request(request)
  [/highlight]
  
  puts "Status: #{response.code} #{response.message}"
  [highlight]
  puts "Content-Type: #{response['content-type']}"
  puts "Body length: #{response.body.length} characters"
  [/highlight]
  
  if response.code == '200'
    data = JSON.parse(response.body)
    user = data['results'].first
    name = user['name']
    puts "Generated user: #{name['first']} #{name['last']}"
    puts "Email: #{user['email']}"
    puts "Location: #{user['location']['city']}, #{user['location']['country']}"
  else
    puts "Request failed with status #{response.code}"
  end
end

fetch_random_user
```

The first highlighted section replaces the simple `get_response` call with separate HTTP and request objects. This provides access to request headers and debugging information before the request is sent. The second section adds response introspection to see content type and body size.

This manual approach provides complete visibility into the HTTP conversation, useful for debugging API issues and understanding response formats.

Run the enhanced version:

```command
ruby basic_api_call.rb
```

```text
[output]
=== Request Details ===
Method: GET
URI: https://randomuser.me/api/
Headers: {"accept-encoding" => ["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"], "accept" => ["*/*"], "user-agent" => ["Ruby-API-Demo/1.0"], "host" => ["randomuser.me"]}
Status: 200 OK
Content-Type: application/json; charset=utf-8
Body length: 1187 characters
Generated user: Fradique da Paz
Email: fradique.dapaz@example.com
Location: Porto Seguro, Brazil
```
The output reveals exactly what your Ruby application sends and receives. The request headers show Ruby automatically adds compression support (`accept-encoding`) and sets a host header, while preserving your custom `user-agent`. The response details confirm the API returns JSON data (`application/json`) with a specific character encoding, helping you understand the data format before parsing.

This approach separates request creation from execution, providing access to detailed request and response properties while maintaining the same core functionality. The `http.use_ssl = true` setting enables HTTPS for secure API communication.

However, creating HTTP objects and request configurations manually becomes verbose for applications making frequent API calls. This is where higher-level gems like HTTParty shine by simplifying common patterns.


## Simplified API calls with HTTParty

While `Net::HTTP` provides complete control, the HTTParty gem offers a more convenient interface for common API consumption patterns. HTTParty automatically handles JSON parsing, SSL, and provides a cleaner syntax.

First install HTTParty:

```command
gem install httparty
```

Let's refactor our Random User example using HTTParty to see the difference:

```ruby
[label httparty_user_api.rb]
require 'httparty'

class RandomUserAPI
  include HTTParty
  base_uri 'https://randomuser.me'
  
  def fetch_user
    response = self.class.get('/api/')
    
    if response.success?
      user = response.parsed_response['results'].first
      name = user['name']
      puts "Generated user: #{name['first']} #{name['last']}"
      puts "Email: #{user['email']}"
      puts "Location: #{user['location']['city']}, #{user['location']['country']}"
    else
      puts "Error: #{response.code} - #{response.message}"
    end
  end
end

api = RandomUserAPI.new
api.fetch_user
```

HTTParty's class-based approach encapsulates the API endpoint configuration. The `include HTTParty` statement adds HTTP methods to your class, while `base_uri` sets the common URL prefix. The `parsed_response` method automatically converts JSON to Ruby objects.

Run the HTTParty version:

```command
ruby httparty_user_api.rb
```

```text
[output]
Generated user: Leo Tanner
Email: leo.tanner@example.com
Location: Lieksa, Finland
```

This compact example demonstrates HTTParty's power - the core API call reduces to just `self.class.get('/api/')` while automatically handling JSON parsing, SSL configuration, and providing convenient response methods like `success?`.

Now let's add response inspection to see what HTTParty provides access to:

```ruby
[label httparty_user_api.rb]
require 'httparty'

class RandomUserAPI
  include HTTParty
  base_uri 'https://randomuser.me'
  
  def fetch_user
    response = self.class.get('/api/')
    
    [highlight]
    puts "Status: #{response.code} #{response.message}"
    puts "Headers: #{response.headers.to_hash}"
    [/highlight]
    
    if response.success?
      user = response.parsed_response['results'].first
      name = user['name']
      puts "Generated user: #{name['first']} #{name['last']}"
      puts "Email: #{user['email']}"
      puts "Location: #{user['location']['city']}, #{user['location']['country']}"
    else
      puts "Error: #{response.code} - #{response.message}"
    end
  end
end

api = RandomUserAPI.new
api.fetch_user
```

```text
[output]
Status: 200 OK
Headers: {"date" => ["Wed, 27 Aug 2025 08:42:39 GMT"], "content-type" => ["application/json; charset=utf-8"], "transfer-encoding" => ..."]}
Generated user: Hermelinda Rosado
Email: hermelinda.rosado@example.com
Location: Santa Cruz de Rosales, Mexico
```

The highlighted lines show HTTParty provides the same response introspection as Net::HTTP but with simpler syntax. Compare this to the manual Net::HTTP approach: no HTTP object creation, no SSL configuration, no manual JSON parsing, and automatic response format detection. HTTParty eliminates the boilerplate while preserving access to response details when needed.

Most production APIs require authentication to access their endpoints. Ruby's HTTP libraries provide flexible approaches to handle various authentication schemes, from simple API keys to complex token-based systems.


## Final thoughts

Ruby's HTTP ecosystem balances simplicity with power. Use `Net::HTTP` for maximum control and zero dependencies, or HTTParty for rapid development with built-in conveniences.

Key patterns for production API consumption include proper authentication management, error handling with retries, and response processing. Ruby's object-oriented design makes these patterns natural to implement and maintain.

Start with simple implementations and add complexity as your needs grow - Ruby's flexible architecture supports both approaches equally well.

For advanced HTTP client features and performance optimization, explore the comprehensive documentation:

- [Net::HTTP documentation](https://ruby-doc.org/stdlib-3.1.0/libdoc/net/http/rdoc/Net/HTTP.html)
- [HTTParty gem documentation](https://github.com/jnunemaker/httparty)
