Skip to main content
Ruby Integration

Ruby Oil Price API

Integrate real-time oil and commodity prices into your Ruby applications. Works with Rails, Sinatra, and any Ruby framework with elegant, idiomatic code.

Quick Start

1. Install Gems

gem install httparty
gem install redis # for caching
gem install sinatra # for web apps

2. Ruby Features

  • Clean, readable Ruby idioms
  • Comprehensive error handling
  • Rails and Sinatra examples

Basic Integration

require 'net/http'
require 'json'
require 'uri'

class OilPriceAPI
  BASE_URL = 'https://api.oilpriceapi.com'
  
  def initialize(api_key)
    @api_key = api_key
    @headers = {
      'Authorization' => "Token #{api_key}",
      'Content-Type' => 'application/json'
    }
  end

  def get_brent_price
    uri = URI("#{BASE_URL}/prices")
    response = make_request(uri)
    
    if response.code == '200'
      JSON.parse(response.body)
    else
      raise "HTTP Error: #{response.code} #{response.message}"
    end
  end

  def get_commodity_health
    uri = URI("#{BASE_URL}/health")
    response = make_request(uri)
    
    if response.code == '200'
      JSON.parse(response.body)
    else
      raise "HTTP Error: #{response.code} #{response.message}"
    end
  end

  def get_wti_price
    health_data = get_commodity_health
    wti_data = health_data.dig('metrics', 'commodity_health', 'WTI_USD')
    
    if wti_data && wti_data['latest_price']
      {
        'price' => wti_data['latest_price'].to_f / 100, # Convert from cents
        'timestamp' => wti_data['last_update'],
        'currency' => 'USD',
        'unit' => 'barrel'
      }
    else
      nil
    end
  end

  def get_all_commodities
    brent_data = get_brent_price
    health_data = get_commodity_health
    
    commodities = {
      'brent_crude' => {
        'price' => brent_data.dig('data', 'price'),
        'timestamp' => brent_data.dig('data', 'timestamp'),
        'unit' => 'barrel'
      }
    }

    # Extract commodities from health endpoint
    health_commodities = health_data.dig('metrics', 'commodity_health') || {}
    
    health_commodities.each do |key, data|
      next unless data['latest_price']
      
      scale = %w[EUR_USD GBP_USD].include?(key) ? 10_000 : 100
      price = data['latest_price'].to_f / scale
      
      commodities[key.downcase] = {
        'price' => price,
        'timestamp' => data['last_update'],
        'unit' => get_unit(key)
      }
    end

    commodities
  rescue => e
    puts "Error fetching commodities: #{e.message}"
    raise e
  end

  private

  def make_request(uri)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    http.read_timeout = 10
    http.open_timeout = 10
    
    request = Net::HTTP::Get.new(uri)
    @headers.each { |key, value| request[key] = value }
    
    http.request(request)
  end

  def get_unit(commodity_key)
    units = {
      'WTI_USD' => 'barrel',
      'NATURAL_GAS_USD' => 'MMBtu',
      'GOLD_USD' => 'ounce',
      'EUR_USD' => 'USD',
      'GBP_USD' => 'USD'
    }
    units[commodity_key] || 'unit'
  end
end

# Usage example
begin
  api = OilPriceAPI.new('YOUR_API_KEY_HERE')
  
  # Get Brent crude price
  brent_data = api.get_brent_price
  puts "Brent Crude: $#{brent_data['data']['price']}/barrel"
  
  # Get WTI price
  wti_data = api.get_wti_price
  puts "WTI Crude: $#{wti_data['price']}/barrel" if wti_data
  
  # Get all commodities
  all_commodities = api.get_all_commodities
  all_commodities.each do |name, data|
    puts "#{name.upcase}: $#{data['price']} per #{data['unit']}"
  end
  
rescue => e
  puts "Error: #{e.message}"
end

HTTParty Implementation

