Look up an oil well by API number in Python
By OilPriceAPI · Product and API reference checked November 5, 2026
An API number identifies a well. A lookup can return the monthly production filings held for that identifier; a missing record does not prove the well does not exist. Keep API numbers as text so leading zeroes survive.
Run the lookup
Set OILPRICEAPI_KEY, save this as well_lookup.py, and run python3 well_lookup.py. The example uses a documented Texas sample identifier and the authenticated /v1/well-production/wells/:api_numberendpoint. Check your account's dataset access before relying on it. A public browser lookup is available for exploration.
import json
import math
import os
import re
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
raw_number = "42-161-00101-00-00"
if not re.fullmatch(r"[0-9-]+", raw_number):
raise SystemExit("API number must contain digits and optional dashes")
api_number = re.sub(r"[^0-9]", "", raw_number)
if len(api_number) not in (10, 12, 14):
raise SystemExit("Use an API-10, API-12, or API-14 identifier")
api_number = api_number.ljust(14, "0")
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/well-production/wells/" + api_number,
headers={"Authorization": "Token " + key},
)
try:
with urlopen(request, timeout=10) as response:
payload = json.load(response)
except HTTPError as error:
if error.code == 404:
raise SystemExit("No production record held for this identifier; check the regulator")
if error.code in (401, 403):
raise SystemExit("Check the API key and well-production access for this account")
if error.code in (402, 429):
raise SystemExit("Quota or rate limit reached; check account limits before retrying")
raise SystemExit(f"Production lookup failed with HTTP {error.code}") from error
except URLError:
raise SystemExit("Lookup unavailable; retry later without treating it as no record")
record = payload.get("data") if isinstance(payload, dict) else None
if not isinstance(payload, dict) or payload.get("status") != "success" or not isinstance(record, dict):
raise SystemExit("Lookup returned no usable production record")
if record.get("api_number") != api_number:
raise SystemExit("Lookup returned a different API number; do not use it")
months = record.get("data")
if not isinstance(months, list) or not months:
raise SystemExit("No monthly filings returned; do not infer zero production")
valid = [row for row in months if isinstance(row, dict) and
isinstance(row.get("period"), str) and
re.fullmatch(r"[0-9]{4}-(0[1-9]|1[0-2])", row["period"]) and
isinstance(row.get("source"), str) and row["source"]]
if not valid:
raise SystemExit("Monthly filings have no usable source period")
latest_period = max(row["period"] for row in valid)
matches = [row for row in valid if row["period"] == latest_period]
if len(matches) != 1:
raise SystemExit("Latest period has duplicate filings; verify with the regulator")
latest = matches[0]
for field in ("oil_bbl", "gas_mcf", "water_bbl"):
value = latest.get(field)
if value is not None and (isinstance(value, bool) or
not isinstance(value, (int, float)) or
not math.isfinite(value) or value < 0):
raise SystemExit(field + " is malformed; do not use this filing")
print({"api_number": record.get("api_number"),
"operator": record.get("operator"),
"latest_period": latest["period"], "source": latest.get("source"),
"oil_bbl": latest.get("oil_bbl"), "gas_mcf": latest.get("gas_mcf"),
"water_bbl": latest.get("water_bbl")})
The script preserves a reported zero and a missing value as different states. It prints the latest returned period and source, rather than calling the record current based on the request date. A 401, quota response, or network failure does not become a false "no well" result. Permit records are a separate dataset.
Production is different from a permit
A permit says a regulator authorized an activity; it does not establish monthly oil or gas production. For recent permits, use the bounded /v1/ei/well-permits/preview endpoint or review the Scale-plan permit feed. Join the two datasets only after checking the identifier, state, source, and event date. The permit API reference explains authentication and response conventions.