Python apps
Example: a daily price alert
Build a job that checks a price list every morning at 09:30 IST and sends you a Telegram message when prices change.
On this page
In this tutorial you build price-bot, a job that runs every day at 09:30 IST. It downloads a price list over HTTPS, compares it with yesterday's prices saved in the app folder, and sends you a Telegram message listing what changed. It uses only things apps are allowed to do: outbound HTTPS and files in its own folder (it uses Telegram rather than email; to send email instead, see Send email from your website or app).
Before you start#
- A plan with a free Python app slot (Pro and above, or any Compute plan). See Run Python apps from the panel.
- The Telegram app on your phone or computer.
- A URL that returns your prices as JSON. In this example the feed looks like this; replace it with your supplier's or your own shop's feed:
[
{"sku": "DRL-500", "name": "Drill 500 W", "price": 2499},
{"sku": "SAW-210", "name": "Circular saw", "price": 5890}
]Step 1: Create a Telegram bot and find your chat id#
- Open Telegram and message @BotFather. Send
/newbot, choose a name and a user name ending inbot. - Copy the token BotFather gives you. It looks like
123456789:AAH.... Treat it like a password. - Send any message to your new bot (open it and press Start). The bot can only message you after you have messaged it.
- Find your chat id. Open
https://api.telegram.org/bot<TOKEN>/getUpdatesin your browser, with your token in place of<TOKEN>. In the reply, find"chat":{"id":123456789and copy the number.
Step 2: Write the code on your computer#
Make a folder called price-bot with three files.
requirements.txt
requests==2.32.3config.json (your own values)
{
"feed_url": "https://shop.acme.in/prices.json",
"telegram_token": "123456789:AAH-your-token",
"chat_id": "123456789"
}main.py
"""price-bot: compare today's prices with the last run and send changes to Telegram."""
import json
import sys
from pathlib import Path
import requests
HERE = Path(__file__).resolve().parent
CONFIG = json.loads((HERE / "config.json").read_text(encoding="utf-8"))
STATE = HERE / "prices.json" # saved between runs, in the app folder
def fetch_prices() -> dict:
r = requests.get(CONFIG["feed_url"], timeout=30)
r.raise_for_status()
return {item["sku"]: {"name": item["name"], "price": item["price"]} for item in r.json()}
def load_previous() -> dict:
if STATE.exists():
return json.loads(STATE.read_text(encoding="utf-8"))
return {}
def send_telegram(text: str) -> None:
url = f"https://api.telegram.org/bot{CONFIG['telegram_token']}/sendMessage"
r = requests.post(url, data={"chat_id": CONFIG["chat_id"], "text": text}, timeout=30)
r.raise_for_status()
def main() -> int:
today = fetch_prices()
before = load_previous()
print(f"fetched {len(today)} prices")
changes = []
for sku, item in sorted(today.items()):
old = before.get(sku)
if old is None:
changes.append(f"NEW {item['name']}: Rs {item['price']}")
elif old["price"] != item["price"]:
arrow = "up" if item["price"] > old["price"] else "down"
changes.append(f"{item['name']}: Rs {old['price']} -> Rs {item['price']} ({arrow})")
if changes:
send_telegram("Price changes today:\n" + "\n".join(changes))
print(f"{len(changes)} change(s), Telegram alert sent")
else:
print("no changes")
# Save only after the alert went out, so a failed send is retried tomorrow.
STATE.write_text(json.dumps(today, indent=2), encoding="utf-8")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except requests.RequestException as e:
print(f"network error: {e}", file=sys.stderr)
sys.exit(1)The script exits with code 1 on a network error, so the run shows as failed in the panel and you notice.
Step 3: Create the app in the panel#
- Open Python apps and go to Create an app.
- Enter App name
price-bot, keep Entry filemain.py. - Choose Mode: Job: runs, then stops.
- Choose Schedule: Daily at a time (IST) and set Daily time (IST) to
09:30. - Press Create app.
Step 4: Upload the code#
- Zip the folder. On Windows right-click
price-bot→ Send to → Compressed (zipped) folder. On macOS right-click → Compress. The zip may contain theprice-botfolder itself: FiniteHost removes that single top folder, somain.pylands in/apps/price-bot. - Drop price-bot.zip on the Upload code box of the app's card.
- Wait a minute for "uploaded. It lands in the app folder within a minute."
You can also copy the three files over SFTP into /apps/price-bot. See Connect with SFTP.
Step 5: Test with Run now#
- Press Run now. The panel says the server starts it within 30 seconds.
- Press the refresh button on the card after 30 seconds. The first run also installs
requests, so give it a minute or two. - Open Logs. You should see lines like
Installing requirements.txt,fetched 2 pricesand2 change(s), Telegram alert sent, and Last run finished with exit 0. - Check Telegram for the message.
Step 6: Turn on the schedule#
- Press Enable schedule. The card now shows the next run, for example next Wed 2026-10-28 09:30 IST.
- Check Cron jobs the next morning: the Recent runs table shows each day's result.
Troubleshooting tips#
network error: 401or404from Telegram: the token is wrong, or has a space in it.400 Bad Request: chat not found: wrong chat id, or you never pressed Start in the bot chat.FileNotFoundError: config.json: the file is not in/apps/price-bot. Check with SFTP; maybe the zip had two levels of folders.KeyError: 'sku': your feed uses different field names. Change them infetch_prices().- The job failed at 09:30 but works with Run now: the feed was down at that moment. The next day's run tries again.
- More in Fix a failing Python app.
What's next#
Was this helpful?
Still stuck?
Email support@finitehost.com with your subscription ID (it starts with t, shown on the panel's Dashboard) and what you tried. A person replies, usually within one business day.
Last updated 27 October 2026. Found something wrong? Tell support@finitehost.com.