Skip to main content
C# Integration

C# Oil Price API

Integrate real-time oil and commodity prices into your .NET applications. Enterprise-ready with ASP.NET Core, Entity Framework, dependency injection, and async patterns.

Quick Start

1. Install NuGet Packages

# Basic HTTP client
dotnet add package System.Text.Json
# ASP.NET Core
dotnet add package Microsoft.AspNetCore.App
# Entity Framework Core
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

2. C# Features

  • Modern C# with nullable reference types
  • Async/await throughout
  • Built-in dependency injection

Basic Integration

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

public class OilPriceAPI
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private const string BaseUrl = "https://api.oilpriceapi.com";
    
    public OilPriceAPI(string apiKey)
    {
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Token {apiKey}");
        _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
        _httpClient.Timeout = TimeSpan.FromSeconds(10);
    }
    
    public async Task<PriceResponse> GetBrentPriceAsync()
    {
        var response = await _httpClient.GetAsync($"{BaseUrl}/prices");
        response.EnsureSuccessStatusCode();
        
        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<PriceResponse>(json);
    }
    
    public async Task<HealthResponse> GetCommodityHealthAsync()
    {
        var response = await _httpClient.GetAsync($"{BaseUrl}/health");
        response.EnsureSuccessStatusCode();
        
        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<HealthResponse>(json);
    }
    
    public async Task<CommodityPrice?> GetWTIPriceAsync()
    {
        try
        {
            var healthData = await GetCommodityHealthAsync();
            
            if (healthData?.Metrics?.CommodityHealth?.TryGetValue("WTI_USD", out var wtiData) == true)
            {
                return new CommodityPrice
                {
                    Price = wtiData.LatestPrice / 100.0, // Convert from cents
                    Timestamp = wtiData.LastUpdate,
                    Currency = "USD",
                    Unit = "barrel"
                };
            }
            
            return null;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting WTI price: {ex.Message}");
            return null;
        }
    }
    
    public async Task<Dictionary<string, CommodityPrice>> GetAllCommoditiesAsync()
    {
        var commodities = new Dictionary<string, CommodityPrice>();
        
        try
        {
            // Run both requests concurrently
            var brentTask = GetBrentPriceAsync();
            var healthTask = GetCommodityHealthAsync();
            
            await Task.WhenAll(brentTask, healthTask);
            
            // Process Brent data
            var brentResponse = await brentTask;
            if (brentResponse?.Data != null)
            {
                commodities["brent_crude"] = new CommodityPrice
                {
                    Price = brentResponse.Data.Price,
                    Timestamp = brentResponse.Data.Timestamp,
                    Currency = brentResponse.Data.Currency,
                    Unit = "barrel"
                };
            }
            
            // Process health data
            var healthResponse = await healthTask;
            if (healthResponse?.Metrics?.CommodityHealth != null)
            {
                foreach (var kvp in healthResponse.Metrics.CommodityHealth)
                {
                    var key = kvp.Key;
                    var data = kvp.Value;
                    
                    if (data.LatestPrice.HasValue)
                    {
                        var scale = IsCurrencyPair(key) ? 10000.0 : 100.0;
                        var price = data.LatestPrice.Value / scale;
                        
                        commodities[key.ToLower()] = new CommodityPrice
                        {
                            Price = price,
                            Timestamp = data.LastUpdate,
                            Currency = "USD",
                            Unit = GetUnit(key)
                        };
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting commodities: {ex.Message}");
        }
        
        return commodities;
    }
    
    private static bool IsCurrencyPair(string commodityKey) =>
        commodityKey is "EUR_USD" or "GBP_USD";
    
    private static string GetUnit(string commodityKey) => commodityKey switch
    {
        "WTI_USD" => "barrel",
        "NATURAL_GAS_USD" => "MMBtu",
        "GOLD_USD" => "ounce",
        "EUR_USD" or "GBP_USD" => "USD",
        _ => "unit"
    };
    
    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}

// Data Transfer Objects
public class PriceResponse
{
    public string Status { get; set; }
    public PriceData Data { get; set; }
}

public class PriceData
{
    public double Price { get; set; }
    public string Timestamp { get; set; }
    public string Currency { get; set; }
    public string Unit { get; set; }
}

public class HealthResponse
{
    public string Status { get; set; }
    public Metrics Metrics { get; set; }
}

public class Metrics
{
    [JsonPropertyName("commodity_health")]
    public Dictionary<string, CommodityHealthData> CommodityHealth { get; set; }
}

public class CommodityHealthData
{
    [JsonPropertyName("latest_price")]
    public double? LatestPrice { get; set; }
    
    [JsonPropertyName("last_update")]
    public string LastUpdate { get; set; }
}

public class CommodityPrice
{
    public double Price { get; set; }
    public string Timestamp { get; set; }
    public string Currency { get; set; }
    public string Unit { get; set; }
    
    public override string ToString() =>
        $"{Price:F2} {Currency} per {Unit} (updated: {Timestamp})";
}

// Usage example
class Program
{
    static async Task Main(string[] args)
    {
        using var api = new OilPriceAPI("YOUR_API_KEY_HERE");
        
        try
        {
            Console.WriteLine("=== Live Oil Prices ===");
            
            // Get Brent crude price
            var brentData = await api.GetBrentPriceAsync();
            if (brentData?.Data != null)
            {
                Console.WriteLine($"Brent Crude: {brentData.Data.Price}/barrel");
            }
            
            // Get WTI price
            var wtiPrice = await api.GetWTIPriceAsync();
            if (wtiPrice != null)
            {
                Console.WriteLine($"WTI Crude: {wtiPrice}");
            }
            
            // Get all commodities
            var allCommodities = await api.GetAllCommoditiesAsync();
            foreach (var (name, price) in allCommodities)
            {
                Console.WriteLine($"{name.ToUpper()}: {price}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

ASP.NET Core Web API

Production-ready ASP.NET Core service with caching, dependency injection, and background services:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using System.Text.Json;

// Program.cs (ASP.NET Core 6+)
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient();

// Configure options
builder.Services.Configure<OilPriceApiOptions>(
    builder.Configuration.GetSection("OilPriceApi"));

// Register services
builder.Services.AddScoped<IOilPriceService, OilPriceService>();
builder.Services.AddHostedService<PriceUpdateService>();

// Add CORS
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

var app = builder.Build();

app.UseCors();
app.UseRouting();
app.MapControllers();

app.Run();

// Configuration model
public class OilPriceApiOptions
{
    public string ApiKey { get; set; } = string.Empty;
    public string BaseUrl { get; set; } = "https://api.oilpriceapi.com";
    public int TimeoutSeconds { get; set; } = 10;
    public int CacheMinutes { get; set; } = 5;
}

// Service interface
public interface IOilPriceService
{
    Task<ApiResult<PriceResponse>> GetBrentPriceAsync();
    Task<ApiResult<HealthResponse>> GetCommodityHealthAsync();
    Task<ApiResult<Dictionary<string, CommodityPrice>>> GetAllCommoditiesAsync();
    Task<ApiResult<CommodityPrice>> GetCommodityPriceAsync(string commodity);
}

// Service implementation
public class OilPriceService : IOilPriceService
{
    private readonly HttpClient _httpClient;
    private readonly IMemoryCache _cache;
    private readonly OilPriceApiOptions _options;
    private readonly ILogger<OilPriceService> _logger;
    
    public OilPriceService(
        HttpClient httpClient,
        IMemoryCache cache,
        IOptions<OilPriceApiOptions> options,
        ILogger<OilPriceService> logger)
    {
        _httpClient = httpClient;
        _cache = cache;
        _options = options.Value;
        _logger = logger;
        
        ConfigureHttpClient();
    }
    
    private void ConfigureHttpClient()
    {
        _httpClient.BaseAddress = new Uri(_options.BaseUrl);
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Token {_options.ApiKey}");
        _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
        _httpClient.Timeout = TimeSpan.FromSeconds(_options.TimeoutSeconds);
    }
    
    public async Task<ApiResult<PriceResponse>> GetBrentPriceAsync()
    {
        const string cacheKey = "brent_price";
        
        if (_cache.TryGetValue(cacheKey, out PriceResponse cachedPrice))
        {
            return ApiResult<PriceResponse>.Success(cachedPrice);
        }
        
        try
        {
            var response = await _httpClient.GetAsync("/prices");
            
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var priceResponse = JsonSerializer.Deserialize<PriceResponse>(json);
                
                // Cache the result
                _cache.Set(cacheKey, priceResponse, TimeSpan.FromMinutes(_options.CacheMinutes));
                
                return ApiResult<PriceResponse>.Success(priceResponse);
            }
            
            _logger.LogError("Failed to fetch Brent price: {StatusCode}", response.StatusCode);
            return ApiResult<PriceResponse>.Failure($"HTTP {response.StatusCode}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching Brent price");
            return ApiResult<PriceResponse>.Failure(ex.Message);
        }
    }
    
    public async Task<ApiResult<HealthResponse>> GetCommodityHealthAsync()
    {
        const string cacheKey = "commodity_health";
        
        if (_cache.TryGetValue(cacheKey, out HealthResponse cachedHealth))
        {
            return ApiResult<HealthResponse>.Success(cachedHealth);
        }
        
        try
        {
            var response = await _httpClient.GetAsync("/health");
            
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var healthResponse = JsonSerializer.Deserialize<HealthResponse>(json);
                
                // Cache the result
                _cache.Set(cacheKey, healthResponse, TimeSpan.FromMinutes(_options.CacheMinutes));
                
                return ApiResult<HealthResponse>.Success(healthResponse);
            }
            
            _logger.LogError("Failed to fetch commodity health: {StatusCode}", response.StatusCode);
            return ApiResult<HealthResponse>.Failure($"HTTP {response.StatusCode}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching commodity health");
            return ApiResult<HealthResponse>.Failure(ex.Message);
        }
    }
    
    public async Task<ApiResult<Dictionary<string, CommodityPrice>>> GetAllCommoditiesAsync()
    {
        const string cacheKey = "all_commodities";
        
        if (_cache.TryGetValue(cacheKey, out Dictionary<string, CommodityPrice> cachedCommodities))
        {
            return ApiResult<Dictionary<string, CommodityPrice>>.Success(cachedCommodities);
        }
        
        try
        {
            var brentTask = GetBrentPriceAsync();
            var healthTask = GetCommodityHealthAsync();
            
            await Task.WhenAll(brentTask, healthTask);
            
            var commodities = new Dictionary<string, CommodityPrice>();
            
            // Process Brent data
            var brentResult = await brentTask;
            if (brentResult.IsSuccess && brentResult.Data?.Data != null)
            {
                commodities["brent_crude"] = new CommodityPrice
                {
                    Price = brentResult.Data.Data.Price,
                    Timestamp = brentResult.Data.Data.Timestamp,
                    Currency = brentResult.Data.Data.Currency,
                    Unit = "barrel"
                };
            }
            
            // Process health data
            var healthResult = await healthTask;
            if (healthResult.IsSuccess && healthResult.Data?.Metrics?.CommodityHealth != null)
            {
                foreach (var (key, data) in healthResult.Data.Metrics.CommodityHealth)
                {
                    if (data.LatestPrice.HasValue)
                    {
                        var scale = IsCurrencyPair(key) ? 10000.0 : 100.0;
                        var price = data.LatestPrice.Value / scale;
                        
                        commodities[key.ToLower()] = new CommodityPrice
                        {
                            Price = price,
                            Timestamp = data.LastUpdate,
                            Currency = "USD",
                            Unit = GetUnit(key)
                        };
                    }
                }
            }
            
            // Cache the result
            _cache.Set(cacheKey, commodities, TimeSpan.FromMinutes(_options.CacheMinutes));
            
            return ApiResult<Dictionary<string, CommodityPrice>>.Success(commodities);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching all commodities");
            return ApiResult<Dictionary<string, CommodityPrice>>.Failure(ex.Message);
        }
    }
    
    public async Task<ApiResult<CommodityPrice>> GetCommodityPriceAsync(string commodity)
    {
        var allCommoditiesResult = await GetAllCommoditiesAsync();
        
        if (!allCommoditiesResult.IsSuccess)
        {
            return ApiResult<CommodityPrice>.Failure(allCommoditiesResult.ErrorMessage);
        }
        
        var commodityKey = commodity.ToLower();
        if (allCommoditiesResult.Data.TryGetValue(commodityKey, out var price))
        {
            return ApiResult<CommodityPrice>.Success(price);
        }
        
        return ApiResult<CommodityPrice>.Failure($"Commodity '{commodity}' not found");
    }
    
    private static bool IsCurrencyPair(string commodityKey) =>
        commodityKey is "EUR_USD" or "GBP_USD";
    
    private static string GetUnit(string commodityKey) => commodityKey switch
    {
        "WTI_USD" => "barrel",
        "NATURAL_GAS_USD" => "MMBtu",
        "GOLD_USD" => "ounce",
        "EUR_USD" or "GBP_USD" => "USD",
        _ => "unit"
    };
}

// API Controller
[ApiController]
[Route("api/[controller]")]
public class OilPricesController : ControllerBase
{
    private readonly IOilPriceService _oilPriceService;
    private readonly ILogger<OilPricesController> _logger;
    
    public OilPricesController(IOilPriceService oilPriceService, ILogger<OilPricesController> logger)
    {
        _oilPriceService = oilPriceService;
        _logger = logger;
    }
    
    [HttpGet]
    public async Task<ActionResult<Dictionary<string, CommodityPrice>>> GetAllPrices()
    {
        var result = await _oilPriceService.GetAllCommoditiesAsync();
        
        if (result.IsSuccess)
        {
            return Ok(new
            {
                status = "success",
                data = result.Data,
                timestamp = DateTime.UtcNow
            });
        }
        
        _logger.LogError("Failed to get all prices: {Error}", result.ErrorMessage);
        return StatusCode(500, new { status = "error", message = result.ErrorMessage });
    }
    
    [HttpGet("{commodity}")]
    public async Task<ActionResult<CommodityPrice>> GetCommodityPrice(string commodity)
    {
        var result = await _oilPriceService.GetCommodityPriceAsync(commodity);
        
        if (result.IsSuccess)
        {
            return Ok(new
            {
                status = "success",
                commodity = commodity,
                data = result.Data,
                timestamp = DateTime.UtcNow
            });
        }
        
        if (result.ErrorMessage.Contains("not found"))
        {
            return NotFound(new { status = "error", message = result.ErrorMessage });
        }
        
        _logger.LogError("Failed to get {Commodity} price: {Error}", commodity, result.ErrorMessage);
        return StatusCode(500, new { status = "error", message = result.ErrorMessage });
    }
    
    [HttpGet("brent")]
    public async Task<ActionResult<PriceResponse>> GetBrentPrice()
    {
        var result = await _oilPriceService.GetBrentPriceAsync();
        
        if (result.IsSuccess)
        {
            return Ok(result.Data);
        }
        
        _logger.LogError("Failed to get Brent price: {Error}", result.ErrorMessage);
        return StatusCode(500, new { status = "error", message = result.ErrorMessage });
    }
}

// Background service for periodic updates
public class PriceUpdateService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<PriceUpdateService> _logger;
    
    public PriceUpdateService(IServiceProvider serviceProvider, ILogger<PriceUpdateService> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using var scope = _serviceProvider.CreateScope();
                var oilPriceService = scope.ServiceProvider.GetRequiredService<IOilPriceService>();
                
                _logger.LogInformation("Refreshing commodity prices...");
                var result = await oilPriceService.GetAllCommoditiesAsync();
                
                if (result.IsSuccess)
                {
                    _logger.LogInformation("Successfully refreshed {Count} commodities", result.Data.Count);
                }
                else
                {
                    _logger.LogError("Failed to refresh prices: {Error}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in price update service");
            }
            
            // Wait 5 minutes before next update
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

// Result wrapper
public class ApiResult<T>
{
    public bool IsSuccess { get; init; }
    public T Data { get; init; }
    public string ErrorMessage { get; init; } = string.Empty;
    
    public static ApiResult<T> Success(T data) =>
        new() { IsSuccess = true, Data = data };
    
    public static ApiResult<T> Failure(string error) =>
        new() { IsSuccess = false, ErrorMessage = error };
}

// appsettings.json
/*
{
  "OilPriceApi": {
    "ApiKey": "YOUR_API_KEY_HERE",
    "BaseUrl": "https://api.oilpriceapi.com",
    "TimeoutSeconds": 10,
    "CacheMinutes": 5
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}
*/

Entity Framework Integration

Advanced implementation with Entity Framework for price history and alerts:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

// Entity models for storing price history
public class PriceHistory
{
    public int Id { get; set; }
    
    [Required]
    [MaxLength(50)]
    public string Commodity { get; set; } = string.Empty;
    
    public double Price { get; set; }
    
    [MaxLength(10)]
    public string Currency { get; set; } = "USD";
    
    [MaxLength(20)]
    public string Unit { get; set; } = string.Empty;
    
    public DateTime Timestamp { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

public class PriceAlert
{
    public int Id { get; set; }
    
    [Required]
    [MaxLength(50)]
    public string Commodity { get; set; } = string.Empty;
    
    public double ThresholdPrice { get; set; }
    
    [Required]
    [MaxLength(10)]
    public string AlertType { get; set; } = string.Empty; // "above" or "below"
    
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;
    
    public bool IsActive { get; set; } = true;
    public DateTime? LastTriggered { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

// DbContext
public class OilPriceDbContext : DbContext
{
    public OilPriceDbContext(DbContextOptions<OilPriceDbContext> options) : base(options) { }
    
    public DbSet<PriceHistory> PriceHistory { get; set; }
    public DbSet<PriceAlert> PriceAlerts { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PriceHistory>(entity =>
        {
            entity.HasIndex(e => new { e.Commodity, e.Timestamp });
            entity.Property(e => e.Price).HasPrecision(18, 4);
        });
        
        modelBuilder.Entity<PriceAlert>(entity =>
        {
            entity.HasIndex(e => new { e.Commodity, e.IsActive });
            entity.Property(e => e.ThresholdPrice).HasPrecision(18, 4);
        });
    }
}

// Enhanced service with Entity Framework
public interface IPriceHistoryService
{
    Task SavePriceHistoryAsync(Dictionary<string, CommodityPrice> prices);
    Task<List<PriceHistory>> GetPriceHistoryAsync(string commodity, DateTime from, DateTime to);
    Task<List<PriceAlert>> CheckAlertsAsync(Dictionary<string, CommodityPrice> currentPrices);
}

public class PriceHistoryService : IPriceHistoryService
{
    private readonly OilPriceDbContext _context;
    private readonly ILogger<PriceHistoryService> _logger;
    
    public PriceHistoryService(OilPriceDbContext context, ILogger<PriceHistoryService> logger)
    {
        _context = context;
        _logger = logger;
    }
    
    public async Task SavePriceHistoryAsync(Dictionary<string, CommodityPrice> prices)
    {
        try
        {
            var priceHistories = prices.Select(kvp => new PriceHistory
            {
                Commodity = kvp.Key,
                Price = kvp.Value.Price,
                Currency = kvp.Value.Currency,
                Unit = kvp.Value.Unit,
                Timestamp = DateTime.TryParse(kvp.Value.Timestamp, out var ts) ? ts : DateTime.UtcNow
            }).ToList();
            
            await _context.PriceHistory.AddRangeAsync(priceHistories);
            await _context.SaveChangesAsync();
            
            _logger.LogInformation("Saved {Count} price history records", priceHistories.Count);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error saving price history");
        }
    }
    
    public async Task<List<PriceHistory>> GetPriceHistoryAsync(string commodity, DateTime from, DateTime to)
    {
        return await _context.PriceHistory
            .Where(p => p.Commodity == commodity.ToLower() && p.Timestamp >= from && p.Timestamp <= to)
            .OrderBy(p => p.Timestamp)
            .ToListAsync();
    }
    
    public async Task<List<PriceAlert>> CheckAlertsAsync(Dictionary<string, CommodityPrice> currentPrices)
    {
        var triggeredAlerts = new List<PriceAlert>();
        
        try
        {
            var activeAlerts = await _context.PriceAlerts
                .Where(a => a.IsActive)
                .ToListAsync();
            
            foreach (var alert in activeAlerts)
            {
                if (currentPrices.TryGetValue(alert.Commodity.ToLower(), out var currentPrice))
                {
                    bool shouldTrigger = alert.AlertType.ToLower() switch
                    {
                        "above" => currentPrice.Price >= alert.ThresholdPrice,
                        "below" => currentPrice.Price <= alert.ThresholdPrice,
                        _ => false
                    };
                    
                    if (shouldTrigger)
                    {
                        alert.LastTriggered = DateTime.UtcNow;
                        triggeredAlerts.Add(alert);
                        
                        _logger.LogInformation(
                            "Alert triggered for {Commodity}: {Price} {AlertType} {Threshold}",
                            alert.Commodity, currentPrice.Price, alert.AlertType, alert.ThresholdPrice);
                    }
                }
            }
            
            if (triggeredAlerts.Any())
            {
                await _context.SaveChangesAsync();
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking price alerts");
        }
        
        return triggeredAlerts;
    }
}

// Enhanced background service with persistence
public class EnhancedPriceUpdateService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<EnhancedPriceUpdateService> _logger;
    
    public EnhancedPriceUpdateService(IServiceProvider serviceProvider, ILogger<EnhancedPriceUpdateService> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using var scope = _serviceProvider.CreateScope();
                var oilPriceService = scope.ServiceProvider.GetRequiredService<IOilPriceService>();
                var historyService = scope.ServiceProvider.GetRequiredService<IPriceHistoryService>();
                
                // Fetch current prices
                var result = await oilPriceService.GetAllCommoditiesAsync();
                
                if (result.IsSuccess && result.Data.Any())
                {
                    // Save price history
                    await historyService.SavePriceHistoryAsync(result.Data);
                    
                    // Check price alerts
                    var triggeredAlerts = await historyService.CheckAlertsAsync(result.Data);
                    
                    if (triggeredAlerts.Any())
                    {
                        _logger.LogInformation("Triggered {Count} price alerts", triggeredAlerts.Count);
                        // Here you could send emails, notifications, etc.
                    }
                    
                    _logger.LogInformation("Successfully processed {Count} commodities", result.Data.Count);
                }
                else
                {
                    _logger.LogError("Failed to fetch prices: {Error}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in enhanced price update service");
            }
            
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

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

WAHA Natural Gas

WAHA Hub natural gas spot price from the Permian Basin. Regional pricing benchmark reflecting Midland-area production and pipeline capacity constraints. Can trade at negative prices during pipeline congestion.

API Field: NATURAL_GAS_WAHA
Unit: MMBtu
Endpoint: /prices

Natural Gas (UK)

UK natural gas pricing in British pence per therm.

API Field: NATURAL_GAS_GBP
Unit: GBp/thm
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

JKM LNG

Japan/Korea Marker (JKM) LNG futures, the primary pricing benchmark for Asian liquefied natural gas markets.

API Field: JKM_LNG_USD
Unit: MMBtu
Endpoint: /health

Gasoline RBOB

Reformulated Blendstock for Oxygenate Blending (RBOB) gasoline futures.

API Field: GASOLINE_RBOB_USD
Unit: gallon
Endpoint: /health

Propane

Mont Belvieu propane spot price — the key U.S. NGL benchmark for residential heating, petrochemical feedstock, and agricultural drying.

API Field: PROPANE_MONT_BELVIEU_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

Newcastle Coal (API6)

Newcastle Coal (API6) — the Asian thermal coal benchmark price per metric ton, widely used for pricing coal imports in Asia-Pacific.

API Field: NEWCASTLE_COAL_USD
Unit: MT
Endpoint: /health

Coking Coal

Metallurgical (coking) coal price per metric ton, essential feedstock for steelmaking in blast furnaces.

API Field: COKING_COAL_USD
Unit: MT
Endpoint: /health

Central Appalachian Coal

Central Appalachian (CAPP) thermal coal spot price, a key US East Coast coal benchmark from EIA weekly data.

API Field: CAPP_COAL_USD
Unit: short_ton
Endpoint: /health

Powder River Basin Coal

Powder River Basin (Wyoming) thermal coal spot price, the lowest-cost US coal basin. EIA weekly data.

API Field: PRB_COAL_USD
Unit: short_ton
Endpoint: /health

Illinois Basin Coal

Illinois Basin thermal coal spot price, a major US interior coal benchmark. EIA weekly data.

API Field: ILLINOIS_COAL_USD
Unit: short_ton
Endpoint: /health

US Natural Gas Storage

US weekly natural gas storage inventory levels in billion cubic feet (Bcf), a key supply indicator from EIA.

API Field: NATURAL_GAS_STORAGE
Unit: Bcf
Endpoint: /health

Gold

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

API Field: GOLD_USD
Unit: ounce
Endpoint: /health

Silver

Silver spot price, a precious metal used in jewelry, electronics, solar panels, and as an investment asset.

API Field: SILVER_USD
Unit: ounce
Endpoint: /health

Platinum

Platinum spot price, a precious metal critical for automotive catalytic converters, jewelry, and industrial applications.

API Field: PLATINUM_USD
Unit: ounce
Endpoint: /health

Palladium

Palladium spot price, a precious metal essential for gasoline vehicle catalytic converters and electronics.

API Field: PALLADIUM_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

USD/CNY

US Dollar to Chinese Yuan exchange rate, critical for global commodity trade and Asian energy markets.

API Field: USD_CNY
Unit: CNY
Endpoint: /health

VLSFO Marine Fuel

Very Low Sulfur Fuel Oil Singapore benchmark price, IMO 2020 compliant marine fuel for commercial shipping. For port-specific bunker prices, use the /v1/bunker-fuels API.

API Field: VLSFO_USD
Unit: MT
Endpoint: /health

Marine Gas Oil 0.5%S

Marine Gas Oil Singapore benchmark price with 0.5% sulfur content, compliant with IMO 2020 emission standards. For port-specific bunker prices, use the /v1/bunker-fuels API.

API Field: MGO_05S_USD
Unit: MT
Endpoint: /health

Heavy Fuel Oil 380

Heavy Fuel Oil with 380 cSt viscosity, traditional marine bunker fuel for large vessels.

API Field: HFO_380_USD
Unit: MT
Endpoint: /health

Heavy Fuel Oil 180

Heavy Fuel Oil with 180 cSt viscosity, medium-grade marine bunker fuel for shipping.

API Field: HFO_180_USD
Unit: MT
Endpoint: /health

Azeri Light Crude Oil

Azeri Light (BTC) Crude Oil — key Caspian/Mediterranean benchmark transported via Baku-Tbilisi-Ceyhan pipeline, important for European refiners.

API Field: AZERI_LIGHT_USD
Unit: barrel
Endpoint: /prices

Tapis Crude Oil

Malaysian Tapis Crude Oil - Light Sweet Asian benchmark oil used for pricing in Southeast Asia.

API Field: TAPIS_CRUDE_USD
Unit: barrel
Endpoint: /health

Western Canadian Select

Western Canadian Select heavy crude oil, the primary Canadian oil benchmark.

API Field: WCS_CRUDE_USD
Unit: barrel
Endpoint: /health

Urals Crude Oil

Russian Urals crude oil, the primary Russian export grade and Eastern European benchmark.

API Field: URALS_CRUDE_USD
Unit: barrel
Endpoint: /health

OPEC Basket

OPEC Reference Basket — a weighted average of petroleum blends from OPEC member countries, used as a benchmark for OPEC production decisions.

API Field: OPEC_BASKET_USD
Unit: barrel
Endpoint: /health

Basrah Medium Crude

Iraqi Basrah Medium crude oil — a key Middle Eastern export grade and benchmark for Asian and European refiners.

API Field: BASRAH_MEDIUM_USD
Unit: barrel
Endpoint: /health

Diesel (Gulf Coast)

U.S. Gulf Coast Ultra-Low Sulfur No 2 Diesel spot price, critical for transportation and logistics.

API Field: DIESEL_USD
Unit: gallon
Endpoint: /health

ULSD Diesel (NY Harbor)

Ultra Low Sulfur No 2 Diesel spot price FOB New York Harbor, the NYMEX delivery standard.

API Field: ULSD_DIESEL_USD
Unit: gallon
Endpoint: /health

Jet Fuel

Kerosene-type jet fuel spot price, essential for aviation industry fuel costs and planning.

API Field: JET_FUEL_USD
Unit: gallon
Endpoint: /health

Ethanol

Ethanol fuel pricing, renewable biofuel component for gasoline blending.

API Field: ETHANOL_USD
Unit: gallon
Endpoint: /health

ICE Gasoil (Rotterdam)

ICE Low Sulphur Gasoil futures — the European diesel benchmark delivered in Amsterdam-Rotterdam-Antwerp (ARA). Used for pricing diesel, heating oil, and jet fuel across Europe.

API Field: GASOIL_USD
Unit: tonne
Endpoint: /health

Jet A-1 (Northwest Europe)

Jet A-1 aviation fuel spot price for Northwest Europe (NWE basis). The primary benchmark for European airline fuel procurement and aviation fuel hedging.

API Field: JET_A1_NWE_USD
Unit: barrel
Endpoint: /health

Singapore Jet Kerosene

Singapore Platts Jet Kerosene (Jet A-1) spot price — the Asia-Pacific aviation fuel benchmark used by airlines across Asia, Australia, and the Middle East.

API Field: SINGAPORE_JET_KEROSENE_USD
Unit: barrel
Endpoint: /health

Singapore Mogas 92

Singapore Platts Mogas 92 RON gasoline benchmark — the primary Asia-Pacific gasoline reference price used for gasoline trading and pricing across Asia.

API Field: SINGAPORE_MOGAS_92_USD
Unit: MT
Endpoint: /health

Jet Fuel (A4A/Argus Index)

Airlines for America (A4A) Jet Fuel Cost Index based on Argus spot prices from Chicago, Houston, Los Angeles, and New York. The U.S. airline industry standard for fuel cost benchmarking.

API Field: JET_FUEL_A4A_USD
Unit: gallon
Endpoint: /health

Asphalt

Asphalt (bitumen) spot price per metric ton, a refined petroleum product used in road construction and roofing.

API Field: ASPHALT_USD
Unit: metric_ton
Endpoint: /health

Naphtha

Naphtha spot price per metric ton, a light petroleum distillate used as a petrochemical feedstock and gasoline blending component.

API Field: NAPHTHA_USD
Unit: metric_ton
Endpoint: /health

Methanol

Methanol (methyl alcohol) spot price per metric ton, a key chemical feedstock used in plastics, fuels, and industrial applications.

API Field: METHANOL_USD
Unit: metric_ton
Endpoint: /health

Ethylene

Ethylene spot price per metric ton, the world's most-produced organic compound and foundational petrochemical feedstock for plastics.

API Field: ETHYLENE_USD
Unit: metric_ton
Endpoint: /health

Polyethylene

Polyethylene spot price per metric ton, the world's most widely used plastic polymer derived from ethylene.

API Field: POLYETHYLENE_USD
Unit: metric_ton
Endpoint: /health

Polypropylene

Polypropylene spot price per metric ton, a versatile thermoplastic polymer used in packaging, automotive parts, and textiles.

API Field: POLYPROPYLENE_USD
Unit: metric_ton
Endpoint: /health

Urea

Urea (fertilizer-grade) spot price per metric ton, a nitrogen-based fertilizer and industrial chemical derived from natural gas.

API Field: UREA_USD
Unit: metric_ton
Endpoint: /health

Ammonia

Ammonia spot price per metric ton, a key nitrogen feedstock for fertilizers and a growing green hydrogen carrier.

API Field: AMMONIA_USD
Unit: metric_ton
Endpoint: /health

Biodiesel

Biodiesel spot price per gallon, a renewable fuel produced from vegetable oils and animal fats, used as a diesel substitute or blending component.

API Field: BIODIESEL_USD
Unit: gallon
Endpoint: /health

EU Carbon Allowances

European Union Emissions Trading System (EU ETS) carbon allowance pricing.

API Field: EU_CARBON_EUR
Unit: tCO2
Endpoint: /prices

UK Carbon Allowances

UK Emissions Trading Scheme (UK ETS) carbon allowance pricing. Post-Brexit separate market from EU ETS.

API Field: UK_CARBON_GBP
Unit: tCO2
Endpoint: /health

International Rig Count

Monthly active drilling rig count for international markets outside North America, sourced from Baker Hughes.

API Field: INTERNATIONAL_RIG_COUNT
Unit: rigs
Endpoint: /health

US Rig Count

Weekly active drilling rig count for the United States from Baker Hughes, the industry standard for tracking US drilling activity across all major basins.

API Field: US_RIG_COUNT
Unit: rigs
Endpoint: /health

C# Best Practices

Async Patterns

  • Use async/await throughout the call stack
  • Configure HttpClient with proper timeouts
  • Implement proper cancellation tokens

Enterprise Features

  • Use dependency injection container
  • Implement structured logging
  • Add health checks and monitoring

Ready to Start Building?

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