OilPriceAPI guides

Diesel price API in Python: retail, ULSD and gasoil without mixing units

By OilPriceAPI · API contract and public series checked November 5, 2026

A diesel number needs a market, unit and source date. Texas retail, German retail, US wholesale ULSD and ICE gasoil are different series. Putting them in one chart without those labels makes a valid response misleading. This example checks all four in one call.

Choose a series

SeriesCodeBasis
Texas retailDIESEL_RETAIL_STATE_TX_USDUSD/gallon
Germany retailDIESEL_RETAIL_DE_EUREUR/litre
US wholesale ULSDULSD_DIESEL_USDUSD/gallon
ICE gasoilGASOIL_USDUSD/tonne

DIESEL_USD is a separate distillate market signal, neither a Texas pump average nor the named ULSD assessment. The diesel API guide explains the product and other supported codes.

Run one authenticated request

Set OILPRICEAPI_KEY in your environment, save this as diesel_prices.py, then run python3 diesel_prices.py. The latest-prices API puts current batch rows in data.pricesand unavailable or stale rows in data.missing.

import json
import math
import os
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen

codes = [
    "DIESEL_RETAIL_STATE_TX_USD",  # USD/gallon, Texas retail
    "DIESEL_RETAIL_DE_EUR",        # EUR/litre, Germany retail
    "ULSD_DIESEL_USD",             # USD/gallon, wholesale ULSD
    "GASOIL_USD",                  # USD/tonne, ICE gasoil
]
query = urlencode({"by_code": ",".join(codes)})
key = os.environ.get("OILPRICEAPI_KEY")
if not key:
    raise SystemExit("Set OILPRICEAPI_KEY before running this example")
request = Request(
    "https://api.oilpriceapi.com/v1/prices/latest?" + query,
    headers={"Authorization": "Token " + key},
)
try:
    with urlopen(request, timeout=10) as response:
        payload = json.load(response)
except HTTPError as error:
    if error.code in (401, 403):
        raise SystemExit("Check the API key and access to these diesel series") from error
    if error.code == 402:
        raise SystemExit("Check your plan's access to these diesel series") from error
    if error.code == 429:
        raise SystemExit("Request limit reached; wait before retrying") from error
    raise SystemExit(f"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 payload.get("status") != "success" or not isinstance(payload.get("data"), dict):
    raise RuntimeError("No usable diesel response")
data = payload["data"]
prices = data.get("prices")
missing = data.get("missing", [])
if not isinstance(prices, list) or not isinstance(missing, list):
    raise RuntimeError("Unexpected diesel batch response shape")
seen = set()
for quote in prices:
    if not isinstance(quote, dict) or quote.get("code") not in codes:
        continue
    seen.add(quote["code"])
    if quote.get("stale") or quote.get("data_status") != "current":
        print(quote["code"], "stale:", quote.get("as_of"))
        continue
    price = quote.get("price")
    if (not isinstance(price, (int, float)) or isinstance(price, bool)
            or not math.isfinite(price) or not quote.get("as_of")
            or not quote.get("unit") or not quote.get("currency")):
        print(quote["code"], "invalid price or missing source details")
        continue
    print(quote["code"], quote["price"], quote["currency"],
          "per", quote["unit"], "as of", quote["as_of"])
for omitted in missing:
    if isinstance(omitted, dict) and omitted.get("code") in codes:
        seen.add(omitted["code"])
        print(omitted["code"], "unavailable:", omitted.get("reason"))
for code in codes:
    if code not in seen:
        print(code, "unavailable: no price or missing reason returned")

A 200 response alone does not mean fresh data. On November 5, the public Germany series had a September 7 source date and a stale status. Check as_of, unit and freshness before using a quote in a fuel-cost model. The latest-prices reference documents the batch and stale-row behavior.

Next step

Start with a free key and the series your application needs. Check current plan and dataset access before production use.