Enhanced version using the popular HTTParty gem:

require 'httparty'
require 'json'

class OilPriceAPI
  include HTTParty
  base_uri 'https://api.oilpriceapi.com'
  
  def initialize(api_key)
    @options = {
      headers: {
        'Authorization' => "Token #{api_key}",
        'Content-Type' => 'application/json'
      },
      timeout: 10
    }
  end

  def get_brent_price
    response = self.class.get('/prices', @options)
    handle_response(response)
  end

  def get_commodity_health
    response = self.class.get('/health', @options)
    handle_response(response)
  end

  def get_wti_price
    health_data = get_commodity_health
    wti_data = health_data.dig('metrics', 'commodity_health', 'WTI_USD')
    
    return nil unless wti_data&.dig('latest_price')
    
    {
      price: wti_data['latest_price'].to_f / 100,
      timestamp: wti_data['last_update'],
      currency: 'USD',
      unit: 'barrel'
    }
  end

  def get_multiple_commodities(*commodity_keys)
    health_data = get_commodity_health
    health_commodities = health_data.dig('metrics', 'commodity_health') || {}
    
    result = {}
    commodity_keys.each do |key|
      data = health_commodities[key.upcase]
      next unless data&.dig('latest_price')
      
      scale = %w[EUR_USD GBP_USD].include?(key.upcase) ? 10_000 : 100
      
      result[key.downcase] = {
        price: data['latest_price'].to_f / scale,
        timestamp: data['last_update'],
        unit: get_unit(key.upcase)
      }
    end
    
    result
  end

  private

  def handle_response(response)
    case response.code
    when 200
      response.parsed_response
    when 401
      raise 'Unauthorized: Invalid API key'
    when 429
      raise 'Rate limit exceeded'
    when 500..599
      raise 'Server error: Please try again later'
    else
      raise "HTTP Error: #{response.code} #{response.message}"
    end
  end

  def get_unit(commodity_key)
    {
      'WTI_USD' => 'barrel',
      'BRENT_CRUDE_USD' => 'barrel',
      'NATURAL_GAS_USD' => 'MMBtu',
      'NATURAL_GAS_GBP' => 'pence/therm',
      'DUTCH_TTF_EUR' => 'EUR/MWh',
      'GASOLINE_RBOB_USD' => 'gallon',
      'HEATING_OIL_USD' => 'gallon',
      'COAL_USD' => 'ton',
      'GOLD_USD' => 'ounce',
      'EUR_USD' => 'USD',
      'GBP_USD' => 'USD'
    }[commodity_key] || 'unit'
  end
end

# Usage with HTTParty
begin
  api = OilPriceAPI.new('YOUR_API_KEY_HERE')
  
  # Get specific commodities
  commodities = api.get_multiple_commodities('wti_usd', 'natural_gas_usd', 'gold_usd')
  
  commodities.each do |name, data|
    formatted_price = data[:price].round(2)
    puts "#{name.tr('_', ' ').upcase}: $#{formatted_price} per #{data[:unit]}"
  end
  
rescue => e
  puts "Error: #{e.message}"
end

Ruby on Rails Integration

Production-ready Rails service with caching, background jobs, and price alerts:

# Gemfile
gem 'httparty'
gem 'redis'  # for caching

