Crack spread API in Python: 3-2-1 and European gasoil
By OilPriceAPI · API docs checked November 5, 2026
A crack spread compares a crude input with refined-product outputs. The 3-2-1 model represents three barrels of crude yielding two barrels of gasoline and one barrel of distillate. The European gasoil crack is a different benchmark: ICE Low Sulphur Gasoil against ICE Brent. Use the API's named calculation instead of subtracting quotes that have different physical units.
Two endpoints, two response shapes
| Calculation | Path | Read from |
|---|---|---|
| WTI 3-2-1 | /v1/spreads/crack?code=WTI_321_CRACK | A row in the data array; check period |
| European gasoil | /v1/spreads/gasoil-crack | The data object; check timestamp and both legs |
Gasoil is quoted in USD per tonne and Brent in USD per barrel. The European endpoint uses the exchange convention of 7.45 barrels per tonne before subtracting Brent. Each futures leg names its own contract month; they can differ around a roll. The gasoil crack reference explains the formula and source bounds.
Run the Python example
Set OILPRICEAPI_KEY, save this as crack_spreads.pyand run python3 crack_spreads.py. Calculated metrics are available during an active trial and on Developer or higher. A Free-plan key returns 403; if a source leg has no recent price, the gasoil endpoint returns 404. The example gives a next action for each instead of turning it into a zero spread.
import json
import math
import os
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
base = "https://api.oilpriceapi.com"
key = os.environ.get("OILPRICEAPI_KEY")
if not key:
raise SystemExit("Set OILPRICEAPI_KEY before running this example")
def get(path):
request = Request(base + path, headers={"Authorization": "Token " + key})
try:
with urlopen(request, timeout=10) as response:
payload = json.load(response)
except HTTPError as error:
if error.code == 401:
raise SystemExit("Check OILPRICEAPI_KEY; this request was not authorized")
if error.code == 402:
raise SystemExit("Check your plan and remaining request allowance")
if error.code == 403:
raise SystemExit("Calculated spreads need an active trial or paid plan")
if error.code == 404:
print(path, "has no recent source data; try again after the market updates")
return None
if error.code == 429:
raise SystemExit("Rate limited; wait before retrying")
raise SystemExit(f"Spread API request failed with HTTP {error.code}") from error
except URLError as error:
raise SystemExit("API connection failed; retry after checking the network") from error
if not isinstance(payload, dict) or payload.get("status") != "success":
raise RuntimeError("Spread API did not return a usable result")
return payload.get("data")
cracks = get("/v1/spreads/crack?code=WTI_321_CRACK")
crack_row = None
if isinstance(cracks, list):
for row in cracks:
if not isinstance(row, dict) or row.get("code") != "WTI_321_CRACK":
continue
value = row.get("value")
if (isinstance(value, (int, float)) and not isinstance(value, bool)
and math.isfinite(value) and row.get("period")
and row.get("unit") and row.get("currency")):
crack_row = row
break
if crack_row is None:
print("WTI 3-2-1 unavailable: check source data and response fields")
else:
print("WTI 3-2-1", crack_row["value"], crack_row["currency"],
"per", crack_row["unit"], "for", crack_row["period"])
gasoil = get("/v1/spreads/gasoil-crack")
if isinstance(gasoil, dict):
value = gasoil.get("value")
legs = gasoil.get("components") or {}
product = legs.get("product") if isinstance(legs, dict) else None
crude = legs.get("crude") if isinstance(legs, dict) else None
if (isinstance(value, (int, float)) and not isinstance(value, bool)
and math.isfinite(value) and gasoil.get("timestamp")
and gasoil.get("unit") and isinstance(product, dict)
and isinstance(crude, dict) and product.get("code")
and crude.get("code") and product.get("contract_month")
and crude.get("contract_month")):
print("European gasoil crack", value, gasoil["unit"],
"as of", gasoil["timestamp"])
print("product", product["code"], product["contract_month"])
print("crude", crude["code"], crude["contract_month"])
else:
print("European gasoil crack unavailable: inspect both source legs")
else:
print("European gasoil crack unavailable: inspect source response")
Check each response's date before charting it, and keep the gasoil leg contract months beside the calculated value. The spreads API reference covers access, historical coverage and data rights. Public display or redistribution of derived analytics requires review.
Explore the spread
See the current market context, then choose the API access that fits your use.