From 46df27b475cb6ea8b97c7246bca277caab6c4db8 Mon Sep 17 00:00:00 2001 From: Karl Waldman Date: Sun, 23 Aug 2026 08:13:18 -0400 Subject: [PATCH 1/2] docs: default polling interval now fits the free plan (api#7235, api#7240) Both recommended intervals in the SDK docs exceeded our own free plan: EXAMPLES.md time.sleep(300) -> 288/day = 5.8x the 50/day allowance PERFORMANCE_GUIDE.md time.sleep(300) -> same, and it is the "Solution" block Defaults are now 1800 (30 min = 48 requests/day, fits free), with an inline note to drop to 300 on Developer and above. PERFORMANCE_GUIDE gains a plan/interval table and the measured update cadence (BRENT ~2.5 min, WTI and natural gas ~5 min, refined products ~twice a day, from the prices table over 7 days), so the interval is chosen from evidence rather than habit. Above Developer, extra quota is better spent on more codes than a shorter timer - the data has no more resolution to give. Also flags api#7240 where a reader will trip over it: get_multiple() currently issues one HTTP request PER CODE (prices.py:96-99 loops over self.get), so it costs the same as a manual loop while the REST API accepts 20 codes in one request that counts once. Verified against production today. NOT changed after reading the context: PERFORMANCE_GUIDE.md:228 time.sleep(1) - explicitly labelled an anti-pattern EXAMPLES.md was patched in binary to preserve its 640 CRLF line endings; a formatter had otherwise normalised the whole file and turned an 8-line change into a 1,286-line diff. Refs api#7235, api#7240 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JKAExynd9zoKwt6rYA66EA --- EXAMPLES.md | 8 ++++++-- docs/PERFORMANCE_GUIDE.md | 26 ++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 3b33b28..271ce73 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -456,8 +456,12 @@ def monitor_prices(): elif price.value < limits['low']: send_alert(commodity, price.value, limits['low'], 'BELOW') - # Example caller-selected interval; honor API limit and freshness metadata. - time.sleep(300) + # 30 minutes fits the free plan: 48 requests/day against a 50/day + # allowance. On Developer and above use 300 (5 minutes), which matches + # how often spot prices actually change - nothing we publish moves + # faster than ~2.5 minutes, so a shorter timer returns the same number. + # See https://docs.oilpriceapi.com/guides/rate-limiting#how-often-to-poll + time.sleep(1800) if __name__ == '__main__': print("Starting price monitor...") diff --git a/docs/PERFORMANCE_GUIDE.md b/docs/PERFORMANCE_GUIDE.md index 421446e..63ac3d8 100644 --- a/docs/PERFORMANCE_GUIDE.md +++ b/docs/PERFORMANCE_GUIDE.md @@ -235,15 +235,37 @@ while True: **Solution:** ```python -# Choose an interval from API limits and the application's freshness need +# Match the interval to your plan AND to how fast the data moves. import time while True: price = client.prices.get("WTI_USD") print(f"WTI: ${price.value}") - time.sleep(300) # Example client-selected interval + time.sleep(1800) # 30 min -> 48 requests/day, fits the free plan + # Developer and above: 300 (5 minutes) ``` +**Pick the interval from your plan:** + +| Plan | Quota | Poll every | Requests/day | +| --- | --- | --- | --- | +| Free | 50 / day | **30 minutes** | 48 | +| Developer | 10,000 / month | 5 minutes | 288 | +| Starter | 50,000 / month | 5 minutes | 1,440 | +| Professional+ | 100,000+ / month | 5 minutes | 2,880 | + +Polling faster than the data changes cannot return new information. Measured +over a week: `BRENT_CRUDE_USD` updates about every 2.5 minutes, `WTI_USD` and +`NATURAL_GAS_USD` about every 5, and refined products such as `DIESEL_USD` +about twice a day. Above Developer, extra quota is better spent on more +commodity codes than on a shorter timer. + +⚠️ **`get_multiple()` currently issues one HTTP request per code**, so it +consumes quota per commodity rather than per call. Until that is batched +(api#7240), a loop over `get()` and a call to `get_multiple()` cost the same. +The REST API itself accepts up to 20 codes in a single request that counts +once — see the rate-limiting guide if you are close to your quota. + **Better Solution (for streamed updates):** ```python # Use WebSocket streaming when the account is entitled to it. From 80f2fd6885f4d83f3da126dbaff36da7955f0b17 Mon Sep 17 00:00:00 2001 From: Karl Waldman Date: Sun, 23 Aug 2026 08:16:01 -0400 Subject: [PATCH 2/2] docs: keep polling guidance contract-driven --- EXAMPLES.md | 8 +++----- docs/PERFORMANCE_GUIDE.md | 28 +++++++++------------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 271ce73..9ad6a3b 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -456,12 +456,10 @@ def monitor_prices(): elif price.value < limits['low']: send_alert(commodity, price.value, limits['low'], 'BELOW') - # 30 minutes fits the free plan: 48 requests/day against a 50/day - # allowance. On Developer and above use 300 (5 minutes), which matches - # how often spot prices actually change - nothing we publish moves - # faster than ~2.5 minutes, so a shorter timer returns the same number. + # Example caller-selected interval. Derive production polling from the + # account's current limit/reset response and the source timestamps. # See https://docs.oilpriceapi.com/guides/rate-limiting#how-often-to-poll - time.sleep(1800) + time.sleep(1800) if __name__ == '__main__': print("Starting price monitor...") diff --git a/docs/PERFORMANCE_GUIDE.md b/docs/PERFORMANCE_GUIDE.md index 63ac3d8..6f7f7c7 100644 --- a/docs/PERFORMANCE_GUIDE.md +++ b/docs/PERFORMANCE_GUIDE.md @@ -235,36 +235,26 @@ while True: **Solution:** ```python -# Match the interval to your plan AND to how fast the data moves. +# Match the interval to the account limit and how fast the data moves. import time while True: price = client.prices.get("WTI_USD") print(f"WTI: ${price.value}") - time.sleep(1800) # 30 min -> 48 requests/day, fits the free plan - # Developer and above: 300 (5 minutes) + # Example interval; derive this from current limit/reset metadata. + time.sleep(1800) ``` -**Pick the interval from your plan:** - -| Plan | Quota | Poll every | Requests/day | -| --- | --- | --- | --- | -| Free | 50 / day | **30 minutes** | 48 | -| Developer | 10,000 / month | 5 minutes | 288 | -| Starter | 50,000 / month | 5 minutes | 1,440 | -| Professional+ | 100,000+ / month | 5 minutes | 2,880 | - -Polling faster than the data changes cannot return new information. Measured -over a week: `BRENT_CRUDE_USD` updates about every 2.5 minutes, `WTI_USD` and -`NATURAL_GAS_USD` about every 5, and refined products such as `DIESEL_USD` -about twice a day. Above Developer, extra quota is better spent on more -commodity codes than on a shorter timer. +Choose the interval from the current account response and the source timestamps +returned with the data. Polling faster than the source changes cannot return new +information, while a hard-coded schedule can exceed an account's current +allowance when product limits change. ⚠️ **`get_multiple()` currently issues one HTTP request per code**, so it consumes quota per commodity rather than per call. Until that is batched (api#7240), a loop over `get()` and a call to `get_multiple()` cost the same. -The REST API itself accepts up to 20 codes in a single request that counts -once — see the rate-limiting guide if you are close to your quota. +See the rate-limiting guide for the current REST batching contract if you are +close to your quota. **Better Solution (for streamed updates):** ```python