# app/services/oil_price_service.rb
class OilPriceService
  include HTTParty
  base_uri 'https://api.oilpriceapi.com'
  
  CACHE_TTL = 5.minutes
  
  def initialize
    @options = {
      headers: {
        'Authorization' => "Token #{Rails.application.credentials.oil_price_api_key}",
        'Content-Type' => 'application/json'
      },
      timeout: 10
    }
  end

  def current_prices
    cached_prices = Rails.cache.read('oil_prices')
    return cached_prices if cached_prices.present?
    
    prices = fetch_fresh_prices
    Rails.cache.write('oil_prices', prices, expires_in: CACHE_TTL)
    prices
  end

  def price_for_commodity(commodity)
    prices = current_prices
    prices[commodity.to_s] || prices[commodity.to_s.upcase]
  end

  def refresh_cache!
    Rails.cache.delete('oil_prices')
    current_prices
  end

  private

  def fetch_fresh_prices
    brent_response = self.class.get('/prices', @options)
    health_response = self.class.get('/health', @options)
    
    raise 'Failed to fetch price data' unless brent_response.success? && health_response.success?
    
    prices = {}
    
    # Add Brent crude from prices endpoint
    if brent_response.success?
      brent_data = brent_response.parsed_response['data']
      prices['brent_crude'] = format_price_data(
        brent_data['price'],
        brent_data['timestamp'],
        'barrel'
      )
    end
    
    # Add other commodities from health endpoint
    if health_response.success?
      health_data = health_response.parsed_response
      commodity_health = health_data.dig('metrics', 'commodity_health') || {}
      
      commodity_health.each do |key, data|
        next unless data['latest_price']
        
        scale = currency_pair?(key) ? 10_000 : 100
        price = data['latest_price'].to_f / scale
        
        prices[key.downcase] = format_price_data(
          price,
          data['last_update'],
          unit_for_commodity(key)
        )
      end
    end
    
    {
      data: prices,
      updated_at: Time.current.iso8601,
      status: 'success'
    }
  rescue => e
    Rails.logger.error "Oil Price API Error: #{e.message}"
    {
      data: {},
      updated_at: Time.current.iso8601,
      status: 'error',
      error: e.message
    }
  end

  def format_price_data(price, timestamp, unit)
    {
      price: price.to_f.round(4),
      timestamp: timestamp,
      unit: unit,
      formatted_price: "$#{price.to_f.round(2)}"
    }
  end

  def currency_pair?(commodity_key)
    %w[EUR_USD GBP_USD].include?(commodity_key)
  end

  def unit_for_commodity(key)
    {
      'WTI_USD' => 'barrel',
      'BRENT_CRUDE_USD' => 'barrel',
      'NATURAL_GAS_USD' => 'MMBtu',
      'NATURAL_GAS_GBP' => 'pence/therm',
      'DUTCH_TTF_EUR' => 'EUR/MWh',
      'GASOLINE_RBOB_USD' => 'gallon',
      'HEATING_OIL_USD' => 'gallon',
      'COAL_USD' => 'ton',
      'GOLD_USD' => 'ounce',
      'EUR_USD' => 'USD',
      'GBP_USD' => 'USD'
    }[key] || 'unit'
  end
end

# app/controllers/api/oil_prices_controller.rb
class Api::OilPricesController < ApplicationController
  before_action :set_oil_price_service
  
  def index
    prices = @service.current_prices
    render json: prices
  end

  def show
    commodity = params[:commodity]
    price_data = @service.price_for_commodity(commodity)
    
    if price_data
      render json: {
        commodity: commodity,
        data: price_data,
        status: 'success'
      }
    else
      render json: {
        error: "Commodity '#{commodity}' not found",
        status: 'not_found'
      }, status: :not_found
    end
  end

  def refresh
    prices = @service.refresh_cache!
    render json: {
      message: 'Cache refreshed successfully',
      data: prices
    }
  end

  private

  def set_oil_price_service
    @service = OilPriceService.new
  end
end

# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    resources :oil_prices, only: [:index, :show] do
      collection do
        post :refresh
      end
    end
  end
end

# app/models/price_alert.rb
class PriceAlert < ApplicationRecord
  validates :commodity, presence: true
  validates :threshold_price, presence: true, numericality: { greater_than: 0 }
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :alert_type, inclusion: { in: %w[above below] }

  scope :active, -> { where(active: true) }

  def check_alert(current_price)
    case alert_type
    when 'above'
      current_price >= threshold_price
    when 'below'
      current_price <= threshold_price
    else
      false
    end
  end
