DocsUse case

Gold price alert bot (Telegram, Discord)

Post to a Telegram chat and a Discord channel when gold crosses your level, from a short script or from signed webhook alerts.

6 Nov 2026 · 3 min read

A gold price alert bot has three parts: a price source, a rule such as "crossed 3,900", and a message to a chat. Telegram and Discord each take one HTTPS POST with JSON. The price comes from GET /v1/spot/XAU, which needs no key. A dry run on 2 November 2026 printed both requests. Plus and Pro also offer webhook alerts, which need no polling loop.

  • The script polls the spot price every five minutes and posts only when the price changes side of your level.
  • Keyless prices are 15 minutes late and keyless calls are capped at 10 a minute and 100 an hour. One call every five minutes is 12 an hour.
  • Plus ($39 a month) and Pro ($89 a month) include five webhook alerts that call your URL, so a bot needs no polling loop.

Get the price

Shell
curl "https://api.goldprice.dev/v1/spot/XAU"

The response holds price in US dollars per troy ounce as a string, is_stale and computed_at.

The two messages

Telegram's Bot API takes a POST to https://api.telegram.org/bot<token>/sendMessage with a chat_id and text of 1 to 4096 characters. You get the token from @BotFather. Discord takes a POST to a channel webhook URL with a content field of up to 2000 characters.

The script

Shell
import json, os, sys, timefrom decimal import Decimalfrom urllib.request import urlopen, RequestLEVEL = Decimal("3900")                       # alert when gold crosses this, USD per troy ounceTELEGRAM = os.environ.get("TELEGRAM_URL", "https://api.telegram.org/bot<token>/sendMessage")DISCORD = os.environ.get("DISCORD_URL", "https://discord.com/api/webhooks/<id>/<token>")DRY = "--dry-run" in sys.argvdef spot():    with urlopen("https://api.goldprice.dev/v1/spot/XAU", timeout=10) as r:        return json.load(r)def post(url, body):    req = Request(url, data=json.dumps(body).encode(),                  headers={"Content-Type": "application/json", "User-Agent": "gold-alert-bot/1.0"})    if DRY:        print("POST", req.full_url, req.data.decode())    else:        urlopen(req, timeout=10).read()def notify(text):    post(TELEGRAM, {"chat_id": os.environ.get("TELEGRAM_CHAT", "<chat id>"), "text": text})    post(DISCORD, {"content": text})side = Nonewhile True:    q = spot()    price = Decimal(q["price"])    now = "above" if price > LEVEL else "below"    if side and now != side:        notify(f"Gold crossed {LEVEL}: {price} USD/oz, now {now} ({q['computed_at'][:16]}Z)")    if DRY:        notify(f"Gold {price} USD/oz at {q['computed_at'][:16]}Z, {now} {LEVEL}")        break    side = now    time.sleep(300)

The script keeps the last side of the level in memory. On the first pass it only records the side, so a restart does not send a false alert. It then sends a message each time the price moves from one side to the other. Set TELEGRAM_URL, TELEGRAM_CHAT and DISCORD_URL through environment variables, and keep tokens out of the repository. The script sends its own User-Agent header because Discord answers the default Python one with a 403.

A dry run with --dry-run prints the two requests it would send and exits. The output on 2 November 2026:

Shell
POST https://api.telegram.org/bot<token>/sendMessage {"chat_id": "<chat id>", "text": "Gold 3904.38 USD/oz at 2026-11-02T11:53Z, above 3900"}POST https://discord.com/api/webhooks/<id>/<token> {"content": "Gold 3904.38 USD/oz at 2026-11-02T11:53Z, above 3900"}

The placeholders show where the token, chat id and webhook go. We did not post to a live chat, so run the script once in your own chat before you trust it.

Avoid repeat alerts

A price that sits on the level can cross it many times in an hour. Add a quiet period, such as ten minutes after each alert, or require the price to move a small margin past the level before you flip the side. The webhook alerts below do this for you.

Webhook alerts without polling

Plus and Pro each include five price alerts. An alert watches gold, silver or copper with one rule and sends a signed POST to one URL. The rules are a level cross, a percentage move over 1 hour or 24 hours, and a daily close. A level alert stays quiet for at least 10 minutes after it fires, and a failed delivery is retried up to five more times over about 8.5 hours. Your endpoint receives the event, checks the signature and posts the text to Telegram or Discord with the same two requests as above. The signature check and payload are in the alerts reference.

Questions

How do I send a gold price alert to Telegram?

POST JSON with `chat_id` and `text` to `https://api.telegram.org/bot<token>/sendMessage`. Get the bot token from @BotFather and the chat id from the `getUpdates` method after you message the bot.

How do I send a gold price alert to Discord?

Create a webhook for a channel and POST JSON with a `content` field, up to 2000 characters, to its URL.

How often can the bot check the price?

Keyless callers get 10 requests a minute and 100 an hour. A free key adds 5,000 requests a month. Free and keyless prices are 15 minutes late.

Do alerts need a paid plan?

Webhook alerts need the Plus or Pro plan, which each include five alerts. A polling script works on the Free plan and with no key at all.

Keep reading

Start with a keyless poll

The spot endpoint needs no key. For alerts that fire without polling, Plus and Pro include five webhook alerts.

Get a free key