Skip to content

Commit bca8064

Browse files
committed
feat: normalize API error contracts
1 parent 2592030 commit bca8064

9 files changed

Lines changed: 826 additions & 325 deletions

File tree

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,27 @@ except RateLimitError as error:
158158
except TimeoutError:
159159
print("Retry once, then check https://status.oilpriceapi.com.")
160160
except OilPriceAPIError as error:
161+
# Safe support context: do not log your API key or request headers.
162+
if error.request_id:
163+
print("Support request ID:", error.request_id)
161164
if error.status_code in (402, 403):
162-
print("Review dataset access for this account.")
165+
print(
166+
"Review dataset access for this account.",
167+
error.required_plan,
168+
error.required_feature,
169+
error.remediation_url,
170+
)
163171
else:
164172
raise
165173
```
166174

175+
All non-2xx responses share the same `OilPriceAPIError` attributes, including
176+
`status_code`, `code`, `request_id`, plan/feature recovery fields, retry
177+
metadata, sanitized response `headers`, `raw_body`, and `raw_text`. Canonical
178+
nested and legacy flat API error envelopes are normalized into that contract.
179+
Transport failures use `NetworkError`; timeouts remain the more specific
180+
`TimeoutError`.
181+
167182
Executable recovery examples cover 401, 403, 429, and timeout responses under
168183
[`examples/snippets/`](examples/snippets/). Empty or malformed successful
169184
responses should stop analysis rather than inventing a price, unit, currency,

docs/index.md

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -182,21 +182,29 @@ client = OilPriceAPI(
182182
### Error Handling
183183

184184
```python
185-
from oilpriceapi.exceptions import (
186-
OilPriceAPIError,
187-
RateLimitError,
188-
DataNotFoundError
189-
)
190-
191-
try:
192-
price = client.prices.get("BRENT_CRUDE_USD")
193-
except RateLimitError as e:
194-
print(f"Rate limited. Resets in {e.seconds_until_reset}s")
195-
except DataNotFoundError:
196-
print("Commodity not found")
197-
except OilPriceAPIError as e:
198-
print(f"API error: {e}")
199-
```
185+
from oilpriceapi.exceptions import (
186+
DataNotFoundError,
187+
OilPriceAPIError,
188+
RateLimitError,
189+
)
190+
191+
try:
192+
price = client.prices.get("BRENT_CRUDE_USD")
193+
except RateLimitError as error:
194+
print(f"Rate limited. Resets in {error.seconds_until_reset}s")
195+
except DataNotFoundError:
196+
print("Commodity not found")
197+
except OilPriceAPIError as error:
198+
if error.request_id:
199+
print("Support request ID:", error.request_id)
200+
if error.remediation_url:
201+
print("Recovery:", error.remediation_url)
202+
print(f"API error: {error}")
203+
```
204+
205+
Every non-2xx response uses this shared typed contract. `status_code`, `code`,
206+
plan or feature requirements, retry metadata, sanitized response headers, and
207+
raw diagnostics remain available without exposing the configured API key.
200208

201209
## 💰 Pricing & Plans
202210

oilpriceapi/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
from oilpriceapi.client import OilPriceAPI
1616
from oilpriceapi.exceptions import (
1717
AuthenticationError,
18+
BadRequestError,
1819
ConfigurationError,
1920
DataNotFoundError,
21+
NetworkError,
2022
OilPriceAPIError,
23+
PaymentRequiredError,
24+
PermissionDeniedError,
2125
RateLimitError,
2226
ServerError,
2327
TimeoutError,
@@ -49,11 +53,15 @@
4953
"OilPriceAPI",
5054
"AsyncOilPriceAPI",
5155
"OilPriceAPIError",
56+
"BadRequestError",
5257
"AuthenticationError",
58+
"PaymentRequiredError",
59+
"PermissionDeniedError",
5360
"RateLimitError",
5461
"DataNotFoundError",
5562
"ServerError",
5663
"ValidationError",
64+
"NetworkError",
5765
"TimeoutError",
5866
"ConfigurationError",
5967
"DieselPrice",

0 commit comments

Comments
 (0)