Skip to content

Commit 4e7e8ec

Browse files
authored
Fix docs and installed-wheel claim drift (#84)
* fix: gate Python docs and wheel claims * test: scan every readable wheel surface * fix: remove residual packaged claim drift
1 parent 7f2338c commit 4e7e8ec

16 files changed

Lines changed: 380 additions & 126 deletions

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.12.2] - 2026-08-11
11+
12+
### Fixed
13+
14+
- Remove stale fixed plan-price, monthly allowance, cadence, uptime, and
15+
generic real-time claims from documentation and packaged docstrings.
16+
- Recursively validate authored docs and package source, then scan the exact
17+
installed wheel and PyPI metadata during the release smoke test.
18+
1019
## [1.12.1] - 2026-08-11
1120

1221
### Fixed

EXAMPLES.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

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

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

77
## 📊 Table of Contents
88

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

263263
## 💻 Web & Mobile Applications
264264

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

267267
Create an interactive web dashboard for monitoring oil prices.
268268

@@ -320,7 +320,7 @@ try:
320320
)
321321
st.plotly_chart(fig, use_container_width=True)
322322

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

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

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

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

622622
Ready to build with these examples?
623623

624-
1. **[Sign up for free](https://oilpriceapi.com/auth/signup)** - Get 50 requests/day
624+
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)
625625
2. **[Install the SDK](https://pypi.org/project/oilpriceapi/)** - `pip install oilpriceapi`
626626
3. **[Read the docs](https://docs.oilpriceapi.com/sdk/python)** - Complete API reference
627627
4. **[Choose a plan](https://oilpriceapi.com/pricing)** - Upgrade for more requests

docs/PERFORMANCE_GUIDE.md

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -231,23 +231,23 @@ while True:
231231
**Problems:**
232232
- Wastes API quota
233233
- Unnecessary load on API
234-
- Price only updates ~every 5 minutes
234+
- Ignores the record's API-provided source timestamp and freshness metadata
235235

236236
**Solution:**
237237
```python
238-
# Poll at reasonable interval
238+
# Choose an interval from API limits and the application's freshness need
239239
import time
240240

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

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

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

336336
**Basic In-Memory Cache:**
337337
```python
338-
from datetime import datetime, timedelta
339-
from functools import lru_cache
338+
from datetime import datetime, timedelta, timezone
340339

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

347-
# Cache key changes every 5 minutes
348344
def get_current_price(commodity):
349-
cache_key = int(datetime.now().timestamp() / 300)
350-
return get_cached_price(commodity, cache_key)
345+
cached = price_cache.get(commodity)
346+
if cached:
347+
source_age = datetime.now(timezone.utc) - cached["source_timestamp"]
348+
if source_age <= MAX_SOURCE_AGE:
349+
return cached["price"]
350+
351+
price = client.prices.get(commodity)
352+
price_cache[commodity] = {
353+
"price": price,
354+
"source_timestamp": price.timestamp,
355+
}
356+
return price
351357

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

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

359365
**Redis Cache (for multi-process):**
360366
```python
361367
import redis
362368
import json
363-
from datetime import timedelta
369+
from datetime import datetime, timedelta, timezone
370+
371+
from oilpriceapi.models import Price
364372

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

367377
def get_cached_price(client, commodity):
368-
"""Cache price in Redis for 5 minutes."""
369-
cache_key = f"oilprice:{commodity}"
378+
cache_key = f"oilprice:{commodity}:latest"
370379

371-
# Check cache
372380
cached = redis_client.get(cache_key)
373381
if cached:
374-
return json.loads(cached)
382+
payload = json.loads(cached)
383+
source_timestamp = datetime.fromisoformat(payload["source_timestamp"])
384+
if datetime.now(timezone.utc) - source_timestamp <= MAX_SOURCE_AGE:
385+
return Price.model_validate(payload["price"])
375386

376-
# Fetch from API
377387
price = client.prices.get(commodity)
378-
379-
# Cache for 5 minutes
388+
payload = {
389+
"price": price.model_dump(mode="json"),
390+
"source_timestamp": price.timestamp.isoformat(),
391+
}
380392
redis_client.setex(
381393
cache_key,
382-
timedelta(minutes=5),
383-
json.dumps(price.dict())
394+
int(MAX_SOURCE_AGE.total_seconds()),
395+
json.dumps(payload),
384396
)
385397

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

391403
**Good candidates for caching:**
392-
- Latest prices (updates every 5 minutes)
404+
- Latest prices, keyed by the API-provided source timestamp
393405
- Historical data (never changes)
394406
- Commodity metadata
395407
- Static reference data
396408

397409
**Don't cache:**
398-
- Real-time price updates (if using WebSocket)
410+
- Streamed price updates (when using WebSocket)
399411
- User-specific data
400412
- Data that changes frequently
401413

docs/TELEMETRY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,10 @@ Possible causes:
188188
v
189189
┌─────────────┐
190190
│ Telemetry │
191-
│ Buffer │ 2. Buffer events (max 10 or 5min)
191+
│ Buffer │ 2. Buffer events (max 10 or configured batch interval)
192192
└──────┬──────┘
193193
194-
│ 3. Flush batch every 5 minutes
194+
│ 3. Flush on the configured batch interval
195195
196196
v
197197
┌─────────────────────┐

docs/index.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>OilPriceAPI Python SDK - Real-time Oil & Commodity Price Data</title>
7-
<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.">
6+
<title>OilPriceAPI Python SDK - Source-Timestamped Commodity Data</title>
7+
<meta name="description" content="Official Python SDK for source-timestamped oil, gas, and commodity price data with typed responses and explicit source context.">
88
<meta name="keywords" content="python oil prices, commodity data python, oil price api, python energy data, brent crude python, wti python sdk">
99

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

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

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

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

246246
<div class="feature-card">

docs/index.md

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# OilPriceAPI Python SDK Documentation
22

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

55
## 🚀 Getting Started
66

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

3434
## 📚 Core Features
3535

36-
### Real-Time Price Data
37-
38-
Get the latest commodity prices updated every 5 minutes:
36+
### Current Price Data
37+
38+
Get the latest available commodity prices with API-provided source timestamps:
3939

4040
```python
4141
# Single commodity
@@ -119,7 +119,7 @@ prices = asyncio.run(get_all_prices())
119119
## 🎯 Use Cases
120120

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

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

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

216-
## 💰 Pricing & Plans
217-
218-
Choose the plan that fits your needs:
219-
220-
### Free Tier
221-
- 1,000 API requests/month
222-
- Real-time data
223-
- No credit card required
224-
225-
**[Start free →](https://oilpriceapi.com/auth/signup)**
226-
227-
### Paid Plans
228-
- **Developer**: $19/month - 10,000 requests
229-
- **Starter**: $49/month - 50,000 requests (adds webhooks)
230-
- **Professional**: $99/month - 100,000 requests (adds webhooks + WebSocket streaming)
231-
- **Scale**: $299/month - 1,000,000 requests
232-
233-
**All plans include:**
234-
- ✅ Real-time price updates every 5 minutes
235-
- ✅ Historical data access
236-
- ✅ 99.9% uptime SLA
237-
- ✅ Email support
238-
- ✅ No hidden fees
239-
240-
**[View detailed pricing →](https://oilpriceapi.com/pricing)**
216+
## 💰 Access & Plans
217+
218+
Dataset access, allowances, and feature availability depend on the current
219+
account entitlement. Review the [current pricing](https://oilpriceapi.com/pricing)
220+
and the machine-readable [product facts](https://api.oilpriceapi.com/product-facts.json)
221+
instead of relying on values bundled into an SDK release. API responses retain
222+
the applicable source, observation timestamp, and limit metadata.
223+
224+
**[Create an API key →](https://oilpriceapi.com/auth/signup)**
241225

242226
## 🛠️ Development
243227

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

299283
---
300284

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

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

oilpriceapi/async_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def __init__(
155155
# Agent watch subscriptions + event polling (#3245 Phase 2).
156156
self.subscriptions = AsyncSubscriptionsResource(self)
157157

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

oilpriceapi/resources/diesel.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ class DieselResource:
1616
Provides access to state-level diesel price averages and station-level pricing.
1717
1818
Example:
19-
>>> # Get state average (free tier)
19+
>>> # Get the available state average
2020
>>> price = client.diesel.get_price("CA")
2121
>>> print(f"California diesel: ${price.price:.2f}/gallon")
2222
23-
>>> # Get nearby stations (paid tiers)
23+
>>> # Get nearby stations when enabled for the current account
2424
>>> result = client.diesel.get_stations(lat=37.7749, lng=-122.4194)
2525
>>> print(f"Found {len(result.stations)} stations")
2626
"""
@@ -36,8 +36,8 @@ def __init__(self, client):
3636
def get_price(self, state: str) -> DieselPrice:
3737
"""Get average diesel price for a US state.
3838
39-
Returns EIA state-level average diesel price. This endpoint is free
40-
and included in all tiers.
39+
Returns the available EIA state-level average diesel price. Access and
40+
request limits follow the account's current entitlement and API metadata.
4141
4242
Args:
4343
state: Two-letter US state code (e.g., "CA", "TX", "NY")
@@ -105,15 +105,11 @@ def get_stations(
105105
106106
Returns station-level diesel prices within specified radius using Google Maps data.
107107
108-
**Tier Requirements:** Available on paid tiers (Exploration and above)
109-
110-
**Pricing Tiers:**
111-
- Exploration: 100 station queries/month
112-
- Starter: 500 station queries/month
113-
- Professional: 2,000 station queries/month
114-
- Business: 5,000 station queries/month
115-
116-
**Caching:** Results are cached for 24 hours to minimize costs.
108+
Station-level access and allowances depend on the account's current
109+
entitlement. Review https://www.oilpriceapi.com/pricing and the API's
110+
response metadata instead of relying on SDK-bundled limits.
111+
112+
Use the returned source timestamp to apply the application's freshness policy.
117113
118114
Args:
119115
lat: Latitude (-90 to 90)
@@ -126,8 +122,8 @@ def get_stations(
126122
Raises:
127123
ValidationError: If coordinates or radius are invalid
128124
AuthenticationError: If API key is invalid
129-
RateLimitError: If monthly station query limit exceeded (429)
130-
OilPriceAPIError: If tier doesn't support station queries (403)
125+
RateLimitError: If the API reports the request limit exceeded (429)
126+
OilPriceAPIError: If the account cannot access station queries (403)
131127
132128
Example:
133129
>>> # Get stations near San Francisco

0 commit comments

Comments
 (0)