end

# app/jobs/price_alert_job.rb
class PriceAlertJob < ApplicationJob
  queue_as :default

  def perform
    service = OilPriceService.new
    prices_data = service.current_prices
    
    return unless prices_data[:status] == 'success'
    
    prices = prices_data[:data]
    
    PriceAlert.active.find_each do |alert|
      price_data = prices[alert.commodity]
      next unless price_data
      
      current_price = price_data[:price]
      
      if alert.check_alert(current_price)
        PriceAlertMailer.alert_triggered(alert, current_price).deliver_now
        alert.update(last_triggered_at: Time.current)
      end
    end
  end
end

Sinatra Microservice

Lightweight Sinatra API with Redis caching:

require 'sinatra'
require 'httparty'
require 'json'
require 'redis'

# Simple Sinatra API for oil prices
class OilPriceApp < Sinatra::Base
  configure do
    set :redis, Redis.new(url: ENV['REDIS_URL'] || 'redis://localhost:6379')
    set :cache_ttl, 300 # 5 minutes
  end

  before do
    content_type :json
    
    # CORS headers
    headers 'Access-Control-Allow-Origin' => '*'
    headers 'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS'
    headers 'Access-Control-Allow-Headers' => 'Content-Type'
  end

  options '*' do
    200
  end

  get '/health' do
    {
      status: 'healthy',
      uptime: `uptime`.strip,
      cache_info: {
        connected: settings.redis.connected?,
        keys: settings.redis.keys('oil_price:*').count
      }
    }.to_json
  end

  get '/api/prices' do
    begin
      prices = get_cached_prices
      
      if prices.nil?
        prices = fetch_fresh_prices
        cache_prices(prices) if prices
      end
      
      prices&.to_json || { error: 'Failed to fetch prices' }.to_json
    rescue => e
      logger.error "Error fetching prices: #{e.message}"
      status 500
      { error: e.message }.to_json
    end
  end

  get '/api/prices/:commodity' do
    commodity = params[:commodity].downcase
    
    begin
      prices = get_cached_prices
      prices = fetch_fresh_prices if prices.nil?
      
      if prices && prices['data'] && prices['data'][commodity]
        {
          commodity: commodity,
          data: prices['data'][commodity],
          status: 'success'
        }.to_json
      else
        status 404
        { error: "Commodity '#{commodity}' not found" }.to_json
      end
    rescue => e
      logger.error "Error fetching commodity price: #{e.message}"
      status 500
      { error: e.message }.to_json
    end
  end

  post '/api/prices/refresh' do
    begin
      settings.redis.del('oil_prices')
      prices = fetch_fresh_prices
      cache_prices(prices) if prices
      
      {
        status: 'success',
        message: 'Prices refreshed',
        data: prices
      }.to_json
    rescue => e
      logger.error "Error refreshing prices: #{e.message}"
      status 500
      { error: e.message }.to_json
    end
  end

  private

  def get_cached_prices
    cached = settings.redis.get('oil_prices')
    cached ? JSON.parse(cached) : nil
  rescue => e
    logger.warn "Cache read error: #{e.message}"
    nil
  end

  def cache_prices(prices)
    settings.redis.setex('oil_prices', settings.cache_ttl, prices.to_json)
  rescue => e
    logger.warn "Cache write error: #{e.message}"
  end

  def fetch_fresh_prices
    api_key = ENV['OIL_PRICE_API_KEY']
    raise 'API key not configured' unless api_key
    
    options = {
      headers: {
        'Authorization' => "Token #{api_key}",
        'Content-Type' => 'application/json'
      },
      timeout: 10
    }

    # Fetch from both endpoints
    brent_response = HTTParty.get('https://api.oilpriceapi.com/prices', options)
    health_response = HTTParty.get('https://api.oilpriceapi.com/health', options)
    
    unless brent_response.success? && health_response.success?
      raise 'Failed to fetch data from Oil Price API'
    end

    prices = {}
    
    # Process Brent data
    if brent_response.success?
      brent_data = brent_response.parsed_response['data']
      prices['brent_crude'] = {
        price: brent_data['price'].to_f,
        timestamp: brent_data['timestamp'],
        unit: 'barrel'
      }
    end
    
    # Process health data
    if health_response.success?
      health_data = health_response.parsed_response
      commodity_health = health_data.dig('metrics', 'commodity_health') || {}
      
      commodity_health.each do |key, data|
        next unless data['latest_price']
        
        scale = %w[EUR_USD GBP_USD].include?(key) ? 10_000 : 100
        
        prices[key.downcase] = {
          price: (data['latest_price'].to_f / scale).round(4),
          timestamp: data['last_update'],
          unit: get_unit(key)
        }
      end
    end
    
    {
      data: prices,
      updated_at: Time.now.iso8601,
      status: 'success'
    }
  end

  def get_unit(commodity_key)
    {
      'WTI_USD' => 'barrel',
      'NATURAL_GAS_USD' => 'MMBtu',
      'GOLD_USD' => 'ounce',
      'EUR_USD' => 'USD',
      'GBP_USD' => 'USD'
    }[commodity_key] || 'unit'
  end
