Lithium Hydroxide (LI-OH) - Per Metric Ton Price API in Python: Getting Started and Authentication
You need to quote, hedge, or reprice battery-grade Lithium Hydroxide on a per metric ton basis inside a Python workflow. By the end of this guide you’ll authenticate with Metals-API, fetch the latest LI-OH price, read the exact USD per metric ton field, convert to per‑kg if needed, and ship a small, production‑ready snippet that handles units, timestamps, caching, and weekend behavior.
What we’re building
We’ll wire up a single Metals-API call to retrieve the latest Lithium Hydroxide price using the LI-OH symbol, then:
- Extract the USD per metric ton price (USDLI-OH).
- Optionally derive per‑kg and per‑lb values for operations and SKU pricing.
- Add lightweight caching and timestamp checks to avoid redundant calls.
We’ll focus on the Latest Rates endpoint for reliability and speed, and briefly show how you’d move to historical/time‑series once you’re reading the right fields.
Before you start: authentication, symbol, and units
Metals-API authenticates with an API key via the access_key query parameter. If you don’t have one yet, create it here: Register. There is no free trial. If you need usage oversight and keys management, use the Metals Control Panel (MCP): MCP.
Confirm the symbol and unit for Lithium Hydroxide on the official symbols page: Symbols. Metals-API returns a per‑symbol native unit; for bulk industrials like LI-OH this is commonly per metric ton. Your code should not assume units—always validate them for your symbol.
For parameter details (endpoints, optional params, response schema), see: Documentation.
One-minute test: cURL request for the latest Lithium Hydroxide price
Run this from your terminal. Replace YOUR_API_KEY with your key.
curl "https://metals-api.com/api/latest?access_key=YOUR_API_KEY&symbols=LI-OH,USDLI-OH"
Official sample JSON you should expect to parse
Below is a real response for LI-OH. Use these exact fields when wiring your parser:
{"success":true,"timestamp":1790986140,"date":"2026-10-03","base":"USD","rates":{"LI-OH":6.0133721973094e-5,"USD":1,"USDLI-OH":16629.60427507607}}
What to read:
- base: "USD". All rates are expressed relative to 1 USD by default.
- rates["LI-OH"]: 6.0133721973094e-5 — this is “metric tons per 1 USD” (the inversion form).
- rates["USDLI-OH"]: 16629.60427507607 — this is “USD per 1 metric ton of LI-OH”. For quoting, invoicing, and P&L, this is the field you’ll typically use.
- timestamp: 1790986140 — Unix seconds (UTC). Store this alongside prices for auditability.
- date: "2026-10-03" — trading date aligned with the timestamp.
Note on units: the inversion pattern is consistent across symbols. With base=USD, a plain metal code (e.g., LI-OH) is “units per USD,” while the USD-prefixed cross (USDLI-OH) is “USD per unit.” Always verify a symbol’s native unit on the Symbols page.
Python: fetch, parse, and convert to per‑kg
The script below retrieves the latest LI-OH price, reads USD per metric ton from rates["USDLI-OH"], converts to per‑kg (divide by 1000), and demonstrates basic caching to avoid hammering the endpoint within the same minute.
import os
import time
import json
import math
import pathlib
import requests
from typing import Tuple, Optional
API_KEY = os.getenv("METALS_API_KEY", "YOUR_API_KEY")
BASE_URL = "https://metals-api.com/api/latest"
SYMBOLS = "LI-OH,USDLI-OH" # Read the inversion pair and the direct USD/ton price
# Simple file cache in /tmp (or current dir if /tmp not present)
CACHE_FILE = pathlib.Path("./li_oh_latest_cache.json")
CACHE_TTL_SECONDS = 55 # Keep < provider update cadence; adjust per your plan's update interval
def read_cache() -> Optional[dict]:
try:
if CACHE_FILE.exists():
age = time.time() - CACHE_FILE.stat().st_mtime
if age < CACHE_TTL_SECONDS:
with CACHE_FILE.open("r") as f:
return json.load(f)
except Exception:
pass
return None
def write_cache(payload: dict) -> None:
try:
with CACHE_FILE.open("w") as f:
json.dump(payload, f)
except Exception:
pass
def fetch_li_oh_latest() -> dict:
# Try cache first
cached = read_cache()
if cached:
return cached
params = {
"access_key": API_KEY,
"symbols": SYMBOLS,
}
resp = requests.get(BASE_URL, params=params, timeout=20)
resp.raise_for_status()
data = resp.json()
# Basic validation
if not data.get("success", False):
raise RuntimeError(f"Metals-API returned an error: {data}")
# Cache it
write_cache(data)
return data
def usd_per_metric_ton(data: dict) -> Tuple[float, int, str]:
base = data.get("base", "USD")
if base != "USD":
# This guide assumes base=USD as in the sample payloads.
# If you use a different base, adjust logic accordingly.
raise ValueError(f"Unexpected base: {base}")
rates = data.get("rates", {})
price_usd_per_ton = float(rates.get("USDLI-OH"))
ts = int(data.get("timestamp"))
iso_date = str(data.get("date"))
return price_usd_per_ton, ts, iso_date
def main():
data = fetch_li_oh_latest()
price_usd_per_ton, ts, iso_date = usd_per_metric_ton(data)
# If LI-OH is quoted per metric ton, per-kg is simply divide by 1000
price_usd_per_kg = price_usd_per_ton / 1000.0
# Optional: per-pound (1 kg = 2.20462262185 lb)
price_usd_per_lb = price_usd_per_kg / 2.20462262185
print(f"Date: {iso_date} (UTC ts={ts})")
print(f"USD per metric ton (LI-OH): {price_usd_per_ton:,.2f}")
print(f"USD per kg: {price_usd_per_kg:,.4f}")
print(f"USD per lb: {price_usd_per_lb:,.4f}")
if __name__ == "__main__":
main()
Swap YOUR_API_KEY for your actual key or export METALS_API_KEY in your environment. The code reads USDLI-OH as the canonical per‑ton price and uses a short file cache to reduce unnecessary calls. If your plan updates more frequently, shrink the TTL; if less frequently, expand it.
Reading the two LI-OH fields correctly (inversion explained)
With base=USD in Metals-API responses:
- rates["LI-OH"] means quantity of LI-OH per 1 USD. Numerically small numbers here are normal for per‑ton metals.
- rates["USDLI-OH"] means USD per 1 metric ton of LI-OH. This is the “sticker price” most commercial and risk systems use.
Both fields are complements: 1.0 / rates["LI-OH"] ≈ rates["USDLI-OH"] (subject to rounding). When in doubt, prefer USDLI-OH for pricing and reporting. If you ever change the base currency, revisit this logic—this guide assumes base=USD as in the sample.
Unit handling and conversions you’ll actually use
Actionable pointers for LI-OH workflows:
- Unit source of truth: confirm the unit for LI-OH on Symbols. If a unit field is present in your plan’s responses, log it alongside prices.
- Per‑kg: price_kg = price_ton / 1000.
- Per‑lb: price_lb = (price_ton / 1000) / 2.20462262185.
- Inventory valuation: multiply USD/ton by your on‑hand tons; account for purity/grade differentials outside the feed if needed.
If you track broader market context (EV demand, supply bottlenecks), the International Energy Agency’s lithium market resources can help contextualize moves: IEA on lithium.
Historical and time-series: backfill charts and signals
Once you’re reading USDLI-OH reliably, enable historical analytics with the historical date or time-series endpoints. These return daily historical rates between dates you specify. Keep the symbol set tight (e.g., LI-OH and USDLI-OH) to minimize payload size and speed up parsing. For endpoint parameters, date ranges, and limits by plan, see the Documentation.
Tips for time-series use:
- Weekend/holiday handling: some dates may have no updates. Code defensively by checking key presence for each date and forward/backfilling as your business logic requires.
- Cache daily series results and only refresh the most recent window (e.g., last 7 days) on each run.
- Pagination: the latest and typical time-series responses are not paginated. Keep date windows appropriate to your needs to avoid oversized responses.
Production details that save time
- Base currency: default is USD, as shown. If you must work in a different currency in downstream systems, convert using the Convert endpoint or read the corresponding USD-prefixed cross the same way you do for USDLI-OH. Review options in the Documentation.
- Timestamps/timezone: timestamp is Unix seconds, UTC. Persist the raw timestamp plus the date string for audit and reproducibility.
- Refresh cadence: the Latest endpoint updates by plan (e.g., every 60 minutes, every 10 minutes, etc.). Cache results at least until the next expected update to reduce costs and speed up your app.
- Error handling: treat success=false as a hard failure; retry with backoff and log the response body.
- Data gaps: on weekends or closures you may see the last valid quote carried forward. Decide whether to carry forward the last price or to suppress trading signals until a fresh timestamp arrives.
- Plan note: there is no free trial. If you only need Copper, the Copper Monthly plan is $19.99/mo; see pricing pages for details on broader coverage and update frequencies.
Quality checks: is your LI-OH integration correct?
- Field mapping: you are using rates["USDLI-OH"] (USD per metric ton) for any end‑user display and valuations.
- Inversion sanity: 1 / rates["LI-OH"] ≈ rates["USDLI-OH"]. Add a one-time assert in tests to catch mapping regressions.
- Unit awareness: your code references the symbol’s native unit from the Symbols page and stores it with each row.
- Caching: you cache within the provider’s update interval and tag each cache entry with timestamp+date.
- Resilience: you handle success=false and network exceptions with retries/backoff and observability (logs/metrics).
Appendix: endpoint recap used here
- Latest Rates: retrieve the most recent LI-OH and USDLI-OH fields for operational pricing and alerts.
- Historical/Time-Series: daily rates back to available history to build charts and signals. See parameters and date limits in the Documentation.
FAQ
Q: Which field is the actual “USD per metric ton” price for Lithium Hydroxide?
A: Use rates["USDLI-OH"]. With base=USD, rates["LI-OH"] is the inverse (tons per 1 USD). Confirm the unit on the Symbols page.
Q: How often does the latest LI-OH price update?
A: Update cadence depends on your plan (e.g., every 60 minutes, 10 minutes). Cache between expected updates. See the Documentation for endpoint behavior.
Q: Do I need to change the base to get USD per ton?
A: No. With base=USD (default), you can read USD per ton from rates["USDLI-OH"] directly. Changing base is optional and not required for USD pricing.
Q: What happens on weekends or holidays?
A: You may see the last valid timestamp carried. If you’re generating trading signals, consider pausing or carrying forward explicitly and labeling the price as “last.”
Q: Is there a free trial?
A: No. If you need an API key, sign up here: Register. For usage monitoring and key management, use MCP.
Ship it
Authenticate with an access key, call the Latest endpoint for LI-OH and USDLI-OH, and persist the USD per metric ton price with its UTC timestamp. Expand to time-series once your mapping is rock solid. Get your API key from Metals-API here: Register, check the exact symbol and units on Symbols, and review endpoint specifics in the Documentation.