C++ Oil Price API
Integrate real-time oil and commodity prices into your C++ applications. High-performance with modern C++17 features, async processing, and cross-platform support.
Quick Start
1. Install Dependencies
# Ubuntu/Debian
sudo apt-get install libcurl4-openssl-dev nlohmann-json3-dev
# macOS with Homebrew
brew install curl nlohmann-json
# Build with CMake
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
2. C++ Features
- Modern C++17 with RAII and smart pointers
- Async processing with std::future
- Cross-platform (Windows, Linux, macOS)
Basic Integration with libcurl
#include <iostream>
#include <string>
#include <map>
#include <memory>
#include <curl/curl.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
class OilPriceAPI {
private:
std::string api_key_;
std::string base_url_;
// Callback function for libcurl to write data
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
userp->append((char*)contents, size * nmemb);
return size * nmemb;
}
// Make HTTP GET request
std::pair<long, std::string> makeRequest(const std::string& endpoint) {
CURL* curl;
CURLcode res;
std::string response_body;
long response_code = 0;
curl = curl_easy_init();
if (!curl) {
throw std::runtime_error("Failed to initialize libcurl");
}
std::string url = base_url_ + endpoint;
struct curl_slist* headers = nullptr;
// Set headers
std::string auth_header = "Authorization: Token " + api_key_;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, auth_header.c_str());
// Configure curl
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "OilPriceAPI-CPP-Client/1.0");
// Perform request
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
}
// Cleanup
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
throw std::runtime_error("HTTP request failed: " + std::string(curl_easy_strerror(res)));
}
return std::make_pair(response_code, response_body);
}
public:
OilPriceAPI(const std::string& api_key)
: api_key_(api_key), base_url_("https://api.oilpriceapi.com") {
// Initialize libcurl globally (call once per application)
curl_global_init(CURL_GLOBAL_DEFAULT);
}
~OilPriceAPI() {
// Cleanup libcurl globally
curl_global_cleanup();
}
json getBrentPrice() {
auto [status_code, response_body] = makeRequest("/prices");
if (status_code != 200) {
throw std::runtime_error("HTTP Error: " + std::to_string(status_code));
}
return json::parse(response_body);
}
json getCommodityHealth() {
auto [status_code, response_body] = makeRequest("/health");
if (status_code != 200) {
throw std::runtime_error("HTTP Error: " + std::to_string(status_code));
}
return json::parse(response_body);
}
struct CommodityPrice {
double price;
std::string timestamp;
std::string currency;
std::string unit;
CommodityPrice(double p, const std::string& ts, const std::string& curr, const std::string& u)
: price(p), timestamp(ts), currency(curr), unit(u) {}
};
std::optional<CommodityPrice> getWTIPrice() {
try {
json health_data = getCommodityHealth();
if (!health_data.contains("metrics") ||
!health_data["metrics"].contains("commodity_health") ||
!health_data["metrics"]["commodity_health"].contains("WTI_USD")) {
return std::nullopt;
}
auto wti_data = health_data["metrics"]["commodity_health"]["WTI_USD"];
if (!wti_data.contains("latest_price") || !wti_data.contains("last_update")) {
return std::nullopt;
}
double latest_price = wti_data["latest_price"].get<double>();
std::string last_update = wti_data["last_update"].get<std::string>();
return CommodityPrice(
latest_price / 100.0, // Convert from cents
last_update,
"USD",
"barrel"
);
} catch (const std::exception& e) {
std::cerr << "Error getting WTI price: " << e.what() << std::endl;
return std::nullopt;
}
}
std::map<std::string, CommodityPrice> getAllCommodities() {
std::map<std::string, CommodityPrice> commodities;
try {
// Get Brent from prices endpoint
json brent_response = getBrentPrice();
if (brent_response.contains("data")) {
auto data = brent_response["data"];
commodities.emplace("brent_crude", CommodityPrice(
data["price"].get<double>(),
data["timestamp"].get<std::string>(),
data["currency"].get<std::string>(),
"barrel"
));
}
// Get other commodities from health endpoint
json health_response = getCommodityHealth();
if (health_response.contains("metrics") &&
health_response["metrics"].contains("commodity_health")) {
auto commodity_health = health_response["metrics"]["commodity_health"];
for (auto& [key, data] : commodity_health.items()) {
if (data.contains("latest_price") && data.contains("last_update")) {
double scale = isCurrencyPair(key) ? 10000.0 : 100.0;
double price = data["latest_price"].get<double>() / scale;
std::string lowercase_key = key;
std::transform(lowercase_key.begin(), lowercase_key.end(), lowercase_key.begin(), ::tolower);
commodities.emplace(lowercase_key, CommodityPrice(
price,
data["last_update"].get<std::string>(),
"USD",
getUnit(key)
));
}
}
}
} catch (const std::exception& e) {
std::cerr << "Error getting commodities: " << e.what() << std::endl;
}
return commodities;
}
private:
bool isCurrencyPair(const std::string& commodity_key) {
return commodity_key == "EUR_USD" || commodity_key == "GBP_USD";
}
std::string getUnit(const std::string& commodity_key) {
static const std::map<std::string, std::string> units = {
{"WTI_USD", "barrel"},
{"NATURAL_GAS_USD", "MMBtu"},
{"GOLD_USD", "ounce"},
{"EUR_USD", "USD"},
{"GBP_USD", "USD"}
};
auto it = units.find(commodity_key);
return (it != units.end()) ? it->second : "unit";
}
};
// Usage example
int main() {
try {
OilPriceAPI api("YOUR_API_KEY_HERE");
std::cout << "=== Live Oil Prices ===" << std::endl;
// Get Brent crude price
json brent_data = api.getBrentPrice();
if (brent_data.contains("data")) {
auto data = brent_data["data"];
std::cout << "Brent Crude: $" << data["price"].get<double>() << "/barrel" << std::endl;
}
// Get WTI price
auto wti_price = api.getWTIPrice();
if (wti_price) {
std::cout << "WTI Crude: $" << std::fixed << std::setprecision(2)
<< wti_price->price << "/" << wti_price->unit << std::endl;
}
// Get all commodities
auto all_commodities = api.getAllCommodities();
for (const auto& [name, price] : all_commodities) {
std::cout << name << ": $" << std::fixed << std::setprecision(2)
<< price.price << "/" << price.unit << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Modern C++17 with Async Processing
Enhanced version using modern C++ features, RAII, and async processing:
#include <iostream>
#include <string>
#include <map>
#include <future>
#include <memory>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <curl/curl.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace std::chrono_literals;
class ModernOilPriceAPI {
public:
struct PriceData {
double price;
std::chrono::system_clock::time_point timestamp;
std::string currency;
std::string unit;
PriceData(double p, const std::string& ts, const std::string& curr, const std::string& u)
: price(p), currency(curr), unit(u) {
// Parse ISO 8601 timestamp
std::istringstream ss(ts);
std::tm tm = {};
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
timestamp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
}
std::string getFormattedTimestamp() const {
auto time_t = std::chrono::system_clock::to_time_t(timestamp);
std::stringstream ss;
ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
return ss.str();
}
};
enum class CommodityType {
BRENT_CRUDE,
WTI_CRUDE,
NATURAL_GAS,
GOLD,
EUR_USD,
GBP_USD
};
private:
std::string api_key_;
std::string base_url_;
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl_;
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
userp->append(static_cast<char*>(contents), size * nmemb);
return size * nmemb;
}
std::future<json> makeAsyncRequest(const std::string& endpoint) {
return std::async(std::launch::async, [this, endpoint]() -> json {
return makeRequest(endpoint);
});
}
json makeRequest(const std::string& endpoint) {
if (!curl_) {
throw std::runtime_error("CURL not initialized");
}
std::string response_body;
std::string url = base_url_ + endpoint;
struct curl_slist* headers = nullptr;
std::string auth_header = "Authorization: Token " + api_key_;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, auth_header.c_str());
// Configure CURL
curl_easy_setopt(curl_.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_.get(), CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl_.get(), CURLOPT_WRITEDATA, &response_body);
curl_easy_setopt(curl_.get(), CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl_.get(), CURLOPT_CONNECTTIMEOUT, 5L);
curl_easy_setopt(curl_.get(), CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl_.get(), CURLOPT_SSL_VERIFYHOST, 2L);
CURLcode res = curl_easy_perform(curl_.get());
curl_slist_free_all(headers);
if (res != CURLE_OK) {
throw std::runtime_error("HTTP request failed: " + std::string(curl_easy_strerror(res)));
}
long response_code;
curl_easy_getinfo(curl_.get(), CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
throw std::runtime_error("HTTP Error: " + std::to_string(response_code));
}
return json::parse(response_body);
}
public:
explicit ModernOilPriceAPI(const std::string& api_key)
: api_key_(api_key),
base_url_("https://api.oilpriceapi.com"),
curl_(curl_easy_init(), curl_easy_cleanup) {
if (!curl_) {
throw std::runtime_error("Failed to initialize libcurl");
}
// Initialize libcurl globally (should be called once)
curl_global_init(CURL_GLOBAL_DEFAULT);
}
~ModernOilPriceAPI() {
curl_global_cleanup();
}
// Async methods using std::future
std::future<std::optional<PriceData>> getBrentPriceAsync() {
return std::async(std::launch::async, [this]() -> std::optional<PriceData> {
try {
json response = makeRequest("/prices");
if (response.contains("data")) {
auto data = response["data"];
return PriceData(
data["price"].get<double>(),
data["timestamp"].get<std::string>(),
data["currency"].get<std::string>(),
"barrel"
);
}
return std::nullopt;
} catch (const std::exception& e) {
std::cerr << "Error getting Brent price: " << e.what() << std::endl;
return std::nullopt;
}
});
}
std::future<std::optional<PriceData>> getWTIPriceAsync() {
return std::async(std::launch::async, [this]() -> std::optional<PriceData> {
try {
json health_data = makeRequest("/health");
auto wti_data = health_data["metrics"]["commodity_health"]["WTI_USD"];
if (wti_data.contains("latest_price") && wti_data.contains("last_update")) {
return PriceData(
wti_data["latest_price"].get<double>() / 100.0,
wti_data["last_update"].get<std::string>(),
"USD",
"barrel"
);
}
return std::nullopt;
} catch (const std::exception& e) {
std::cerr << "Error getting WTI price: " << e.what() << std::endl;
return std::nullopt;
}
});
}
std::future<std::map<std::string, PriceData>> getAllCommoditiesAsync() {
return std::async(std::launch::async, [this]() -> std::map<std::string, PriceData> {
std::map<std::string, PriceData> commodities;
try {
// Launch both requests concurrently
auto brent_future = makeAsyncRequest("/prices");
auto health_future = makeAsyncRequest("/health");
// Get Brent data
try {
json brent_response = brent_future.get();
if (brent_response.contains("data")) {
auto data = brent_response["data"];
commodities.emplace("brent_crude", PriceData(
data["price"].get<double>(),
data["timestamp"].get<std::string>(),
data["currency"].get<std::string>(),
"barrel"
));
}
} catch (const std::exception& e) {
std::cerr << "Error processing Brent data: " << e.what() << std::endl;
}
// Get health data
try {
json health_response = health_future.get();
if (health_response.contains("metrics") &&
health_response["metrics"].contains("commodity_health")) {
auto commodity_health = health_response["metrics"]["commodity_health"];
for (auto& [key, data] : commodity_health.items()) {
if (data.contains("latest_price") && data.contains("last_update")) {
double scale = isCurrencyPair(key) ? 10000.0 : 100.0;
double price = data["latest_price"].get<double>() / scale;
std::string lowercase_key = key;
std::transform(lowercase_key.begin(), lowercase_key.end(),
lowercase_key.begin(), ::tolower);
commodities.emplace(lowercase_key, PriceData(
price,
data["last_update"].get<std::string>(),
"USD",
getUnit(key)
));
}
}
}
} catch (const std::exception& e) {
std::cerr << "Error processing health data: " << e.what() << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Error in getAllCommoditiesAsync: " << e.what() << std::endl;
}
return commodities;
});
}
// Synchronous convenience methods
std::optional<PriceData> getBrentPrice() {
return getBrentPriceAsync().get();
}
std::optional<PriceData> getWTIPrice() {
return getWTIPriceAsync().get();
}
std::map<std::string, PriceData> getAllCommodities() {
return getAllCommoditiesAsync().get();
}
private:
bool isCurrencyPair(const std::string& commodity_key) const {
return commodity_key == "EUR_USD" || commodity_key == "GBP_USD";
}
std::string getUnit(const std::string& commodity_key) const {
static const std::unordered_map<std::string, std::string> units = {
{"WTI_USD", "barrel"},
{"NATURAL_GAS_USD", "MMBtu"},
{"GOLD_USD", "ounce"},
{"EUR_USD", "USD"},
{"GBP_USD", "USD"}
};
auto it = units.find(commodity_key);
return (it != units.end()) ? it->second : "unit";
}
};
// Modern usage example with async processing
int main() {
try {
ModernOilPriceAPI api("YOUR_API_KEY_HERE");
std::cout << "=== Fetching Live Prices Asynchronously ===" << std::endl;
// Launch all requests asynchronously
auto brent_future = api.getBrentPriceAsync();
auto wti_future = api.getWTIPriceAsync();
auto all_future = api.getAllCommoditiesAsync();
// Wait for and process results
auto brent_price = brent_future.get();
if (brent_price) {
std::cout << "Brent Crude: $" << std::fixed << std::setprecision(2)
<< brent_price->price << "/" << brent_price->unit
<< " (updated: " << brent_price->getFormattedTimestamp() << ")" << std::endl;
}
auto wti_price = wti_future.get();
if (wti_price) {
std::cout << "WTI Crude: $" << std::fixed << std::setprecision(2)
<< wti_price->price << "/" << wti_price->unit
<< " (updated: " << wti_price->getFormattedTimestamp() << ")" << std::endl;
}
std::cout << "\n=== All Commodities ===" << std::endl;
auto all_commodities = all_future.get();
for (const auto& [name, price] : all_commodities) {
std::cout << name << ": $" << std::fixed << std::setprecision(4)
<< price.price << "/" << price.unit << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Using Microsoft cpprestsdk
Alternative implementation using Microsoft's cpprestsdk for modern async HTTP:
// Using Microsoft's cpprestsdk for modern C++ HTTP client
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
#include <cpprest/json.h>
#include <iostream>
#include <memory>
#include <string>
#include <map>
using namespace web;
using namespace web::http;
using namespace web::http::client;
class CppRestOilPriceAPI {
private:
std::string api_key_;
std::unique_ptr<http_client> client_;
public:
explicit CppRestOilPriceAPI(const std::string& api_key)
: api_key_(api_key) {
http_client_config config;
config.set_timeout(std::chrono::seconds(10));
client_ = std::make_unique<http_client>(
U("https://api.oilpriceapi.com"),
config
);
}
pplx::task<json::value> getBrentPriceAsync() {
http_request request(methods::GET);
request.headers().add(U("Authorization"), U("Token " + api_key_));
request.headers().add(U("Content-Type"), U("application/json"));
return client_->request(request, U("/prices"))
.then([](http_response response) -> pplx::task<json::value> {
if (response.status_code() == status_codes::OK) {
return response.extract_json();
}
throw std::runtime_error("HTTP Error: " + std::to_string(response.status_code()));
});
}
pplx::task<json::value> getCommodityHealthAsync() {
http_request request(methods::GET);
request.headers().add(U("Authorization"), U("Token " + api_key_));
request.headers().add(U("Content-Type"), U("application/json"));
return client_->request(request, U("/health"))
.then([](http_response response) -> pplx::task<json::value> {
if (response.status_code() == status_codes::OK) {
return response.extract_json();
}
throw std::runtime_error("HTTP Error: " + std::to_string(response.status_code()));
});
}
pplx::task<std::map<std::string, double>> getAllPricesAsync() {
// Launch both requests concurrently
auto brent_task = getBrentPriceAsync();
auto health_task = getCommodityHealthAsync();
return pplx::when_all(brent_task, health_task)
.then([](std::vector<json::value> results) -> std::map<std::string, double> {
std::map<std::string, double> prices;
try {
// Process Brent data (results[0])
if (results[0].has_field(U("data"))) {
auto data = results[0][U("data")];
if (data.has_field(U("price"))) {
prices["brent_crude"] = data[U("price")].as_double();
}
}
// Process health data (results[1])
if (results[1].has_field(U("metrics"))) {
auto metrics = results[1][U("metrics")];
if (metrics.has_field(U("commodity_health"))) {
auto commodity_health = metrics[U("commodity_health")];
for (auto& pair : commodity_health.as_object()) {
auto key = utility::conversions::to_utf8string(pair.first);
auto data = pair.second;
if (data.has_field(U("latest_price"))) {
double scale = (key == "EUR_USD" || key == "GBP_USD") ? 10000.0 : 100.0;
double price = data[U("latest_price")].as_double() / scale;
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
prices[key] = price;
}
}
}
}
} catch (const std::exception& e) {
std::cerr << "Error processing price data: " << e.what() << std::endl;
}
return prices;
});
}
};
// Usage example with cpprestsdk
void demonstrateCppRest() {
try {
CppRestOilPriceAPI api("YOUR_API_KEY_HERE");
std::cout << "=== Using cpprestsdk for async HTTP ===" << std::endl;
// Get all prices asynchronously
api.getAllPricesAsync()
.then([](std::map<std::string, double> prices) {
std::cout << "\n=== Live Commodity Prices ===" << std::endl;
for (const auto& [name, price] : prices) {
std::cout << name << ": $" << std::fixed << std::setprecision(2)
<< price << std::endl;
}
})
.then([](pplx::task<void> previous_task) {
try {
previous_task.wait();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
})
.wait();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
CMake Build Configuration
Complete CMakeLists.txt for building your Oil Price API client:
# CMakeLists.txt for Oil Price API C++ client
cmake_minimum_required(VERSION 3.15)
project(OilPriceAPI VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Find required packages
find_package(PkgConfig REQUIRED)
find_package(nlohmann_json 3.11.0 REQUIRED)
# Find libcurl
pkg_check_modules(CURL REQUIRED libcurl)
# Optional: Find cpprestsdk
find_package(cpprestsdk QUIET)
# Create library for Oil Price API client
add_library(oilpriceapi STATIC
src/oil_price_api.cpp
src/oil_price_api.h
)
target_link_libraries(oilpriceapi
PRIVATE
nlohmann_json::nlohmann_json
${CURL_LIBRARIES}
)
target_include_directories(oilpriceapi
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE
${CURL_INCLUDE_DIRS}
)
target_compile_options(oilpriceapi
PRIVATE
${CURL_CFLAGS_OTHER}
)
# Create executable examples
add_executable(basic_example examples/basic_example.cpp)
target_link_libraries(basic_example oilpriceapi)
add_executable(modern_example examples/modern_example.cpp)
target_link_libraries(modern_example oilpriceapi)
# Optional: cpprestsdk example
if(cpprestsdk_FOUND)
add_executable(cpprest_example examples/cpprest_example.cpp)
target_link_libraries(cpprest_example
cpprestsdk::cpprest
nlohmann_json::nlohmann_json
)
endif()
# Installation
install(TARGETS oilpriceapi
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
install(DIRECTORY include/ DESTINATION include)
# Package configuration
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/OilPriceAPIConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
export(TARGETS oilpriceapi
FILE "${CMAKE_CURRENT_BINARY_DIR}/OilPriceAPITargets.cmake"
)
# Build instructions:
# mkdir build && cd build
# cmake .. -DCMAKE_BUILD_TYPE=Release
# make -j$(nproc)
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
Memory Management
- Use RAII and smart pointers
- Proper cleanup of libcurl resources
- Exception safety with RAII
Performance
- Use async processing for concurrent requests
- Reuse CURL handles for multiple requests
- Implement connection pooling
Ready to Start Building?
Get your free API key and start integrating real-time commodity prices into your C++ applications today.