To get live gold prices in Python, send a GET to api.goldprice.dev/v1/spot/XAU-USD-SPOT with your API key in the X-API-Key header using requests. Wrap the call in cachetools.TTLCache for a 60-second cache and use Decimal for price math. The free tier covers 5,000 calls/month, no credit card. Tested October 2026 against Python 3.15 + requests 2.34.
Get your goldprice.dev API key
Sign up for a free account at goldprice.dev/onboarding. After email confirmation, your dashboard shows a key starting with ga_live_. Set it as an environment variable:
export GOLDPRICE_API_KEY=ga_live_replace_with_your_key_hereThe free tier includes 5,000 calls/month and gold spot in 31 currencies. No credit card required.
Install dependencies
You need three packages: requests for the HTTP call, cachetools for the in-process TTL cache, and tenacity for retry-on-429. Install with pip or uv:
# pippip install requests cachetools tenacity# or uv (recommended)uv add requests cachetools tenacityWrite the goldprice client
Save this as goldprice.py. The spot() function caches per symbol for 60 seconds, retries up to 3 times on 429 with exponential backoff, and returns prices as Decimal so downstream math does not drift from float imprecision.
# python: goldprice.py# curl-equivalent: https://api.goldprice.dev/v1/spot/XAU-USD-SPOTimport osfrom decimal import Decimalfrom typing import TypedDictimport requestsfrom cachetools import TTLCache, cachedfrom tenacity import ( retry, retry_if_exception_type, stop_after_attempt, wait_exponential,)API_BASE = "https://api.goldprice.dev"API_KEY = os.environ["GOLDPRICE_API_KEY"]TIMEOUT = 5 # secondsclass SpotResponse(TypedDict): symbol: str quote_currency: str price: Decimal bid: Decimal | None # only on plans that include bid and ask ask: Decimal | None is_stale: bool computed_at: strclass RateLimited(Exception): """Raised on HTTP 429 so tenacity retries with backoff."""@cached(cache=TTLCache(maxsize=128, ttl=60))@retry( retry=retry_if_exception_type(RateLimited), wait=wait_exponential(multiplier=1, min=2, max=30), stop=stop_after_attempt(3), reraise=True,)def spot(symbol: str = "XAU-USD-SPOT") -> SpotResponse: """Fetch live spot price. Cached 60s per symbol, retries 3x on 429.""" resp = requests.get( f"{API_BASE}/v1/spot/{symbol}", headers={"X-API-Key": API_KEY}, timeout=TIMEOUT, ) if resp.status_code == 429: raise RateLimited(resp.text) resp.raise_for_status() data = resp.json() return { "symbol": data["symbol"], "quote_currency": data["quote_currency"], "price": Decimal(data["price"]), "bid": Decimal(data["bid"]) if data.get("bid") else None, "ask": Decimal(data["ask"]) if data.get("ask") else None, "is_stale": data["is_stale"], "computed_at": data["computed_at"], }if __name__ == "__main__": r = spot("XAU-USD-SPOT") print(f"{r['symbol']}-{r['quote_currency']}: {r['price']} (computed {r['computed_at']})")Run it
From the same shell where you exported GOLDPRICE_API_KEY, run the file. The first call hits the API; calls within 60 seconds return the cached value instantly. Repeated runs from a fresh process miss the cache (in-process only); persist to Redis if you need cross-process caching.
python goldprice.py# XAU-USD: 4012.75 (computed 2026-10-23T07:00:18.221118+00:00)Switch currency or metal
Symbols are METAL-CURRENCY-SPOT. Pass any symbol from the 31 spot currencies to spot(). Each unique symbol caches independently in TTLCache, so a multi-currency dashboard polling 5 symbols at 60s intervals fires 5 API calls/minute uncached and 0 cached.
# USD spot, defaultspot("XAU-USD-SPOT")spot("XAU-INR-SPOT")spot("XAU-EUR-SPOT")# Silver in USDspot("XAG-USD-SPOT")Expected output
{ "symbol": "XAU", "quote_currency": "USD", "unit": "troy_ounce", "contract_type": "spot", "price": "4012.75", "is_stale": false, "computed_at": "2026-10-23T07:00:18.221118+00:00"}Stdout shows one line: symbol, quote_currency, price as Decimal, and the ISO 8601 computed_at timestamp. The TypedDict response is what your downstream code consumes.
Common errors
| Code | What you see | Fix |
|---|---|---|
| 401 | requests.exceptions.HTTPError: 401 Client Error | API key missing or invalid. Confirm GOLDPRICE_API_KEY is set: echo $GOLDPRICE_API_KEY. If empty, re-export from your dashboard. The client raises on missing env at import time, not at call time, so the failure surfaces on the first import goldprice. |
| 429 | Three RateLimited retries fail with backoff, then raises out | You exhausted the per-minute cap. The 60-second cache prevents this for steady polling; if a burst of distinct symbols hits at once, raise TTLCache.maxsize or stagger calls. For sustained high-volume use, see /pricing for paid plans. |
| N/A | Float drift accumulates when summing many prices | Always pass Decimal through arithmetic, never float. The client returns prices as Decimal; if you cast to float for JSON serialization, use str(price) instead so precision is preserved across the wire. |
Going further
- Persist the cache across processes, replace
TTLCachewith a Redis-backed cache so multiple workers share one in-memory price - Add a
bars()wrapper for/v1/bars?symbol=XAU-USD-SPOT&interval=1d&from=...&to=...with pandas DataFrame conversion for backtesting - Switch
requestsforhttpx.AsyncClientif your service is async, the response shape is identical - Wire the client into a FastAPI endpoint and serve internally for non-Python services
- Expose Prometheus metrics for cache hit rate, p50/p95 latency, and 429-retry counts