Silver price in Python
Fetch XAG/USD spot and daily bars in Python with Decimal, auth, and freshness checks.
Updated
Use goldprice.dev to fetch silver with GET https://api.goldprice.dev/v1/spot/XAG-USD-SPOT and a Pro or Realtime Pro API key in the Bearer header. Parse price strings as Decimal, reject stale quotes, and request daily history from /v1/bars. The example below keeps authentication in an environment variable, converts troy ounces to grams, and handles 401, 403, and 429 responses.
1.Create a Pro-tier API key
Silver spot and silver history are available on Pro and Realtime Pro. Store the key outside your source code:
export GOLDPRICE_API_KEY=ga_live_replace_with_your_keyDo not put a key in a notebook checked into git or a client-side application. The Free tier does not include XAG.
Get a silver API keySilver requires Pro or Realtime Pro.Compare plans →2.Install the HTTP client
Install
requests. The standard librarydecimalmodule avoids binary floating-point representation errors; division still uses the configured Decimal precision.BASH · shellpython -m pip install requests3.Fetch spot and daily history
Save this as
silver_price.py. It uses the canonical Bearer header, checks the tier response, parses decimal strings, rejects stale data, and reverses newest-first bars into chronological order. The canonical response usesbars,bar_start, andis_closed; a forming daily bar is provisional. This example limits the range to 90 days with a 100-row page, so it does not silently truncate a longer series.PYTHON · silver_price.pyimport os from datetime import datetime, timedelta, timezone from decimal import Decimal import requests BASE = "https://api.goldprice.dev" KEY = os.environ["GOLDPRICE_API_KEY"] HEADERS = {"Authorization": f"Bearer {KEY}"} TROY_OUNCE_GRAMS = Decimal("31.1034768") def get_json(path, params=None): response = requests.get(BASE + path, headers=HEADERS, params=params, timeout=10) if response.status_code == 401: raise RuntimeError("401: check GOLDPRICE_API_KEY") if response.status_code == 403: raise RuntimeError("403: XAG requires a Pro or Realtime Pro key") if response.status_code == 429: raise RuntimeError("429: wait for the Retry-After window before trying again") response.raise_for_status() return response.json() def silver_spot(): data = get_json("/v1/spot/XAG-USD-SPOT") price = Decimal(data["price"]) if data.get("is_stale"): raise RuntimeError("the API marked this quote stale") return { "price_per_oz": price, "price_per_gram": price / TROY_OUNCE_GRAMS, "computed_at": data["computed_at"], } def silver_history(days=30): if not 1 <= days <= 90: raise ValueError("this example supports 1 to 90 daily bars per request") end = datetime.now(timezone.utc).date() start = end - timedelta(days=days - 1) data = get_json("/v1/bars", { "symbol": "XAG-USD-SPOT", "interval": "1d", "from": start.isoformat(), "to": end.isoformat(), "limit": 100, }) return [ {"date": bar["bar_start"][:10], "close": Decimal(bar["close"]), "is_closed": bar["is_closed"]} for bar in reversed(data["bars"]) if bar.get("close") is not None ] if __name__ == "__main__": quote = silver_spot() print(f"XAG/USD per oz: {quote['price_per_oz']}") print(f"XAG/USD per gram: {quote['price_per_gram']}") print(f"Computed: {quote['computed_at']}") print(f"Daily bars returned: {len(silver_history())}")4.Run the client
Run
python silver_price.py. Keep thecomputed_attimestamp with every stored value. A quote can be current at request time yet still be marked stale by the service, so treat that flag as a data-quality check.BASH · shellpython silver_price.py # XAG/USD per gram: 1.04... # Daily bars returned: depends on available observations5.Control request volume
One spot call plus one history call per run is two calls. A script run every 15 minutes uses about 5,760 calls/month, before retries. Cache spot for at least 60 seconds, batch work where possible, and choose a tier whose monthly quota covers your schedule.
PYTHON · quota.pycalls_per_month = 2 * 4 * 24 * 30 # 5,760 calls for a 15-minute schedule print(calls_per_month)
Expected output
The API returns this shape:
{
"symbol": "XAG",
"quote_currency": "USD",
"price": "32.50",
"is_stale": false,
"computed_at": "2026-09-29T10:00:00+00:00"
}The script prints the Decimal price per troy ounce, the Decimal price per gram, the source timestamp, and the number of daily bars returned (which can be less than the requested calendar-day range on weekends or when data is unavailable).
Common errors
| Code | Symptom | Fix |
|---|---|---|
| 401 | The request returns 401. | Check that GOLDPRICE_API_KEY is set and send it as Authorization: Bearer .... |
| 403 | The request returns 403 plan_gated. | XAG is not included on Free. Use a Pro or Realtime Pro key and check pricing. |
| 429 | The request returns 429. | Honor Retry-After, cache repeated symbols, and reduce the schedule before increasing tier. |
FAQ
Is silver available on the Free tier?
No. XAG spot and XAG history require Pro or Realtime Pro. Gold spot remains the Free-tier example.
How many calls does the example make?
One spot request and one daily-history request per run. A 15-minute schedule is about 5,760 calls per 30-day month before retries.
Why use Decimal?
The API sends numeric prices as strings. Decimal preserves decimal inputs and makes the rounding context explicit instead of introducing binary float rounding; choose a quantization policy before displaying or storing derived values.
How do I detect an old quote?
Check is_stale and retain computed_at. Apply an application-specific age policy to the timestamp before displaying or storing a quote.
Can an AI agent use the same silver data?
Yes. The goldprice.dev MCP server exposes the same product surface for supported plans. Use the API docs and the MCP setup guide to keep tool and tier assumptions current.
Can I use this in a commercial application?
Commercial use requires Pro or Realtime Pro with the attribution required by the terms. Keep the API key on your server.
Going further
- Store the raw response beside each normalized bar for reproducibility.
- Add a circuit breaker that stops retries when the service reports a stale quote.
- Use the same Decimal unit boundary when valuing silver holdings by gram.
Next steps
Try the same setup in a different platform: