Handling Responses
Learn how to parse and work with API responses from OilPriceAPI.
Response Format
All API responses follow a consistent JSON structure:
Success Response
{
"status": "success",
"data": {
// Response data here
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2025-01-10T15:30:00.000Z"
}
}
Error Response
{
"status": "fail",
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {}
}
}
Response Status Codes
Status Code | Description |
---|---|
200 OK | Request successful |
400 Bad Request | Invalid parameters or request format |
401 Unauthorized | Invalid or missing API key |
404 Not Found | Resource not found |
429 Too Many Requests | Rate limit exceeded |
500 Internal Server Error | Server error (rare) |
Parsing Price Data
Single Commodity Response
// Response from /v1/prices/latest?by_code=WTI_USD
{
"status": "success",
"data": {
"prices": {
"WTI_USD": {
"value": 78.45,
"currency": "USD",
"unit": "barrel",
"timestamp": "2025-01-10T15:30:00.000Z",
"change_24h": 1.23,
"change_percentage_24h": 1.59
}
}
}
}
// Parsing in JavaScript
const response = await fetch(url, options);
const result = await response.json();
if (result.status === 'success') {
const wtiPrice = result.data.prices.WTI_USD;
console.log(`WTI: $${wtiPrice.value} per ${wtiPrice.unit}`);
console.log(`24h change: ${wtiPrice.change_percentage_24h}%`);
}
Multiple Commodities Response
# Response from /v1/prices/latest?by_code=WTI_USD,BRENT_CRUDE_USD
response = {
"status": "success",
"data": {
"prices": {
"WTI_USD": {
"value": 78.45,
"currency": "USD",
"unit": "barrel",
"timestamp": "2025-01-10T15:30:00.000Z"
},
"BRENT_CRUDE_USD": {
"value": 82.30,
"currency": "USD",
"unit": "barrel",
"timestamp": "2025-01-10T15:30:00.000Z"
}
}
}
}
# Parsing in Python
import requests
response = requests.get(url, headers=headers)
data = response.json()
if data['status'] == 'success':
for commodity_code, price_data in data['data']['prices'].items():
print(f"{commodity_code}: ${price_data['value']}")
Working with Historical Data
Historical Response Structure
{
"status": "success",
"data": {
"commodity": "WTI_USD",
"currency": "USD",
"unit": "barrel",
"prices": [
{
"date": "2025-01-10",
"value": 78.45,
"open": 77.20,
"high": 79.10,
"low": 76.90,
"close": 78.45,
"volume": 425000
},
{
"date": "2025-01-09",
"value": 77.22,
"open": 76.50,
"high": 77.80,
"low": 76.10,
"close": 77.22,
"volume": 398000
}
],
"meta": {
"total_records": 10,
"start_date": "2025-01-01",
"end_date": "2025-01-10"
}
}
}
Processing Historical Data
// JavaScript - Calculate average price
function calculateAverage(historicalData) {
const prices = historicalData.data.prices;
const sum = prices.reduce((acc, day) => acc + day.value, 0);
return sum / prices.length;
}
// Find price trends
function analyzeTrend(historicalData) {
const prices = historicalData.data.prices;
const firstPrice = prices[0].value;
const lastPrice = prices[prices.length - 1].value;
const change = lastPrice - firstPrice;
const changePercent = (change / firstPrice) * 100;
return {
trend: change > 0 ? 'upward' : 'downward',
changeAmount: change,
changePercent: changePercent.toFixed(2)
};
}
# Python - Create DataFrame for analysis
import pandas as pd
import json
def process_historical_data(response_data):
"""Convert API response to pandas DataFrame"""
prices = response_data['data']['prices']
df = pd.DataFrame(prices)
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
# Calculate moving averages
df['ma_5'] = df['value'].rolling(window=5).mean()
df['ma_10'] = df['value'].rolling(window=10).mean()
return df
# Usage
response = requests.get(url, headers=headers)
data = response.json()
df = process_historical_data(data)
print(df.describe())
Error Handling
Common Error Codes
const ERROR_HANDLERS = {
'INVALID_API_KEY': () => {
console.error('Check your API key configuration');
// Redirect to settings or show configuration help
},
'RATE_LIMIT_EXCEEDED': (error) => {
console.error(`Rate limit hit. Resets at: ${new Date(error.reset_at)}`);
// Implement backoff or queue requests
},
'COMMODITY_NOT_FOUND': (error) => {
console.error(`Invalid commodity code: ${error.details.code}`);
// Show list of valid codes
},
'INVALID_DATE_RANGE': (error) => {
console.error('Date range issue:', error.message);
// Validate date inputs
}
};
async function makeAPICall(url, options) {
try {
const response = await fetch(url, options);
const data = await response.json();
if (data.status === 'fail') {
const handler = ERROR_HANDLERS[data.error.code];
if (handler) {
handler(data.error);
} else {
console.error('API Error:', data.error.message);
}
return null;
}
return data;
} catch (err) {
console.error('Network error:', err);
return null;
}
}
Retry Logic
import time
from typing import Optional, Dict, Any
class APIClient:
def __init__(self, api_key: str, max_retries: int = 3):
self.api_key = api_key
self.max_retries = max_retries
def make_request(self, endpoint: str, params: Dict[str, Any]) -> Optional[Dict]:
"""Make API request with automatic retry on failure"""
for attempt in range(self.max_retries):
try:
response = requests.get(
f"https://api.oilpriceapi.com/v1{endpoint}",
params=params,
headers={'Authorization': f'Token {self.api_key}'},
timeout=10
)
if response.status_code == 429:
# Rate limited - wait before retry
retry_after = int(response.headers.get('Retry-After', 60))
if attempt < self.max_retries - 1:
time.sleep(retry_after)
continue
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if attempt == self.max_retries - 1:
print(f"Failed after {self.max_retries} attempts: {e}")
return None
# Exponential backoff
wait_time = 2 ** attempt
time.sleep(wait_time)
return None
Response Headers
Important headers included in responses:
X-RateLimit-Limit: 10000 # Monthly limit
X-RateLimit-Remaining: 9876 # Requests remaining
X-RateLimit-Reset: 1735689600 # Unix timestamp when limit resets
X-Request-Id: req_abc123def # Unique request ID for support
X-Response-Time: 45 # Response time in milliseconds
Monitoring Usage
class UsageMonitor {
constructor() {
this.usage = {
limit: null,
remaining: null,
resetAt: null
};
}
updateFromHeaders(headers) {
this.usage.limit = parseInt(headers.get('x-ratelimit-limit'));
this.usage.remaining = parseInt(headers.get('x-ratelimit-remaining'));
this.usage.resetAt = new Date(parseInt(headers.get('x-ratelimit-reset')) * 1000);
this.checkUsage();
}
checkUsage() {
const percentUsed = ((this.usage.limit - this.usage.remaining) / this.usage.limit) * 100;
if (percentUsed > 90) {
console.warn(`API usage at ${percentUsed.toFixed(1)}%`);
this.sendUsageAlert();
}
}
getDaysUntilReset() {
const now = new Date();
const msUntilReset = this.usage.resetAt - now;
return Math.ceil(msUntilReset / (1000 * 60 * 60 * 24));
}
}
Data Transformation
Format for Display
function formatPriceDisplay(priceData) {
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: priceData.currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
return {
formatted: formatter.format(priceData.value),
unit: priceData.unit,
change: priceData.change_24h > 0 ? '↑' : '↓',
changeColor: priceData.change_24h > 0 ? 'green' : 'red',
changeText: `${priceData.change_24h > 0 ? '+' : ''}${priceData.change_percentage_24h}%`
};
}
// Usage
const display = formatPriceDisplay(result.data.prices.WTI_USD);
console.log(`${display.formatted}/${display.unit} ${display.changeText}`);
// Output: $78.45/barrel +1.59%
Export to CSV
import csv
from datetime import datetime
def export_to_csv(historical_data, filename):
"""Export historical price data to CSV"""
prices = historical_data['data']['prices']
commodity = historical_data['data']['commodity']
with open(filename, 'w', newline='') as csvfile:
fieldnames = ['date', 'open', 'high', 'low', 'close', 'volume']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
# Write header with commodity info
csvfile.write(f"# {commodity} Historical Prices\n")
csvfile.write(f"# Generated: {datetime.now().isoformat()}\n")
writer.writeheader()
for price in prices:
writer.writerow({
'date': price['date'],
'open': price.get('open', price['value']),
'high': price.get('high', price['value']),
'low': price.get('low', price['value']),
'close': price.get('close', price['value']),
'volume': price.get('volume', 0)
})
print(f"Exported {len(prices)} records to {filename}")
WebSocket Responses (Premium)
For real-time updates via WebSocket:
// WebSocket message format
{
"type": "price_update",
"data": {
"commodity": "WTI_USD",
"price": 78.52,
"previous": 78.45,
"change": 0.07,
"timestamp": "2025-01-10T15:31:00.000Z"
}
}
// Handling WebSocket messages
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'price_update') {
updatePriceDisplay(message.data);
// Check for significant changes
if (Math.abs(message.data.change) > 1.0) {
notifySignificantChange(message.data);
}
}
};
Next Steps
- Explore the API Reference for detailed endpoint documentation
- Learn about Marine Fuels specialty endpoints
- Set up WebSocket connections for real-time updates (Premium)