Silver prices have fluctuated quite a bit recently, so I wanted to receive a daily notification with the latest silver spot price.
Requirements and setup
I had a few requirements:
- I wanted a single, simple Python script that would fetch the latest price of silver and send a notification to my phone.
- I wanted to run the script in a cron job that would run every day at 8am Pacific Time.
With these requirements in mind, I went with this setup:
- Hetzner for compute: I'd run the cron job on a Hetzner server I have in Oregon. I already use it as an exit node for my Tailscale network and it wasn't doing anything else, so this was perfect.
- ntfy for notifying me: I recently discovered ntfy, and it was love at first site. It makes it super easy to send notifications to your phone without any kind of auth or setup. One cURL request and boom, you've got a notification. It's the perfect solution for a simple job like my silver notifier.
I put my requirements into Claude Code and had it write the script for me.
The script
Here's the script it came up with:
#!/usr/bin/env python3
"""Fetch the current silver spot price and push it to an ntfy topic."""
import logging
import os
import requests
NTFY_TOPIC = "silver-aaaaaaaaaa" # Not my actual ID. I used this page from the ntfy docs to generate a unique id: https://docs.ntfy.sh/publish/#picking-a-topic
NTFY_URL = f"https://ntfy.sh/{NTFY_TOPIC}"
PRICE_URL = "https://api.gold-api.com/price/XAG"
TIMEOUT = 15
LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "silver.log")
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger(__name__)
def fetch_price():
log.info("Fetching silver spot price from %s", PRICE_URL)
response = requests.get(PRICE_URL, timeout=TIMEOUT)
response.raise_for_status()
data = response.json()
price = float(data["price"])
log.info("Silver spot price: %s USD/oz (updated %s)", price, data.get("updatedAt"))
return price
def notify(message, title, tags):
requests.post(
NTFY_URL,
data=message.encode("utf-8"),
headers={"Title": title, "Tags": tags},
timeout=TIMEOUT,
).raise_for_status()
log.info("Sent ntfy notification to %s: %s", NTFY_TOPIC, message)
def main():
try:
price = fetch_price()
notify(f"Silver spot: ${price:,.2f}/oz", "Silver Price", "coin")
except Exception as exc:
log.exception("Silver notifier failed")
message = f"Silver notifier failed: {type(exc).__name__}: {exc}"[:300]
try:
notify(message, "Silver Price Error", "rotating_light")
except Exception:
log.exception("Could not send error notification")
if __name__ == "__main__":
main()
The script fetches the latest silver price from a public, unauthenticated API, sends me a notification with ntfy, and writes logs to a silver.log
file so I can debug in case something goes wrong. All I had to do was download the ntfy iOS app and subscribe to the unique notification topic
I chose.
The notification schedule
Finally, I ran crontab -e and added these lines to run the script every day at 9am Pacific Time:
# Notify me with the latest silver price. Machine is in UTC - this corresponds to 8AM PDT.
0 15 * * * /usr/bin/python3 /home/ops/projects/silver-notifier/silver_notifier.py
I'll have to update the crontab in a couple of months once daylight savings kicks in, but it's a super quick fix. Even if I don't update it, an hour difference in the notification doesn't really matter.