Skip to content
LastPing
GUIDE · PYTHON · FREE

Monitor Python scripts: a try/except and one HTTP request

Ping on success. Report the exception on failure. Let absence detection catch everything that never reaches your except: the OOM kill, the crashed interpreter, the cron entry that vanished. No SDK, no agent; requests or plain stdlib. Free, no monitor cap.

Your assistant can do this for you

Monitor my Python scripts with LastPing

On this page
  1. The snippets
  2. What this catches
  3. Questions people ask

The snippets

Create a monitor in the console (its schedule = how often the script should run, plus a grace window), copy the ping URL, and wrap your entry point.

With requests

Success pings the monitor URL; an exception POSTs its text to /fail (stored with the ping, 10,000 bytes max) and re-raises so the process still exits non-zero:

import requests

PING = "https://ping.lastping.dev/<your-monitor-id>"

def main() -> None:
    ...  # your job

if __name__ == "__main__":
    try:
        main()
    except Exception as exc:
        requests.post(f"{PING}/fail", data=str(exc)[:10000], timeout=10)
        raise
    else:
        requests.get(PING, timeout=10)

Standard library only

Zero dependencies: urllib.request sends a GET for success and a POST (a Request with a body) for failure:

from urllib.request import Request, urlopen

PING = "https://ping.lastping.dev/<your-monitor-id>"

def main() -> None:
    ...  # your job

if __name__ == "__main__":
    try:
        main()
    except Exception as exc:
        urlopen(Request(f"{PING}/fail", data=str(exc)[:10000].encode()), timeout=10)
        raise
    else:
        urlopen(PING, timeout=10)

Optional: overrun detection and run duration

A /start ping arms the hung-job alarm (no success inside the grace window → incident), and a shared rid pairs start with finish so each run's duration is recorded:

import time

import requests

PING = "https://ping.lastping.dev/<your-monitor-id>"

def main() -> None:
    ...  # your job

if __name__ == "__main__":
    rid = str(int(time.time()))
    requests.get(f"{PING}/start", params={"rid": rid}, timeout=10)
    try:
        main()
    except Exception as exc:
        requests.post(f"{PING}/fail", data=str(exc)[:10000], timeout=10)
        raise
    else:
        requests.get(PING, params={"rid": rid}, timeout=10)

Why the re-raise matters

raise keeps the exit code non-zero, so whatever runs the script (cron, systemd, a Kubernetes CronJob) still sees the failure and applies its own retry semantics. The ping adds alerting; it never hides the error.

What this catches

The except reports what it can; the silence reports the rest.

  • An exception: the /fail ping opens an incident immediately, with the exception text stored on the ping.
  • A crash your code never sees: OOM kill, segfault in a C extension, kill -9. No success ping arrives; the grace window raises the alert.
  • The script never ran: missing cron entry, broken virtualenv path, dead host. Absence → incident.
  • A hung run: with the /start ping armed, a job stuck on a lock or a dead socket opens an incident inside the grace window.
  • Recovery: the next successful run closes the incident and sends a recovery notice automatically.

Questions people ask

Front-loaded answers: the most important fact first.

  • How do I monitor a Python cron job?

    Wrap the entry point in try/except: on success, requests.get the monitor URL https://ping.lastping.dev/<your-monitor-id>; on exception, requests.post the error text to the /fail endpoint and re-raise so the exit code stays non-zero. If the script fails, hangs, or never starts, LastPing alerts you by email, Slack, Discord, Telegram, ntfy, Pushover, Microsoft Teams, Google Chat or webhook.

  • Do I need a special library or SDK?

    No. A ping is one plain HTTP request: GET, HEAD, or POST. requests is convenient; the standard library's urllib.request works with zero dependencies.

  • What if my script crashes before it can ping?

    That's the point of dead-man's-switch monitoring: a script that segfaults, is OOM-killed, or never starts sends no ping, and the missing ping itself opens the incident after the grace window. The failure path needs no working code.

  • Can I see the exception in the alert trail?

    Yes. POST the exception text as the body of the /fail ping. Up to 10,000 bytes are stored with the ping event and retrievable via the API.

  • Does this work outside plain scripts?

    Anywhere Python runs and can make an HTTP request: a Django management command, a task in a worker, an ETL step, a notebook job. The ping doesn't care what invoked the code.

  • Is Python job monitoring free?

    Yes. LastPing is free for individuals with no monitor cap. See how it compares to Healthchecks.io and Cronitor.

FREE FOR INDIVIDUALS · FULLY HOSTED

Your except clause can't catch a missing process.

Six lines now beat a quiet data gap later. First alert wired up in about a minute, and LastPing monitors itself the same way.