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.
WTI Crude Oil
West Texas Intermediate crude oil, the primary benchmark for North American oil markets and NYMEX futures.
Dubai Crude Oil
Dubai crude oil benchmark for Middle Eastern crude oil pricing and Asian markets.
Natural Gas
NYMEX Henry Hub natural gas futures, the primary pricing benchmark for North American natural gas markets.
Natural Gas (UK)
UK natural gas pricing in British pence per therm.
Dutch TTF Gas
Dutch Title Transfer Facility (TTF) natural gas futures, the European gas pricing benchmark.
Gasoline RBOB
Reformulated Blendstock for Oxygenate Blending (RBOB) gasoline futures.
Heating Oil
No. 2 heating oil futures, a key refined petroleum product for heating and industrial use.
Coal
Coal futures pricing for thermal coal used in power generation.
Gold
Gold futures pricing, a key precious metal and safe-haven asset.
EUR/USD
Euro to US Dollar exchange rate, the most traded currency pair globally.
GBP/USD
British Pound to US Dollar exchange rate, also known as "Cable".
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.