# Get a text when your Python cron job fails

Wrap an existing Python script with SMS failure alerts. Use the Python standard library, keep the job's exit status, and schedule it with Linux cron.

## 1. Get your key and set the environment

Sign in with your own phone number and copy or create your key on the dashboard. Reuse your existing key if you already have one. Your key can only text your verified phone; there is no recipient field.

Python 3 is the only dependency. Set these variables in the terminal where you run the job, replacing the placeholder. Keep the key out of Git and browser code.

Terminal:

```bash
export SELFPING_API_KEY="sp_your_api_key"
export SELFPING_JOB_NAME="Nightly import"
```

Free accounts get 10 texts a month. Hobby is $5/month for 100. Each send uses one credit, and allowances refill on the 1st. Failure-only alerts help keep routine successful runs quiet.

## 2. Wrap your existing script

Save this as notify_job.py beside your script. It runs the command you pass, leaves its output in your logs, and sends one short alert if the command exits with an error or cannot start.

An alert failure does not replace the job's exit status. The HTTP request has a 10-second socket timeout and makes no automatic retries. A timeout can happen after acceptance, so check the dashboard before sending again.

notify_job.py:

```python
import json
import os
import subprocess
import sys
import urllib.error
import urllib.request


def notify(message):
    key = os.environ.get("SELFPING_API_KEY", "").strip()
    if not key or key == "sp_your_api_key":
        print("SelfPing: set SELFPING_API_KEY to enable alerts.", file=sys.stderr)
        return

    try:
        request = urllib.request.Request(
            "https://www.selfping.com/api/sms",
            data=json.dumps({"message": message[:160]}).encode("utf-8"),
            headers={
                "Authorization": f"Bearer {key}",
                "Content-Type": "application/json",
            },
            method="POST",
        )
        with urllib.request.urlopen(request, timeout=10) as response:
            response.read()
        print("SelfPing: alert accepted. Check delivery on your dashboard.")
    except urllib.error.HTTPError as error:
        print(f"SelfPing: alert rejected (HTTP {error.code}). Check your key, balance and API docs.", file=sys.stderr)
    except Exception:
        print("SelfPing: alert could not be confirmed. Check your dashboard before retrying.", file=sys.stderr)


def main():
    command = sys.argv[1:]
    if not command:
        print("Usage: python3 notify_job.py <command> [arguments...]", file=sys.stderr)
        return 2

    name = " ".join(os.environ.get("SELFPING_JOB_NAME", "Scheduled job").split())[:80] or "Scheduled job"
    try:
        code = subprocess.run(command, check=False).returncode
    except OSError:
        notify(f"{name} could not start. Check the job logs.")
        return 1

    if code != 0:
        notify(f"{name} failed (exit {code}). Check the job logs.")
    elif os.environ.get("SELFPING_NOTIFY_SUCCESS") == "1":
        notify(f"{name} finished successfully. Nothing needs you.")

    return code if code >= 0 else 128 - code


if __name__ == "__main__":
    sys.exit(main())
```

Your script must exit nonzero on failure. If it catches an error and exits successfully, the wrapper cannot detect that failure. Keep sensitive output in your logs, not in the SMS.

## 3. Try one deliberate failure

Run this once after setting your key. It intentionally exits with status 1, sends a real text to your phone, and uses one credit. This page itself never runs the example or sends a text.

Terminal: deliberate failure:

```bash
python3 notify_job.py python3 -c 'import sys; sys.exit(1)'
```

Terminal: your existing job:

```bash
python3 notify_job.py python3 job.py
```

Look for the text on your phone and the final delivery status on your dashboard. A successful API response means accepted for sending, not delivered. Successful jobs send no text unless you set SELFPING_NOTIFY_SUCCESS=1.

## 4. Schedule it with Linux cron

On your Linux server, put the files in /opt/my-job or substitute your actual project directory. Save the key in selfping.env and restrict access with chmod 600 /opt/my-job/selfping.env. Keep that file out of Git.

Cron does not load your interactive shell's exported variables. The entry below loads the environment file explicitly and runs every day at 9 a.m. in the cron host's configured timezone. Edit your user's crontab with crontab -e and add the entry once.

Replace /usr/bin/python3 with the absolute path to your Python interpreter. If the job uses a virtual environment, use its interpreter for job.py. Make sure the cron user can read the files and write job.log.

/opt/my-job/selfping.env:

```dotenv
SELFPING_API_KEY="sp_your_api_key"
SELFPING_JOB_NAME="Nightly import"
```

User crontab: one line:

```cron
0 9 * * * (cd /opt/my-job && . ./selfping.env && export SELFPING_API_KEY SELFPING_JOB_NAME && /usr/bin/python3 notify_job.py /usr/bin/python3 job.py) >> /opt/my-job/job.log 2>&1
```

Replace the existing schedule rather than adding a second copy of the same job. This wrapper reports jobs that start and return. It cannot report a server outage, a hung process, or a cron entry that never runs. Use a separate uptime or heartbeat monitor for those cases.

## 5. If the text does not arrive

Check job.log first. A missing-key message means the job's environment has no SELFPING_API_KEY. HTTP 401 means check your key. HTTP 429 can mean exhausted credits or a rate limit; consult the API documentation and your dashboard balance.

If the API accepted the text, check the dashboard for final delivery status. If the wrapper never ran, check your cron schedule, absolute paths, file permissions, and interpreter. Test the same command as the cron user.

For success notifications, add SELFPING_NOTIFY_SUCCESS=1 to your environment and export it in the cron entry too. A daily success text can exceed the free plan's 10 monthly texts.

Reading SMS replies is also available through GET /api/replies or the MCP get_replies tool with the same key. It costs no credits to read replies. See the complete reference for polling and retention limits.

## References

- [Get your key](https://www.selfping.com/dashboard)

- [Pricing](https://www.selfping.com/pricing)

- [API documentation](https://www.selfping.com/docs)

- [Replies reference](https://www.selfping.com/llms-full.txt)

- [Python HTTP requests](https://docs.python.org/3/library/urllib.request.html)

- [Python subprocess](https://docs.python.org/3/library/subprocess.html)

- [Linux crontab](https://man7.org/linux/man-pages/man5/crontab.5.html)