BlogDeveloper

Gold Price Alert Script in Python with Retries and Caching

Run a Python gold price alert script that polls live XAU-USD spot, checks a threshold and sends a notification, with a local TTL cache and retries on errors.

20 Oct 2026 · 5 min read

Gold Price Alert Script in Python with Retries and Caching

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:

  1. Fetches the live XAU-USD spot price from the API
  2. Checks it against a configurable threshold
  3. Fires a notification when the threshold is crossed
  4. Caches responses locally to avoid burning quota
  5. 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:

Python
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:

Python
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 price

At 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:

Python
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:

Python
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:

Python
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:

Python
"""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:

Python
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:

Python
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

PlanCalls/monthAt 60s pollDays of coverage
Free5,000~1,440/day~3.5 days (testing)
Basic100,000~1,440/dayFull month
Plus300,000~1,440/dayFull month
Pro1,000,000~1,440/dayFull 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.

Keep reading

Start with a free API key

5,000 requests a month on the free plan. No credit card.

Get a free key