end

# config.ru
run OilPriceApp

Available Commodities

Brent Crude Oil

North Sea Brent crude oil, the global benchmark for oil pricing representing ~60% of internationally traded crude oil.

API Field: BRENT_CRUDE_USD
Unit: barrel
Endpoint: /prices

WTI Crude Oil

West Texas Intermediate crude oil, the primary benchmark for North American oil markets and NYMEX futures.

API Field: WTI_USD
Unit: barrel
Endpoint: /health

Dubai Crude Oil

Dubai crude oil benchmark for Middle Eastern crude oil pricing and Asian markets.

API Field: DUBAI_CRUDE_USD
Unit: barrel
Endpoint: /health

Natural Gas

NYMEX Henry Hub natural gas futures, the primary pricing benchmark for North American natural gas markets.

API Field: NATURAL_GAS_USD
Unit: MMBtu
Endpoint: /health

Natural Gas (UK)

UK natural gas pricing in British pence per therm.

API Field: NATURAL_GAS_GBP
Unit: pence/therm
Endpoint: /health

Dutch TTF Gas

Dutch Title Transfer Facility (TTF) natural gas futures, the European gas pricing benchmark.

API Field: DUTCH_TTF_EUR
Unit: EUR/MWh
Endpoint: /health

Gasoline RBOB

Reformulated Blendstock for Oxygenate Blending (RBOB) gasoline futures.

API Field: GASOLINE_RBOB_USD
Unit: gallon
Endpoint: /health

Heating Oil

No. 2 heating oil futures, a key refined petroleum product for heating and industrial use.

API Field: HEATING_OIL_USD
Unit: gallon
Endpoint: /health

Coal

Coal futures pricing for thermal coal used in power generation.

API Field: COAL_USD
Unit: ton
Endpoint: /health

Gold

Gold futures pricing, a key precious metal and safe-haven asset.

API Field: GOLD_USD
Unit: ounce
Endpoint: /health

EUR/USD

Euro to US Dollar exchange rate, the most traded currency pair globally.

API Field: EUR_USD
Unit: USD
Endpoint: /health

GBP/USD

British Pound to US Dollar exchange rate, also known as "Cable".

API Field: GBP_USD
Unit: USD
Endpoint: /health

Ruby Best Practices

Error Handling

  • Use begin/rescue blocks for API calls
  • Implement custom exception classes
  • Log errors with proper context

Performance

  • Use Rails.cache or Redis for caching
  • Implement background job processing
  • Set appropriate HTTP timeouts

Ready to Start Building?

Get your free API key and start integrating real-time commodity prices into your Ruby applications today.