How to Monitor a Cron Job in Python

Picture this: a Python script has been quietly failing every night for two weeks because a dependency changed its API, and the only reason anyone noticed is that finance asked why last month's report never arrived. No traceback ever reached a human. No alert ever fired. The script just stopped doing its job, silently, exactly like cron jobs tend to when nobody's specifically watching them.

That's what a heartbeat is for. Your script pings a unique URL when it finishes, and if that ping goes missing on schedule, you find out immediately instead of two weeks later. Here's how to add that to a Python cron job using the Downdar API.

What You Will Need

A Downdar API key from your account, and the requests library (pip install requests). Authentication is via Bearer token in the Authorization header. Your API key must always be kept server-side, never commit it to version control or embed it in client-facing code.

Creating a Heartbeat Monitor

The /api/v1/heartbeats/create endpoint accepts a POST request with a name and returns the full heartbeat object, including the unique url your job will call on schedule. You only need to do this once per job, not on every run. If scripting this isn't necessary for your setup, you can just as easily create the heartbeat from your Downdar dashboard and copy the URL, the API is mainly there for provisioning heartbeats automatically as part of a deploy pipeline.

# Run this once to set up the heartbeat. import requests   response = requests.post( 'https://downdar.com/api/v1/heartbeats/create', headers={'Authorization': 'Bearer YOUR_API_KEY'}, json={'name': 'Nightly report'} )   result = response.json()

The response looks like this:

{ "ok": true, "heartbeat": { "guid": "550e8400-e29b-41d4-a716-446655440000", "name": "Nightly report", "url": "https://downdar.com/heartbeats/550e8400-e29b-41d4-a716-446655440000", "paused": false }, "capacity": { "limit": 10, "used": 1, "remaining": 9 } }

Save the url field somewhere your job can reach it, an environment variable is the simplest option. You can also pass alert_channel_keys in the create request if you want this heartbeat routed to a specific Slack, Discord, or Telegram channel instead of the default.

Pinging the Heartbeat From Your Job

Once the heartbeat exists, the actual monitoring is a single HTTP request to its url at the end of a successful run. No library beyond requests is needed.

import os import requests   def run_nightly_report(): try: generate_report()   # Job succeeded, ping the heartbeat. requests.get( os.environ['DOWNDAR_HEARTBEAT_URL'], timeout=10 ) except Exception as err: # Don't ping on failure, let the missed check-in trigger the alert. print(f'Report failed: {err}')   if __name__ == '__main__': run_nightly_report()

The important detail is in the except block: only ping the heartbeat when the job actually succeeds. If generate_report() raises, skip the ping entirely, the heartbeat will notice the missing check-in on its own schedule and open an incident. This is what catches a silent failure, if you pinged unconditionally in a finally block instead, a broken job would still report as healthy.

If your job runs as a standalone script rather than something with its own error handling, the same call works from crontab directly:

# crontab: run the script, then ping only if it exits 0 0 3 * * * /usr/bin/python3 /opt/scripts/report.py && \ curl -fsS -m 10 https://downdar.com/heartbeats/550e8400-e29b-41d4-a716-446655440000

The && means curl only runs if the Python script exits successfully. If report.py exits with a non-zero status, the ping never fires, and a missed check-in is exactly what should happen.

Checking Heartbeat Status Programmatically

If you want to surface a heartbeat's current state in your own dashboard or admin panel, the /api/v1/heartbeats/status endpoint returns just the essentials, lighter than a full get call.

response = requests.post( 'https://downdar.com/api/v1/heartbeats/status', headers={'Authorization': f'Bearer {os.environ["DOWNDAR_API_KEY"]}'}, json={'guid': '550e8400-e29b-41d4-a716-446655440000'} )   result = response.json() # result['status'] - "unknown", "up", "warn", "down", or "paused"

status reflects the heartbeat's current state directly, so you can show "last report: healthy" somewhere your team already looks, without them needing to check Downdar itself.

A Note on API Key Security

Store your API key as an environment variable and never hardcode it. The python-dotenv package (pip install python-dotenv) makes this easy to manage locally.

# .env DOWNDAR_API_KEY=your_api_key_here DOWNDAR_HEARTBEAT_URL=https://downdar.com/heartbeats/your_heartbeat_guid   # Usage import os from dotenv import load_dotenv   load_dotenv() os.environ['DOWNDAR_API_KEY']

Unlike the API key, the heartbeat URL itself isn't sensitive in the same way, it only accepts a check-in and can't read or modify anything in your account, so it's fine to keep alongside your other deploy config. The API key is what actually needs protecting here.

Get Started

You can set this up in about the time it takes to run the script once. Create the heartbeat, drop the URL into your environment, and the next scheduled run is already being watched.