How to Monitor a Django Management Command
A Django upgrade changes how a queryset behaves, a management command that's been running fine for a year quietly starts throwing on line 40, and nobody notices because it was scheduled through cron and nobody's tailing that log. Three weeks later, someone asks why the weekly digest emails stopped going out. That gap, between when something breaks and when a human finds out, is exactly what heartbeat monitoring closes.
The idea is simple: your command pings a unique URL when it finishes successfully, and if that ping doesn't arrive on schedule, you get an alert instead of a delayed complaint. Here's how to add that to a Django management command using the Downdar API.
What You Will Need
A Downdar API key from your account, a Django project with a management command already defined, and the requests library, which is likely already a dependency in most Django projects. Your API key should live in settings via an environment variable, never committed to your repository.
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 command will call on schedule. You only need to do this once per command. If you'd rather not script it, creating the heartbeat from your Downdar dashboard and copying the URL works just as well, the API call is mainly useful when you're provisioning monitoring as part of a deploy pipeline across multiple environments.
The response looks like this:
Add the url to your settings as DOWNDAR_HEARTBEAT_URL, sourced from an environment variable rather than hardcoded.
Pinging the Heartbeat From Your Command
The cleanest place to add the ping is inside the command's own handle() method, so success and failure are determined by the same logic that already decides whether your command worked.
The structure here matters more than it might look: send_weekly_digest() runs first and is allowed to raise normally, if it fails, Django's command machinery reports the error and the code never reaches the ping. Only a command that actually completes gets to the try block that pings the heartbeat. And the ping itself is wrapped separately, so a flaky network call to Downdar can't cause a successful digest run to report as an error.
If you're scheduling this through plain cron rather than something like Celery beat, the same effect works without touching the command file at all:
Either approach works. Wiring the ping into handle() keeps it version-controlled alongside the command; wiring it into crontab keeps the command itself free of any monitoring-specific code. Pick whichever fits how your team already manages scheduled tasks.
Checking Heartbeat Status Programmatically
To surface a heartbeat's current state, say, in an internal Django admin view, the /api/v1/heartbeats/status endpoint returns just the essentials.
That gives you a quick way to display "last digest run: healthy" in a place your team already looks, rather than everyone needing a separate login to Downdar to check.
A Note on API Key Security
Keep the API key and heartbeat URL out of your settings file directly, and read them from the environment instead, the same way you'd already be handling your database credentials or secret key.
The heartbeat URL itself doesn't need the same level of protection as the API key, it only accepts a check-in and can't read or modify your account, so it's fine sitting in the same environment configuration as the rest of your app. The API key is what should never end up in a settings file that gets committed, a debug page, or anything a user could reach.
Get Started
Set up a heartbeat for your most important management command first, whichever one causes the most trouble when it silently stops running. The rest of your scheduled commands are the same handful of lines away once that first one's working.