RoR Consume External APIs

Your Rails app can call other services' APIs to fetch data, send messages, process payments, and more. Consuming an external API means your app sends an HTTP request to another server and processes the response. Ruby has built-in and third-party tools that make this easy.

How API Consumption Works

Your Rails App                   External API Server
     |                                  |
     | GET https://api.weather.com/     |
     |   /current?city=London           |
     | ─────────────────────────────►   |
     |                                  |
     |   Returns JSON:                  |
     |  { "temp": 18, "desc": "Cloudy" }|
     | ◄──────────────────────────────  |
     |                                  |
Your app reads the JSON
and uses the data

The Net::HTTP Library (Built-in Ruby)

Ruby includes Net::HTTP with no gem required:

require "net/http"
require "json"

uri = URI("https://api.exchangerate.host/latest?base=USD")
response = Net::HTTP.get(uri)
data = JSON.parse(response)

data["rates"]["EUR"]   # => 0.93
data["rates"]["GBP"]   # => 0.79

The HTTParty Gem (Recommended)

HTTParty makes HTTP requests cleaner with less boilerplate:

gem "httparty"
bundle install
require "httparty"

response = HTTParty.get(
  "https://api.openweathermap.org/data/2.5/weather",
  query: {
    q:     "London",
    appid: ENV["OPENWEATHER_API_KEY"],
    units: "metric"
  }
)

if response.success?
  data = response.parsed_response
  temp = data["main"]["temp"]       # 18.4
  desc = data["weather"][0]["desc"] # "light rain"
else
  Rails.logger.error "Weather API failed: #{response.code}"
end

Wrap External APIs in a Service Object

Never call external APIs directly from controllers. Wrap them in a service object so the logic stays organized and testable:

app/services/weather_service.rb

class WeatherService
  BASE_URL = "https://api.openweathermap.org/data/2.5/weather"

  def initialize(city)
    @city = city
  end

  def current
    response = HTTParty.get(BASE_URL, query: query_params)
    return nil unless response.success?

    parse_weather(response.parsed_response)
  end

  private

  def query_params
    {
      q:     @city,
      appid: ENV["OPENWEATHER_API_KEY"],
      units: "metric"
    }
  end

  def parse_weather(data)
    {
      city:        data["name"],
      temperature: data["main"]["temp"],
      feels_like:  data["main"]["feels_like"],
      description: data["weather"][0]["description"],
      humidity:    data["main"]["humidity"]
    }
  end
end

Use it in a controller:

class WeatherController < ApplicationController
  def show
    @weather = WeatherService.new(params[:city]).current
    if @weather.nil?
      flash[:alert] = "Could not fetch weather data."
      redirect_to root_path
    end
  end
end

Handling Authentication with External APIs

Most APIs require authentication. Common methods:

API Key in query string:
  HTTParty.get(url, query: { api_key: ENV["API_KEY"] })

API Key in header:
  HTTParty.get(url, headers: { "X-API-Key" => ENV["API_KEY"] })

Bearer Token:
  HTTParty.get(url, headers: { "Authorization" => "Bearer #{ENV["TOKEN"]}" })

Basic Auth:
  HTTParty.get(url, basic_auth: { username: "user", password: "pass" })

POST Requests to External APIs

Send JSON data to an external API:

response = HTTParty.post(
  "https://api.sendgrid.com/v3/mail/send",
  headers: {
    "Authorization" => "Bearer #{ENV["SENDGRID_API_KEY"]}",
    "Content-Type"  => "application/json"
  },
  body: {
    personalizations: [{ to: [{ email: "user@example.com" }] }],
    from:    { email: "noreply@myapp.com" },
    subject: "Welcome!",
    content: [{ type: "text/plain", value: "Thanks for signing up." }]
  }.to_json
)

response.code    # 202 = accepted

Caching API Responses

External API calls are slow and often rate-limited. Cache responses to avoid redundant calls:

def current_weather(city)
  Rails.cache.fetch("weather_#{city}", expires_in: 30.minutes) do
    WeatherService.new(city).current
  end
end

The first call hits the API. Subsequent calls within 30 minutes return the cached result instantly.

Error Handling

def fetch_data(url)
  response = HTTParty.get(url, timeout: 5)

  case response.code
  when 200      then response.parsed_response
  when 401      then raise "API authentication failed"
  when 404      then nil
  when 429      then raise "Rate limit exceeded — try again later"
  when 500..599 then raise "External API server error"
  end

rescue Net::OpenTimeout, Net::ReadTimeout
  Rails.logger.error "API request timed out: #{url}"
  nil
rescue HTTParty::Error => e
  Rails.logger.error "HTTParty error: #{e.message}"
  nil
end

Faraday — A Flexible HTTP Client

For apps that make many API calls, Faraday offers middleware, retries, and connection pooling:

gem "faraday"
bundle install

conn = Faraday.new(url: "https://api.github.com") do |f|
  f.request :json
  f.response :json
  f.adapter Faraday.default_adapter
  f.headers["Authorization"] = "Bearer #{ENV["GITHUB_TOKEN"]}"
end

response = conn.get("/repos/rails/rails")
response.body["stargazers_count"]   # => GitHub stars count

Common External APIs Used with Rails

ServicePurposeGem Available
StripePayment processingstripe
TwilioSMS and phone callstwilio-ruby
SendGridTransactional emailsendgrid-ruby
Google MapsGeocoding and mapsgeocoder
OpenAIAI text generationruby-openai
GitHubRepository dataoctokit

Always store API keys in environment variables or Rails credentials. Never commit them to version control. Wrap API calls in service objects, handle timeouts and errors gracefully, and cache responses where possible to keep your app fast and reliable.

Leave a Comment

Your email address will not be published. Required fields are marked *