Jewellery stores in India get the same WhatsApp message dozens of times every morning: "What's today's gold rate?" Instead of answering each one by hand, you can send a short daily rate update to customers who asked for it.
This post builds that with Python and the official WhatsApp Cloud API, and covers the rules you must follow so the number stays healthy.
The rules first
- Opt-in only. Send only to customers who agreed to receive daily rates (a checkbox at billing, or a "Reply YES for daily rates" message).
- Marketing template. A daily rate update is promotional, so it uses a Marketing template and is charged at the marketing rate.
- Respect STOP. Remove anyone who replies STOP before the next send.
- Go slow. Send in batches; don't blast thousands of messages in one second.
1. Create the template
In WhatsApp Manager, create daily_gold_rate (category: Marketing):
Good morning {{1}}! Today's gold rates at Lakshmi Jewellers:
22K: Rs {{2}}/g
24K: Rs {{3}}/g
Reply STOP to stop daily rates.
2. The script
import csv, os, time, requests
PHONE_ID = os.environ["PHONE_NUMBER_ID"]
TOKEN = os.environ["WA_TOKEN"]
URL = f"https://graph.facebook.com/v21.0/{PHONE_ID}/messages"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
def load_subscribers(path="subscribers.csv"):
# columns: phone,name,opted_in,stopped
with open(path, newline="", encoding="utf-8") as f:
return [r for r in csv.DictReader(f)
if r["opted_in"] == "yes" and r["stopped"] != "yes"]
def send_rate(phone, name, rate_22k, rate_24k):
payload = {
"messaging_product": "whatsapp",
"to": phone,
"type": "template",
"template": {
"name": "daily_gold_rate",
"language": {"code": "en"},
"components": [{
"type": "body",
"parameters": [
{"type": "text", "text": name or "there"},
{"type": "text", "text": rate_22k},
{"type": "text", "text": rate_24k},
],
}],
},
}
r = requests.post(URL, json=payload, headers=HEADERS, timeout=15)
return r.status_code, r.text
def main():
rate_22k = os.environ["RATE_22K"] # e.g. "7,120" entered by staff each morning
rate_24k = os.environ["RATE_24K"]
sent = failed = 0
for i, sub in enumerate(load_subscribers(), 1):
status, body = send_rate(sub["phone"], sub["name"], rate_22k, rate_24k)
if status == 200:
sent += 1
else:
failed += 1
print("Failed", sub["phone"], body)
if i % 50 == 0:
time.sleep(5) # gentle batching
print(f"Done: {sent} sent, {failed} failed")
if __name__ == "__main__":
main()
Run it every morning at 10 AM with cron: 0 10 * * * python3 send_rates.py
3. Handle STOP replies
Your webhook receives replies. When the text is STOP, set stopped=yes for that number before the next run. Also verify X-Hub-Signature-256 on every webhook POST.
Costs and limits to know
- Marketing messages are charged per delivered message (in India about Rs 0.86 each on Meta's October 2026 rate card).
- Meta may limit how many marketing messages a single user receives from businesses, so not every send is guaranteed to deliver. Check the message status webhooks.
- Low engagement or many blocks hurt your number's quality rating, so keep the list opted-in.
Beyond a script
A real store also wants the rate update, catalogue sharing, gold scheme instalment reminders and a shared inbox for replies.
If the store's site runs on WordPress, here's how jewellery stores run rate broadcasts and scheme reminders without maintaining scripts: https://wabbiyo.com/industries/jewellery-stores/
Wabbiyo, the self-hosted WhatsApp Business API plugin for WordPress: https://wabbiyo.com/
Top comments (0)