Build a Weather Bot, Part 1: Temperature Threshold Alerts
You pick a station. You set a number. The bot tells you when the temperature crosses it, or when the day's trajectory says it won't. About 80 lines of Python.
This is Part 1 of a three-part series on building with the DailyHigh API. Part 2 covers tracking a daily high from prediction to final result, and Part 3 covers monitoring multiple stations at once.
Why threshold alerts?
Forecasts give you a range. "High near 84 °F." That's useful for packing a jacket, less useful when you need to know whether a station will actually hit 85 °F on a specific day.
The DailyHigh prediction endpoint updates every two minutes. It returns the current observed max, a predicted max, a confidence score, and a flag for whether the station has passed its daily peak. That's enough to build a bot that watches a single number and tells you the moment the answer is clear.
What you'll need
- Python 3.9+
- A DailyHigh Pro API key (starts with
dh_live_) - A Discord webhook URL (or Slack, or any service that accepts a POST request)
Step 1: Fetch the prediction
The /api/v1/prediction/:icao endpoint returns everything we need. Here's a minimal fetch:
import requests
API_KEY = "dh_live_xxxxx"
BASE = "https://dailyhigh.app"
def get_prediction(icao: str) -> dict:
resp = requests.get(
f"{BASE}/api/v1/prediction/{icao}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
resp.raise_for_status()
return resp.json()["data"]A typical response looks like this:
{
"currentTemp": 28.4,
"observedMax": 29.1,
"predictedMax": 31.2,
"forecastMax": 31.0,
"confidence": 6,
"isPastPeak": false,
"hoursUntilPeak": 2.5,
"slope": 1.4,
"slopeWindow": "90 min",
"predictionReason": "Warming trend",
"reasoning": [
"About 2h until peak warming",
"Temperatures still climbing",
"Observed temps tracking close to prediction"
]
}The fields that matter most for threshold checking:
| Field | What it tells you |
|---|---|
observedMax | Highest temperature recorded so far today |
predictedMax | Where DailyHigh thinks the day will end up |
confidence | 1 (low) to 10 (high), rises as the day progresses |
isPastPeak | true once the station has likely hit its high for the day |
slope | Rate of temperature change in °C/hour over the last 90 minutes |
Step 2: Define your threshold
Keep it simple. A config dict with the station, target temperature, and direction:
ALERT = {
"icao": "KLGA",
"target": 29.4, # 85 °F in Celsius
"direction": "over", # "over" or "under"
}Step 3: Check the threshold
Three possible outcomes each time you poll:
- Crossed. The observed max has already passed the target.
- Unlikely. The station is past peak and the predicted max didn't reach it.
- Still in play. Neither condition is met yet.
def check_threshold(data: dict, target: float, direction: str) -> str:
observed = data["observedMax"]
predicted = data["predictedMax"]
past_peak = data["isPastPeak"]
if direction == "over":
if observed >= target:
return "crossed"
if past_peak and predicted < target:
return "unlikely"
else: # "under"
if past_peak and observed < target:
return "crossed"
if observed >= target:
return "unlikely"
return "pending"The isPastPeak flag is the key signal. Once it flips to true, the observed max is very close to the final daily high. If the number hasn't been hit by then, it probably won't be.
Step 4: Send an alert
Discord webhooks accept a simple JSON POST. Same pattern works for Slack.
WEBHOOK_URL = "https://discord.com/api/webhooks/..."
def send_alert(message: str):
requests.post(
WEBHOOK_URL,
json={"content": message},
timeout=10,
)Format the message with the data that matters:
def format_message(data: dict, status: str, alert: dict) -> str:
icao = alert["icao"]
target = alert["target"]
observed = data["observedMax"]
predicted = data["predictedMax"]
confidence = data["confidence"]
if status == "crossed":
return (
f"✅ **{icao}** crossed {target} °C\n"
f"Observed max: {observed} °C | "
f"Predicted max: {predicted} °C | "
f"Confidence: {confidence}/10"
)
elif status == "unlikely":
return (
f"❌ **{icao}** unlikely to reach {target} °C\n"
f"Observed max: {observed} °C | "
f"Predicted max: {predicted} °C | "
f"Past peak: yes"
)
return ""Step 5: Put it together
The full script polls the prediction endpoint, checks the threshold, and sends one alert when the outcome is decided.
import requests
import sys
API_KEY = "dh_live_xxxxx"
BASE = "https://dailyhigh.app"
WEBHOOK_URL = "https://discord.com/api/webhooks/..."
ALERT = {
"icao": "KLGA",
"target": 29.4,
"direction": "over",
}
def get_prediction(icao: str) -> dict:
resp = requests.get(
f"{BASE}/api/v1/prediction/{icao}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
if resp.status_code == 202:
return None # prediction not yet cached, retry later
resp.raise_for_status()
return resp.json()["data"]
def check_threshold(data: dict, target: float, direction: str) -> str:
observed = data["observedMax"]
predicted = data["predictedMax"]
past_peak = data["isPastPeak"]
if direction == "over":
if observed >= target:
return "crossed"
if past_peak and predicted < target:
return "unlikely"
else:
if past_peak and observed < target:
return "crossed"
if observed >= target:
return "unlikely"
return "pending"
def send_alert(message: str):
requests.post(WEBHOOK_URL, json={"content": message}, timeout=10)
def main():
data = get_prediction(ALERT["icao"])
if data is None:
print("Prediction not ready, will retry next run")
sys.exit(0)
status = check_threshold(data, ALERT["target"], ALERT["direction"])
print(f'{ALERT["icao"]}: {status} '
f'(observed={data["observedMax"]}, '
f'predicted={data["predictedMax"]}, '
f'confidence={data["confidence"]})')
if status in ("crossed", "unlikely"):
msg = format_message(data, status, ALERT)
send_alert(msg)
print("Alert sent")
if __name__ == "__main__":
main()Step 6: Schedule it
Run the script every 30 minutes during the station's daylight hours. The prediction endpoint updates every two minutes, but for a threshold bot, checking twice an hour is plenty.
Cron (macOS/Linux):
# Every 30 minutes from 6 AM to 8 PM UTC
*/30 6-20 * * * cd /path/to/bot && python alert.py >> alert.log 2>&1GitHub Actions (free tier):
on:
schedule:
- cron: "*/30 6-20 * * *"One thing to handle: you only want to send the alert once per day, not every 30 minutes after the threshold is decided. The simplest fix is a state file:
import json
from pathlib import Path
STATE_FILE = Path("state.json")
def already_alerted(icao: str, date: str) -> bool:
if not STATE_FILE.exists():
return False
state = json.loads(STATE_FILE.read_text())
return state.get(icao) == date
def mark_alerted(icao: str, date: str):
state = {}
if STATE_FILE.exists():
state = json.loads(STATE_FILE.read_text())
state[icao] = date
STATE_FILE.write_text(json.dumps(state))Check already_alerted before sending, call mark_alerted after. The state file resets naturally when the date changes.
Going further
This bot watches one station and one threshold. In Part 2 of this series, we'll follow a station through the full day: morning prediction, midday observations, and the final result from the history endpoint. Part 3 extends the pattern to all 12 tracked stations at once.
A few ideas to extend this bot on your own:
- Add
confidencegating. Only alert when confidence is 7 or higher, so you're not acting on shaky early-morning predictions. - Use
slopefor early signals. A steep positive slope (e.g. +2.0 °C/hour) with 3 hours until peak makes a high target more plausible. - Fetch the forecast for context. Call
/api/v1/weather/:icaoand compareforecast.maxTempto your target before the prediction engine has enough data.
The full API reference covers all available endpoints and fields. Browse the stations index to find ICAO codes for all tracked stations.
Live METAR observations, AI daily high predictions, and temperature alerts for 12 airport stations worldwide. Free to browse, from $7/mo for real-time data and API access.
Related posts
Build a Weather Bot, Part 3: Monitoring Multiple Stations
Use the DailyHigh API to poll all tracked stations, rank them by confidence, and build a daily digest of temperature predictions.
Build a Weather Bot, Part 2: Tracking Predictions Through the Day
Follow a single station through the full day: morning prediction, midday observations, and the final daily high from the history endpoint.
How Weather Forecasts Are Made, Step by Step
From raw observations to the forecast on your phone. A look at the data, models, and human decisions behind every weather prediction.