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.
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".
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.