How to get live gold prices in Python

Call the goldprice.dev API from Python with requests, Decimal arithmetic, retry-on-429, and a 60-second TTL cache.

Updated

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 1,000 calls/month, no credit card. Tested April 2026 against Python 3.12 + requests 2.32.

  1. 1.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 goldprice_live_. Set it as an environment variable:

    export GOLDPRICE_API_KEY=goldprice_live_replace_with_your_key_here

    The free tier includes 1,000 calls/month and 13 supported currencies. No credit card required.

    Get your free API key1,000 calls/mo, no credit card
    Sign up free →
  2. 2.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:

    BASH · shell
    # pip
    pip install requests cachetools tenacity
    
    # or uv (recommended)
    uv add requests cachetools tenacity
  3. 3.Write 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
    # python: goldprice.py
    # curl-equivalent: https://api.goldprice.dev/v1/spot/XAU-USD-SPOT
    import os
    from decimal import Decimal
    from typing import TypedDict
    
    import requests
    from cachetools import TTLCache, cached
    from 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  # seconds
    
    
    class SpotResponse(TypedDict):
        symbol: str
        quote_currency: str
        price: Decimal
        bid: Decimal
        ask: Decimal
        is_stale: bool
        computed_at: str
    
    
    class 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"]),
            "ask": Decimal(data["ask"]),
            "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']})")
  4. 4.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.

    BASH · shell
    python goldprice.py
    # XAU-USD: 4726.01 (computed 2026-04-27T04:49:01.706844+00:00)
  5. 5.Switch currency or metal

    Symbols are METAL-CURRENCY-SPOT. Pass any symbol from the 13 supported 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.

    PYTHON · goldprice.py · multi-currency
    # USD spot — default
    spot("XAU-USD-SPOT")
    
    # INR — Indian gold market (largest retail gold-search globally)
    spot("XAU-INR-SPOT")
    
    # EUR — European pricing surfaces
    spot("XAU-EUR-SPOT")
    
    # Silver in USD
    spot("XAG-USD-SPOT")

Expected output

The API returns this shape:

JSON · GET /v1/spot/XAU-USD-SPOT
{
  "symbol": "XAU",
  "quote_currency": "USD",
  "unit": "troy_ounce",
  "contract_type": "spot",
  "price": "4726.01",
  "bid": "4726.68",
  "ask": "4725.33",
  "is_stale": false,
  "divergence_flag": false,
  "computed_at": "2026-04-27T04:49:01.706844+00:00",
  "sources": [
    {
      "source": "wgc.fsapi.usd",
      "display_name": "World Gold Council (live spot)",
      "price": "4726.01",
      "is_stale": false,
      "timestamp": "2026-04-27T04:47:24+00:00"
    }
    /* + cmc.paxg + cmc.xaut entries — full sources[] in live response */
  ],
  "value_stale": false,
  "price_gram_24k": "151.9447",
  "open_price": "4681.305",
  "high_price": "4729.56",
  "low_price": "4672.927",
  "prev_close_price": "4681.302",
  "ch": "44.6260",
  "chp": "0.9533",
  "open_time": 1777248000
}

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

CodeSymptomFix
401requests.exceptions.HTTPError: 401 Client ErrorAPI 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.
429Three RateLimited retries fail with backoff, then raises outYou 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 Basic and Pro tiers.
N/AFloat drift accumulates when summing many pricesAlways 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.

FAQ

How often does the price update?

The spot endpoint refreshes every 60 seconds (Pyth + WGC + CME aggregation). The TTLCache above matches that cadence — repeated spot() calls within 60s return the cached Decimal without an HTTP round-trip.

Will this fit the free tier with auto-refresh?

Depends on your call site. A trading bot polling once per minute = 1,440 calls/day = ~43,000/month — well over the 1,000/month free tier. Use the cache aggressively and call only on demand. Steady polling at 1 call/hour = 720/month, inside the free tier. For continuous high-volume use, see /pricing for Basic and Pro tiers.

Can I use this commercially?

The Free tier is for personal use. For commercial use (apps you ship, services you sell, internal production systems), upgrade to Basic or Pro — see /pricing.

Why Decimal instead of float?

Float arithmetic drifts on summation: 0.1 + 0.2 == 0.30000000000000004. Gold prices are reported with 6+ decimal places (e.g. 4726.0100); summing thousands of trades in float introduces basis-point errors over time. Decimal preserves exact arithmetic. The API returns prices as strings precisely so clients can choose Decimal.

What metals are supported?

Gold (XAU), silver (XAG), copper (HG), platinum (XPT), palladium (XPD). Substitute the metal code in the symbol, e.g. spot("XAG-USD-SPOT").

Is the free tier really free?

Yes — 1,000 calls/month, no credit card, no expiration. Get the key at /onboarding.

Can I have Claude write this for me?

Yes. Open Claude Desktop with the goldprice.dev MCP server installed and ask: Build a Python client for the goldprice.dev /v1/spot endpoint. Use requests, Decimal, a 60-second cachetools TTLCache, and tenacity retry-on-429 with exponential backoff. Read the API key from GOLDPRICE_API_KEY. The MCP server gives Claude the live schema, so generated code uses correct field names on first try.

Going further

  • Persist the cache across processes — replace TTLCache with a Redis-backed cache so multiple workers share one in-memory price
  • Add a history() wrapper for /v1/history/XAU-USD-SPOT?days=30 with pandas DataFrame conversion for backtesting
  • Switch requests for httpx.AsyncClient if 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

Next steps

Try the same setup in a different platform:

Browse all 6 tutorials →