OilPriceAPI Docs
GitHub
GitHub
  • Guides

    • Authentication
    • Security & Compliance
    • API Versioning
    • Data Freshness & Update Frequency
    • Data Quality and Validation
    • Coal Price Data - Complete Guide
    • Testing & Development
    • Error Codes Reference
    • SDK Code Examples
    • Webhook Signature Verification
    • Production Deployment Checklist
    • Service Level Agreement (SLA)
    • Rate Limiting & Response Headers
    • Troubleshooting Guide
    • Incident Response Guide
    • Video Tutorials

Rate Limiting & Response Headers

Understanding rate limits and response headers is crucial for building reliable integrations with the OilPriceAPI.


Rate Limits by Plan

Two independent limits apply:

  1. A monthly request quota, which varies by plan. Exceeding it returns 402 Payment Required.
  2. A request rate limit of 60 requests per rolling 60-second window, per API key, which is the same on every plan. Exceeding it returns 429 Too Many Requests.

The rate limit is a window, not a per-second cap. Ten requests fired in the same second are fine — you are only throttled once you have made more than 60 requests in the preceding 60 seconds. There is no requirement to space requests one second apart.

PlanMonthly QuotaRequest Rate Limit
Free200 / month60 per rolling 60s
Free Trial (7 days)10,00060 per rolling 60s
Developer10,000 / month60 per rolling 60s
Starter50,000 / month60 per rolling 60s
Professional100,000 / month60 per rolling 60s
Scale1,000,000 / month60 per rolling 60s

Plan prices are on the pricing page. Plans differ by monthly quota, not by request rate.

Other Rate-Limit Lanes

A few request classes have their own limits:

Request classLimitKeyed by
Authenticated /v1/* (the default)60 per rolling 60sAPI key
Unauthenticated /v1/* (e.g. demo endpoints)10 per rolling 60sClient IP
Authenticated but over monthly quota5 per rolling 60sAPI key
Marine-fuel endpoints30 per rolling 60sAPI key
Marine-fuel historical5 per rolling 60sAPI key

If you have blown through your monthly quota, your rate limit drops from 60/min to 5/min until the quota resets — so a client that ignores 402 responses and keeps hammering will also start seeing 429.

Free Trial

New accounts receive a 7-day free trial with:

  • 10,000 API requests during the trial period
  • Access to all commodities (460+)
  • Full API functionality (Professional-level features)
  • No credit card required

After the trial ends, the account continues on the Free tier with 200 requests/month. Upgrade when you need higher monthly quotas or paid-tier features.

Note: The request rate limit is 60 requests per rolling 60-second window on all plans; exceeding it returns 429. Monthly quotas reset on the 1st of each month at 00:00 UTC. Trial quota converts when you upgrade to a paid plan.


Rate Limit Headers

Every API response includes headers that help you monitor your usage:

Rate Limit Headers

Every response includes headers that track your monthly quota:

HeaderDescriptionExample
X-RateLimit-LimitYour plan's monthly request quota10000
X-RateLimit-RemainingRequests remaining this month9876
X-RateLimit-UsedRequests used this month124
X-RateLimit-ResetUnix timestamp when the monthly quota resets1785542399
X-RateLimit-TierYour current plan tierdeveloper
X-Request-IdUnique request ID — include it in support tickets8c1fc2b1-…

Pagination Headers

When retrieving paginated data (historical prices, etc.):

HeaderDescriptionExample
X-TotalTotal number of records available2016
X-Total-PagesTotal number of pages21
X-PageCurrent page number1
X-Per-PageNumber of records per page100
LinkRFC 5988 pagination linksSee below

Link Header Format

Link: <https://api.oilpriceapi.com/v1/prices/past_week?page=1>; rel="first",
      <https://api.oilpriceapi.com/v1/prices/past_week?page=2>; rel="next",
      <https://api.oilpriceapi.com/v1/prices/past_week?page=21>; rel="last"

Response Time Headers

HeaderDescriptionExample
X-Response-TimeServer processing time in milliseconds145ms
X-Request-IdUnique request identifier for supportreq_abc123def456
X-CacheCache status (HIT/MISS)HIT

Handling Rate Limits

Check Headers Before Making Requests

import requests
import time

class RateLimitedClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.remaining = None
        self.reset_time = None

    def make_request(self, endpoint, params=None):
        # Check if we need to wait
        if self.remaining == 0 and self.reset_time:
            wait_time = self.reset_time - time.time()
            if wait_time > 0:
                print(f"Rate limited. Waiting {wait_time:.0f} seconds...")
                time.sleep(wait_time)

        response = requests.get(
            f'https://api.oilpriceapi.com/v1{endpoint}',
            headers={'Authorization': f'Token {self.api_key}'},
            params=params
        )

        # Update rate limit info
        self.remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
        self.reset_time = int(response.headers.get('X-RateLimit-Reset', 0))

        return response

Implement Exponential Backoff

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    // Check rate limit headers
    const remaining = response.headers.get("X-RateLimit-Remaining");
    const resetAfter = response.headers.get("X-RateLimit-Reset-After");

    console.log(`Remaining requests: ${remaining}`);

    if (response.status === 429) {
      // Rate limited - wait and retry
      const waitTime = parseInt(resetAfter || "60") * 1000;
      console.log(`Rate limited. Waiting ${waitTime}ms...`);
      await new Promise((resolve) => setTimeout(resolve, waitTime));
      continue;
    }

    if (response.ok) {
      return response;
    }

    // Exponential backoff for other errors
    const delay = Math.min(1000 * Math.pow(2, i), 10000);
    await new Promise((resolve) => setTimeout(resolve, delay));
  }

  throw new Error("Max retries exceeded");
}

Batch Requests Efficiently

def fetch_multiple_commodities_efficiently():
    """
    Instead of making separate requests for each commodity,
    use the all-prices endpoint or comma-separated codes
    """

    # ❌ Inefficient - Multiple requests
    # for code in ['WTI_USD', 'BRENT_CRUDE_USD', 'NATURAL_GAS_USD']:
    #     response = requests.get(f'/v1/prices/latest?by_code={code}')

    # ✅ Efficient - Single request
    response = requests.get(
        'https://api.oilpriceapi.com/v1/prices/latest',
        headers={'Authorization': 'Token YOUR_API_KEY'},
        params={'by_code': 'WTI_USD,BRENT_CRUDE_USD,NATURAL_GAS_USD'}
    )

    return response.json()

Rate Limit Error Responses

429 Too Many Requests

When you exceed rate limits:

{
  "error": "Rate limit exceeded",
  "error_code": "RATE_LIMIT_EXCEEDED",
  "message": "Too many requests. Upgrade for higher limits.",
  "retry_after": 37
}

Response Headers on 429

HTTP/2 429
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 37
Retry-After: 37

Retry-After and X-RateLimit-Reset are seconds until the window resets, not a timestamp. Sleep for that long and retry.

Best Practices

1. Cache Responses

class CachedAPIClient {
  constructor(apiKey, cacheTTL = 300000) {
    // 5 minutes default
    this.apiKey = apiKey;
    this.cache = new Map();
    this.cacheTTL = cacheTTL;
  }

  async fetch(endpoint, params = {}) {
    const cacheKey = `${endpoint}:${JSON.stringify(params)}`;
    const cached = this.cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
      console.log("Cache hit");
      return cached.data;
    }

    const response = await fetch(
      `https://api.oilpriceapi.com/v1${endpoint}?${new URLSearchParams(params)}`,
      { headers: { Authorization: `Token ${this.apiKey}` } },
    );

    const data = await response.json();

    this.cache.set(cacheKey, {
      data,
      timestamp: Date.now(),
    });

    return data;
  }
}

2. Use Webhooks for Real-time Updates

Instead of polling, use webhooks (available on Starter and above):

# Instead of polling every minute
# ❌ while True:
#     data = fetch_prices()
#     time.sleep(60)

# ✅ Set up a webhook endpoint
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook/prices', methods=['POST'])
def handle_price_update():
    data = request.json
    # Process real-time price update
    return '', 200

3. Implement Request Queuing

class RequestQueue {
  constructor(apiKey, requestsPerMinute = 100) {
    this.apiKey = apiKey;
    this.queue = [];
    this.interval = 60000 / requestsPerMinute;
    this.processing = false;
  }

  async add(endpoint, params) {
    return new Promise((resolve, reject) => {
      this.queue.push({ endpoint, params, resolve, reject });
      if (!this.processing) {
        this.process();
      }
    });
  }

  async process() {
    this.processing = true;

    while (this.queue.length > 0) {
      const { endpoint, params, resolve, reject } = this.queue.shift();

      try {
        const response = await fetch(
          `https://api.oilpriceapi.com/v1${endpoint}`,
          {
            headers: { Authorization: `Token ${this.apiKey}` },
            params,
          },
        );
        resolve(await response.json());
      } catch (error) {
        reject(error);
      }

      // Wait before next request
      await new Promise((r) => setTimeout(r, this.interval));
    }

    this.processing = false;
  }
}

Monitoring Your Usage

Track Usage in Your Application

import logging
from datetime import datetime

class UsageMonitor:
    def __init__(self):
        self.requests_made = 0
        self.monthly_remaining = None
        self.rate_limit_remaining = None

    def log_response(self, response):
        # Extract headers
        self.monthly_remaining = response.headers.get('X-Monthly-Remaining')
        self.rate_limit_remaining = response.headers.get('X-RateLimit-Remaining')
        self.requests_made += 1

        # Log if approaching limits
        if self.monthly_remaining and int(self.monthly_remaining) < 100:
            logging.warning(f"Only {self.monthly_remaining} monthly requests remaining!")

        if self.rate_limit_remaining and int(self.rate_limit_remaining) < 10:
            logging.warning(f"Only {self.rate_limit_remaining} requests remaining in rate limit window!")

Dashboard Monitoring

Monitor your usage at oilpriceapi.com/dashboard:

  • Real-time request count
  • Usage by endpoint
  • Error rates
  • Response times
  • Geographic distribution

Upgrading Your Plan

If you consistently hit rate limits, consider upgrading:

  1. Monitor your usage patterns
  2. Calculate required limits
  3. Visit oilpriceapi.com/pricing
  4. Upgrade instantly without downtime

Related Documentation

  • Authentication
  • Error Handling
  • Webhooks
  • Production Checklist
Last Updated: 7/13/26, 2:09 PM
Prev
Service Level Agreement (SLA)
Next
Troubleshooting Guide