Historical energy futures data for your analysis
Retrieve available Brent, WTI and natural-gas futures observations as JSON. Inspect the returned contract month and dates before joining a series to your risk model, dashboard or backtest.
Select the window with from and to. With no dates the endpoint returns the last 30 days, and a single request may span at most 730 days. History depth, contract availability and access depend on the dataset and your account: a date you can ask for is not a guarantee that every observation inside it exists.
First request: available Brent history
Start with the bare endpoint, which returns the last 30 days across every open contract, then narrow it with from, to and contract_month as shown below. Keep your API key on the server.
curl "https://api.oilpriceapi.com/v1/futures/brent/historical" \
-H "Authorization: Token YOUR_API_KEY"The response is keyed contracts, one entry per delivery month, each carrying contract_month, contract_code, total_days and a daily_data array. There is no top-level data array and no status field; errors arrive as an error object with a code, message and request_id.
At interval=daily each row is code, trading_date, open, high, low, close, settlement, volume, open_interest, change_percent, open_captured_at and close_captured_at. There is no date field and no price field — read trading_date and close or settlement. Any of these may be null where the observation was not captured; a null is not a zero price. Measured on Brent over the 730 days to 9 October 2026, 4,828 of 4,860 rows carried a close and 4,331 carried a settlement.
Choose your query
| Parameter | Supported values and behavior |
|---|---|
| from, to | ISO dates, e.g. from=2026-09-01&to=2026-10-09. Omit both and you get the last 30 days. A single request may span at most 730 days; wider ranges return 400 INVALID_PARAMETER, “Date range cannot exceed 730 days”. Page longer histories by stepping the window. |
| interval | daily, weekly or monthly; defaults to daily. hourly is not accepted here and returns 400. Note that weekly and monthly change the row shape — see below. |
| contract_month | One delivery month, e.g. 2026-12, to pin a single contract instead of receiving every open month. contracts is accepted as a synonym. Confirm the available identifiers in the current reference before pinning a series. |
Parameters outside this list are discarded without an error, so a typo returns somebody else’s question answered with 200. In particular period, days and contract have no effect on this endpoint: all of them leave you with the default 30-day window or every contract. Check date_range and the returned contract months in the response against what you asked for.
Aggregated intervals return a different row
weekly and monthly aggregate server-side and replace trading_date with a period range. A loader written against the daily shape will not find its date field:
# interval=daily
{ "code": "BRENT_FUTURES_2026_10", "trading_date": "2026-10-09",
"open": null, "close": null, "settlement": "104.39", "volume": null }
# interval=weekly
{ "period_start": "2026-01-05", "period_end": "2026-01-09",
"open": "60.89", "high": "63.87", "low": "59.79", "close": "63.02",
"avg_close": "61.68", "settlement": "63.02", "trading_days": 5 }total_records is the pre-aggregation count and does not shrink with the interval, so size the payload from the rows themselves rather than that field.
Check the series before backtesting
- Compare
date_rangein the response with the window you requested, and the first and lasttrading_datewith the history your model requires. - Inspect contract changes. Do not assume the result is a continuous, roll-adjusted or settlement-price series.
- Verify units, source cadence and missing values before combining futures with spot or physical-price observations.
- Handle authentication, entitlement and unavailable-data responses explicitly; an empty result is not a zero-valued market.