
This is a runnable Python script that polls the live XAU-USD spot price and sends a notification when a threshold is crossed, with local caching and retries with exponential backoff. The live gold spot price comes from the API, and the script:
- Fetches the live XAU-USD spot price from the API
- Checks it against a configurable threshold
- Fires a notification when the threshold is crossed
- Caches responses locally to avoid burning quota
- Retries on transient errors with exponential backoff
No SDKs required, only the standard library plus requests.
Prerequisites
- Python 3.9+
pip install requests- A free API key from goldprice.dev/pricing (5,000 calls/month on the free tier)
Step 1: Fetch the live spot price
The spot endpoint returns the current price along with source metadata:
import requestsAPI_KEY = "YOUR_API_KEY"BASE_URL = "https://api.goldprice.dev/v1"def fetch_spot_price() -> float: """Return the current XAU-USD spot price in USD per troy ounce.""" resp = requests.get( f"{BASE_URL}/prices", params={"symbol": "XAU-USD-SPOT"}, headers={"Authorization": f"Bearer {API_KEY}"}, timeout=10, ) resp.raise_for_status() data = resp.json() quote = data["symbols"][0] if quote["is_stale"]: raise ValueError("Spot price is stale") return float(quote["price"])The price is a string in symbols[0].price, so the script converts it with float(). If is_stale is true, the price is out of date, and the script raises instead of alerting on it.
Step 2: Add a TTL cache
Polling every second against a REST endpoint is wasteful and burns quota. A simple in-memory cache with a configurable TTL is enough for most alert use cases:
import timefrom typing import Optional_cache: dict[str, tuple[float, float]] = {} # key -> (value, expiry)def cached_spot_price(ttl_seconds: int = 60) -> float: """Return a cached spot price, refreshing only when TTL has elapsed.""" key = "XAU-USD-SPOT" now = time.monotonic() if key in _cache: value, expiry = _cache[key] if now < expiry: return value price = fetch_spot_price() _cache[key] = (price, now + ttl_seconds) return priceAt 60-second TTL you use at most 1,440 calls per day. Free covers about 3.5 days of testing; Basic and up cover continuous polling. For a deeper look at cache strategy and rate-limit headers, see Caching gold prices and staying inside rate limits.
Step 3: Retry on transient errors
Network failures and rate-limit responses (HTTP 429) are transient. Wrap the fetch with exponential backoff:
import timeimport requestsMAX_RETRIES = 4BACKOFF_BASE = 2.0 # secondsdef fetch_spot_price_with_retry() -> float: for attempt in range(MAX_RETRIES): try: return fetch_spot_price() except requests.exceptions.HTTPError as exc: if exc.response is not None and exc.response.status_code == 429: wait = BACKOFF_BASE ** attempt print(f"Rate limited. Retrying in {wait:.0f}s…") time.sleep(wait) else: raise except requests.exceptions.RequestException as exc: wait = BACKOFF_BASE ** attempt print(f"Request error: {exc}. Retrying in {wait:.0f}s…") time.sleep(wait) raise RuntimeError("Max retries exceeded fetching spot price")Step 4: The alert logic
Define thresholds and fire a notification. Here we use print as the notifier, swap in email, Slack, SMS, or any webhook:
def send_alert(message: str) -> None: """Replace this with your preferred notification channel.""" print(f"[ALERT] {message}")def check_and_alert( above: Optional[float] = None, below: Optional[float] = None,) -> None: """Fire an alert if the live price crosses either threshold.""" price = cached_spot_price(ttl_seconds=60) print(f"XAU-USD spot: ${price:,.2f}") if above is not None and price >= above: send_alert(f"Gold crossed ABOVE ${above:,.0f}, current: ${price:,.2f}") if below is not None and price <= below: send_alert(f"Gold fell BELOW ${below:,.0f}, current: ${price:,.2f}")Step 5: Poll on an interval
Wire everything into a polling loop:
import timedef run_alert_loop( above: Optional[float] = None, below: Optional[float] = None, poll_interval: int = 60,) -> None: """ Poll forever, checking thresholds on each tick. Args: above: Fire when price >= this value (USD/oz). None = no upper alert. below: Fire when price <= this value (USD/oz). None = no lower alert. poll_interval: Seconds between checks (minimum 60 recommended). """ print(f"Starting gold alert. above={above}, below={below}, interval={poll_interval}s") while True: try: check_and_alert(above=above, below=below) except Exception as exc: print(f"Error during check: {exc}") time.sleep(poll_interval)if __name__ == "__main__": # Alert if gold goes above $4,200 or below $3,800 run_alert_loop(above=4200, below=3800, poll_interval=60)Complete file
Putting it all together in gold_alert.py:
"""gold_alert.py, Poll the live gold spot price and fire threshold alerts.Requires: pip install requests"""import timefrom typing import Optionalimport requestsAPI_KEY = "YOUR_API_KEY"BASE_URL = "https://api.goldprice.dev/v1"MAX_RETRIES = 4BACKOFF_BASE = 2.0_cache: dict[str, tuple[float, float]] = {}def fetch_spot_price() -> float: resp = requests.get( f"{BASE_URL}/prices", params={"symbol": "XAU-USD-SPOT"}, headers={"Authorization": f"Bearer {API_KEY}"}, timeout=10, ) resp.raise_for_status() data = resp.json() quote = data["symbols"][0] if quote["is_stale"]: raise ValueError("Spot price is stale") return float(quote["price"])def fetch_spot_price_with_retry() -> float: for attempt in range(MAX_RETRIES): try: return fetch_spot_price() except requests.exceptions.HTTPError as exc: if exc.response is not None and exc.response.status_code == 429: wait = BACKOFF_BASE ** attempt time.sleep(wait) else: raise except requests.exceptions.RequestException: time.sleep(BACKOFF_BASE ** attempt) raise RuntimeError("Max retries exceeded")def cached_spot_price(ttl_seconds: int = 60) -> float: key = "XAU-USD-SPOT" now = time.monotonic() if key in _cache: value, expiry = _cache[key] if now < expiry: return value price = fetch_spot_price_with_retry() _cache[key] = (price, now + ttl_seconds) return pricedef send_alert(message: str) -> None: print(f"[ALERT] {message}")def check_and_alert( above: Optional[float] = None, below: Optional[float] = None,) -> None: price = cached_spot_price(ttl_seconds=60) print(f"XAU-USD spot: ${price:,.2f}") if above is not None and price >= above: send_alert(f"Gold crossed ABOVE ${above:,.0f}, current: ${price:,.2f}") if below is not None and price <= below: send_alert(f"Gold fell BELOW ${below:,.0f}, current: ${price:,.2f}")def run_alert_loop( above: Optional[float] = None, below: Optional[float] = None, poll_interval: int = 60,) -> None: print(f"Starting gold alert. above={above}, below={below}, interval={poll_interval}s") while True: try: check_and_alert(above=above, below=below) except Exception as exc: print(f"Error: {exc}") time.sleep(poll_interval)if __name__ == "__main__": run_alert_loop(above=4200, below=3800, poll_interval=60)Extending the notifier
The send_alert function is the only thing you need to swap out. A few common patterns:
Slack webhook:
import requests as reqdef send_alert(message: str) -> None: req.post( "https://hooks.slack.com/services/YOUR/WEBHOOK", json={"text": message}, timeout=5, )Email via SMTP:
import smtplibfrom email.message import EmailMessagedef send_alert(message: str) -> None: msg = EmailMessage() msg["Subject"] = "Gold Price Alert" msg["From"] = "alerts@yourdomain.com" msg["To"] = "you@yourdomain.com" msg.set_content(message) with smtplib.SMTP_SSL("smtp.yourdomain.com", 465) as s: s.login("alerts@yourdomain.com", "YOUR_PASSWORD") s.send_message(msg)Quota guidance
| Plan | Calls/month | At 60s poll | Days of coverage |
|---|---|---|---|
| Free | 5,000 | ~1,440/day | ~3.5 days (testing) |
| Basic | 100,000 | ~1,440/day | Full month |
| Plus | 300,000 | ~1,440/day | Full month |
| Pro | 1,000,000 | ~1,440/day | Full month |
For continuous 60-second polling, any paid plan is appropriate. Free tier is useful for testing the integration before upgrading. See the pricing page for plan details.
Live gold prices, historical OHLC, and multi-source aggregation, available via REST and SSE.