Use goldprice.dev to fetch silver with GET https://api.goldprice.dev/v1/spot/XAG-USD-SPOT and a Basic, Plus, or 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.
Create an API key
Silver spot is on every plan; 90-day daily history needs Basic or higher. Store the key outside your code:
export GOLDPRICE_API_KEY=ga_live_replace_with_your_keyKeep keys out of committed notebooks and client-side apps. Free includes XAG spot and 30-day daily history.
Install the HTTP client
Install requests. The standard library decimal module avoids binary floating-point representation errors; division still uses the configured Decimal precision.
python -m pip install requestsFetch 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 uses bars, bar_start, and is_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.
import osfrom datetime import datetime, timedelta, timezonefrom decimal import Decimalimport requestsBASE = "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: 90-day history needs Basic or higher") 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())}")Run the client
Run python silver_price.py. Keep the computed_at timestamp 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.
python silver_price.py# XAG/USD per gram: 1.04...# Daily bars returned: depends on available observationsControl 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.
calls_per_month = 2 * 4 * 24 * 30 # 5,760 calls for a 15-minute scheduleprint(calls_per_month)Expected output
{ "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 | What you see | 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. | Free includes XAG spot; 90-day daily history needs Basic or higher. See pricing. |
| 429 | The request returns 429. | Honor Retry-After, cache repeated symbols, and reduce the schedule before increasing tier. |
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.