Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.12.2] - 2026-08-11

### Fixed

- Remove stale fixed plan-price, monthly allowance, cadence, uptime, and
generic real-time claims from documentation and packaged docstrings.
- Recursively validate authored docs and package source, then scan the exact
installed wheel and PyPI metadata during the release smoke test.

## [1.12.1] - 2026-08-11

### Fixed
Expand Down
10 changes: 5 additions & 5 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This guide showcases practical applications of the [OilPriceAPI Python SDK](https://oilpriceapi.com) for energy trading, financial analysis, research, and application development.

**[Get your free API key →](https://oilpriceapi.com/auth/signup)** to run these examples.
**[Create an API key →](https://oilpriceapi.com/auth/signup)** to run these examples.

## 📊 Table of Contents

Expand Down Expand Up @@ -262,7 +262,7 @@ print(f"Predicted change: {((predictions[-1] - y[-1]) / y[-1] * 100):.2f}%")

## 💻 Web & Mobile Applications

### Example 7: Real-Time Price Dashboard (Streamlit)
### Example 7: Current Price Dashboard (Streamlit)

Create an interactive web dashboard for monitoring oil prices.

Expand Down Expand Up @@ -320,7 +320,7 @@ try:
)
st.plotly_chart(fig, use_container_width=True)

st.success(f"✅ Data updates every 5 minutes • [View all commodities](https://docs.oilpriceapi.com/commodities)")
st.success(f"✅ Values include API-provided source timestamps • [View commodity metadata](https://docs.oilpriceapi.com/commodities)")

except Exception as e:
st.error(f"Error: {e}")
Expand Down Expand Up @@ -456,7 +456,7 @@ def monitor_prices():
elif price.value < limits['low']:
send_alert(commodity, price.value, limits['low'], 'BELOW')

# Check every 5 minutes (aligned with API update frequency)
# Example caller-selected interval; honor API limit and freshness metadata.
time.sleep(300)

if __name__ == '__main__':
Expand Down Expand Up @@ -621,7 +621,7 @@ print("📊 Powered by https://oilpriceapi.com")

Ready to build with these examples?

1. **[Sign up for free](https://oilpriceapi.com/auth/signup)** - Get 50 requests/day
1. **[Create an API key](https://oilpriceapi.com/auth/signup)** - The current free-account allowance is 50 requests/day; verify the [product facts](https://api.oilpriceapi.com/product-facts.json)
2. **[Install the SDK](https://pypi.org/project/oilpriceapi/)** - `pip install oilpriceapi`
3. **[Read the docs](https://docs.oilpriceapi.com/sdk/python)** - Complete API reference
4. **[Choose a plan](https://oilpriceapi.com/pricing)** - Upgrade for more requests
Expand Down
70 changes: 41 additions & 29 deletions docs/PERFORMANCE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,23 +231,23 @@ while True:
**Problems:**
- Wastes API quota
- Unnecessary load on API
- Price only updates ~every 5 minutes
- Ignores the record's API-provided source timestamp and freshness metadata

**Solution:**
```python
# Poll at reasonable interval
# Choose an interval from API limits and the application's freshness need
import time

while True:
price = client.prices.get("WTI_USD")
print(f"WTI: ${price.value}")
time.sleep(300) # Poll every 5 minutes
time.sleep(300) # Example client-selected interval
```

**Better Solution (for real-time):**
**Better Solution (for streamed updates):**
```python
# Use WebSocket for real-time updates (if available)
# Or increase polling interval to match update frequency
# Use WebSocket streaming when the account is entitled to it.
# Otherwise use response metadata to select the polling interval.
```

### Pitfall 2: Fetching All Historical Data
Expand Down Expand Up @@ -335,52 +335,64 @@ price = client.prices.get("WTI_USD") # Resilient

**Basic In-Memory Cache:**
```python
from datetime import datetime, timedelta
from functools import lru_cache
from datetime import datetime, timedelta, timezone

@lru_cache(maxsize=100)
def get_cached_price(commodity, cache_key):
"""Cache prices for 5 minutes."""
client = OilPriceAPI()
return client.prices.get(commodity)
price_cache = {}
# Illustrative application policy; choose this for your freshness requirement.
MAX_SOURCE_AGE = timedelta(minutes=5)

# Cache key changes every 5 minutes
def get_current_price(commodity):
cache_key = int(datetime.now().timestamp() / 300)
return get_cached_price(commodity, cache_key)
cached = price_cache.get(commodity)
if cached:
source_age = datetime.now(timezone.utc) - cached["source_timestamp"]
if source_age <= MAX_SOURCE_AGE:
return cached["price"]

price = client.prices.get(commodity)
price_cache[commodity] = {
"price": price,
"source_timestamp": price.timestamp,
}
return price

# First call: API request (150ms)
price1 = get_current_price("WTI_USD")

# Second call within 5 min: cached (<1ms)
# A second call is cached only while its source timestamp meets the policy.
price2 = get_current_price("WTI_USD")
```

**Redis Cache (for multi-process):**
```python
import redis
import json
from datetime import timedelta
from datetime import datetime, timedelta, timezone

from oilpriceapi.models import Price

redis_client = redis.Redis(host='localhost', port=6379)
# Illustrative application policy; use your required maximum source age.
MAX_SOURCE_AGE = timedelta(minutes=5)

def get_cached_price(client, commodity):
"""Cache price in Redis for 5 minutes."""
cache_key = f"oilprice:{commodity}"
cache_key = f"oilprice:{commodity}:latest"

# Check cache
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached)
payload = json.loads(cached)
source_timestamp = datetime.fromisoformat(payload["source_timestamp"])
if datetime.now(timezone.utc) - source_timestamp <= MAX_SOURCE_AGE:
return Price.model_validate(payload["price"])

# Fetch from API
price = client.prices.get(commodity)

# Cache for 5 minutes
payload = {
"price": price.model_dump(mode="json"),
"source_timestamp": price.timestamp.isoformat(),
}
redis_client.setex(
cache_key,
timedelta(minutes=5),
json.dumps(price.dict())
int(MAX_SOURCE_AGE.total_seconds()),
json.dumps(payload),
)

return price
Expand All @@ -389,13 +401,13 @@ def get_cached_price(client, commodity):
### When to Cache

✅ **Good candidates for caching:**
- Latest prices (updates every 5 minutes)
- Latest prices, keyed by the API-provided source timestamp
- Historical data (never changes)
- Commodity metadata
- Static reference data

❌ **Don't cache:**
- Real-time price updates (if using WebSocket)
- Streamed price updates (when using WebSocket)
- User-specific data
- Data that changes frequently

Expand Down
4 changes: 2 additions & 2 deletions docs/TELEMETRY.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,10 @@ Possible causes:
v
┌─────────────┐
│ Telemetry │
│ Buffer │ 2. Buffer events (max 10 or 5min)
│ Buffer │ 2. Buffer events (max 10 or configured batch interval)
└──────┬──────┘
│ 3. Flush batch every 5 minutes
│ 3. Flush on the configured batch interval
Comment thread
coderabbitai[bot] marked this conversation as resolved.
v
┌─────────────────────┐
Expand Down
16 changes: 8 additions & 8 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OilPriceAPI Python SDK - Real-time Oil & Commodity Price Data</title>
<meta name="description" content="Official Python SDK for real-time and historical oil, gas, and commodity price data. 98% less cost than Bloomberg Terminal. Free tier available.">
<title>OilPriceAPI Python SDK - Source-Timestamped Commodity Data</title>
<meta name="description" content="Official Python SDK for source-timestamped oil, gas, and commodity price data with typed responses and explicit source context.">
<meta name="keywords" content="python oil prices, commodity data python, oil price api, python energy data, brent crude python, wti python sdk">

<!-- Open Graph -->
<meta property="og:title" content="OilPriceAPI Python SDK">
<meta property="og:description" content="Real-time oil & commodity price data for Python developers">
<meta property="og:description" content="Source-timestamped oil and commodity data for Python developers">
<meta property="og:type" content="website">
<meta property="og:url" content="https://oilpriceapi.github.io/python-sdk/">

Expand Down Expand Up @@ -209,12 +209,12 @@
<!-- Hero -->
<div class="hero">
<h1>🛢️ OilPriceAPI Python SDK</h1>
<p class="tagline">Real-time oil & commodity price data for Python developers</p>
<p>Professional-grade API at <strong>98% less cost</strong> than Bloomberg Terminal</p>
<p class="tagline">Source-timestamped oil and commodity data for Python developers</p>
<p>Typed API access with explicit currency, unit, source, and timestamp context</p>

<div class="cta-buttons">
<a href="https://pypi.org/project/oilpriceapi/" class="btn btn-primary">Install from PyPI</a>
<a href="https://oilpriceapi.com/auth/signup" class="btn btn-secondary">Get Free API Key</a>
<a href="https://oilpriceapi.com/auth/signup" class="btn btn-secondary">Create API Key</a>
</div>

<pre><code>pip install oilpriceapi</code></pre>
Expand All @@ -239,8 +239,8 @@ <h1>🛢️ OilPriceAPI Python SDK</h1>
<!-- Features -->
<div class="features">
<div class="feature-card">
<h3>⚡ Real-Time Prices</h3>
<p>Latest spot prices for Brent, WTI, Natural Gas, Coal, and more. Updated every 15 minutes.</p>
<h3>⚡ Source-Timestamped Prices</h3>
<p>Latest available spot records include source and timestamp context for freshness decisions.</p>
</div>

<div class="feature-card">
Expand Down
46 changes: 15 additions & 31 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OilPriceAPI Python SDK Documentation

Welcome to the official Python SDK for [OilPriceAPI](https://oilpriceapi.com) - the most affordable way to access professional-grade oil and commodity price data.
Welcome to the official Python SDK for [OilPriceAPI](https://oilpriceapi.com), providing source-timestamped oil and commodity data.

## 🚀 Getting Started

Expand Down Expand Up @@ -33,9 +33,9 @@ print(f"Brent Crude: ${price.value:.2f}")

## 📚 Core Features

### Real-Time Price Data
Get the latest commodity prices updated every 5 minutes:
### Current Price Data

Get the latest available commodity prices with API-provided source timestamps:

```python
# Single commodity
Expand Down Expand Up @@ -119,7 +119,7 @@ prices = asyncio.run(get_all_prices())
## 🎯 Use Cases

### Energy Trading
Build algorithmic trading strategies with real-time price feeds and historical data for backtesting.
Build algorithmic trading strategies with current and historical data while retaining source timestamps for backtesting.

**[Explore trading examples →](https://oilpriceapi.com/use-cases/trading)**

Expand Down Expand Up @@ -213,31 +213,15 @@ commodity suggestions, plan or feature requirements, retry metadata, sanitized
response headers, and raw diagnostics remain available without exposing the
configured API key.

## 💰 Pricing & Plans

Choose the plan that fits your needs:

### Free Tier
- 1,000 API requests/month
- Real-time data
- No credit card required

**[Start free →](https://oilpriceapi.com/auth/signup)**

### Paid Plans
- **Developer**: $19/month - 10,000 requests
- **Starter**: $49/month - 50,000 requests (adds webhooks)
- **Professional**: $99/month - 100,000 requests (adds webhooks + WebSocket streaming)
- **Scale**: $299/month - 1,000,000 requests

**All plans include:**
- ✅ Real-time price updates every 5 minutes
- ✅ Historical data access
- ✅ 99.9% uptime SLA
- ✅ Email support
- ✅ No hidden fees

**[View detailed pricing →](https://oilpriceapi.com/pricing)**
## 💰 Access & Plans

Dataset access, allowances, and feature availability depend on the current
account entitlement. Review the [current pricing](https://oilpriceapi.com/pricing)
and the machine-readable [product facts](https://api.oilpriceapi.com/product-facts.json)
instead of relying on values bundled into an SDK release. API responses retain
the applicable source, observation timestamp, and limit metadata.

**[Create an API key →](https://oilpriceapi.com/auth/signup)**

## 🛠️ Development

Expand Down Expand Up @@ -298,7 +282,7 @@ MIT License - see [LICENSE](https://github.com/OilpriceAPI/python-sdk/blob/main/

---

**Ready to get started?** [Sign up for your free API key →](https://oilpriceapi.com/auth/signup)
**Ready to get started?** [Create an API key →](https://oilpriceapi.com/auth/signup)

**Questions?** [Contact our support team →](mailto:support@oilpriceapi.com)

Expand Down
2 changes: 1 addition & 1 deletion oilpriceapi/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def __init__(
# Agent watch subscriptions + event polling (#3245 Phase 2).
self.subscriptions = AsyncSubscriptionsResource(self)

# Real-time WebSocket streaming namespace (requires the [stream] extra).
# WebSocket price-update namespace (requires the [stream] extra).
# Lazily imports `websockets` only when a stream is actually opened.
from .streaming import AsyncStreamNamespace

Expand Down
26 changes: 11 additions & 15 deletions oilpriceapi/resources/diesel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ class DieselResource:
Provides access to state-level diesel price averages and station-level pricing.

Example:
>>> # Get state average (free tier)
>>> # Get the available state average
>>> price = client.diesel.get_price("CA")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
>>> print(f"California diesel: ${price.price:.2f}/gallon")

>>> # Get nearby stations (paid tiers)
>>> # Get nearby stations when enabled for the current account
>>> result = client.diesel.get_stations(lat=37.7749, lng=-122.4194)
>>> print(f"Found {len(result.stations)} stations")
"""
Expand All @@ -36,8 +36,8 @@ def __init__(self, client):
def get_price(self, state: str) -> DieselPrice:
"""Get average diesel price for a US state.

Returns EIA state-level average diesel price. This endpoint is free
and included in all tiers.
Returns the available EIA state-level average diesel price. Access and
request limits follow the account's current entitlement and API metadata.

Args:
state: Two-letter US state code (e.g., "CA", "TX", "NY")
Expand Down Expand Up @@ -105,15 +105,11 @@ def get_stations(

Returns station-level diesel prices within specified radius using Google Maps data.

**Tier Requirements:** Available on paid tiers (Exploration and above)

**Pricing Tiers:**
- Exploration: 100 station queries/month
- Starter: 500 station queries/month
- Professional: 2,000 station queries/month
- Business: 5,000 station queries/month

**Caching:** Results are cached for 24 hours to minimize costs.
Station-level access and allowances depend on the account's current
entitlement. Review https://www.oilpriceapi.com/pricing and the API's
response metadata instead of relying on SDK-bundled limits.

Use the returned source timestamp to apply the application's freshness policy.

Args:
lat: Latitude (-90 to 90)
Expand All @@ -126,8 +122,8 @@ def get_stations(
Raises:
ValidationError: If coordinates or radius are invalid
AuthenticationError: If API key is invalid
RateLimitError: If monthly station query limit exceeded (429)
OilPriceAPIError: If tier doesn't support station queries (403)
RateLimitError: If the API reports the request limit exceeded (429)
OilPriceAPIError: If the account cannot access station queries (403)

Example:
>>> # Get stations near San Francisco
Expand Down
Loading