Before diving into specific examples, remember these key principles:
  1. Ping only on success - Only send pings when your task completes successfully
  2. Include timeouts - Prevent hanging requests that could block your job
  3. Add retries - Handle temporary network issues gracefully
  4. Position correctly - Place pings at the very end of your success path
  5. Use source headers - Help identify which system sent the ping
All examples include timeout and retry configurations to prevent false alerts caused by temporary network issues. Always include these safeguards in production implementations.

Shell Scripts and Command Line

Basic cURL Examples

The most common way to ping heartbeat monitors from shell scripts:
# Basic GET request with timeout and retry
curl -m 5 --retry 3 https://ping.checklyhq.com/your-heartbeat-id

# With source identification
curl -m 5 --retry 3 \
  -H "Origin: backup-server-prod" \
  https://ping.checklyhq.com/your-heartbeat-id

# Silent operation (no output)
curl -m 5 --retry 3 -s \
  https://ping.checklyhq.com/your-heartbeat-id

wget Alternative

If cURL isn’t available, use wget instead:
# Basic wget with timeout and retry
wget -T 5 -t 3 -q -O /dev/null \
  https://ping.checklyhq.com/your-heartbeat-id

# In a complete backup script
#!/bin/bash
set -e  # Exit on any error

echo "Starting database backup..."
pg_dump production_db > /tmp/backup.sql

echo "Uploading to S3..."
aws s3 cp /tmp/backup.sql s3://backups/$(date +%Y%m%d).sql

echo "Cleaning up..."
rm /tmp/backup.sql

echo "Sending success ping..."
wget -T 5 -t 3 -q -O /dev/null \
  https://ping.checklyhq.com/your-heartbeat-id

echo "Backup completed successfully!"

Platform-Specific Integrations

Heroku Scheduler

Monitor Heroku scheduled tasks:
# In your Heroku Scheduler command
run_task.sh && curl -m 5 --retry 3 https://ping.checklyhq.com/your-heartbeat-id

Render Cron Jobs

For Render cron job services:
#!/bin/bash
# Render cron job script

# Run your task
python process_data.py

# Only ping if successful (exit code 0)
if [ $? -eq 0 ]; then
  curl -m 5 --retry 3 https://ping.checklyhq.com/your-heartbeat-id
fi

Railway Cron Jobs

Similar pattern for Railway scheduled deployments:
# In your railway cron script
npm run data-sync && \
curl -m 5 --retry 3 https://ping.checklyhq.com/your-heartbeat-id

Kubernetes Cron Jobs

Basic CronJob with Heartbeat

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-backup
  namespace: production
spec:
  schedule: "0 2 * * *"  # 2 AM daily
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup-job
            image: your-registry/backup-job:latest
            command:
            - /bin/sh
            - -c
            args:
            - |
              echo "Running backup job..."
              
              # Run your actual backup logic
              /scripts/run-backup.sh
              
              # Only ping if backup succeeded
              if [ $? -eq 0 ]; then
                echo "Backup successful, sending heartbeat..."
                curl -m 5 --retry 3 \
                  -H "Origin: k8s-cluster-prod" \
                  https://ping.checklyhq.com/your-heartbeat-id
              else
                echo "Backup failed, no heartbeat sent"
                exit 1
              fi
            env:
            - name: HEARTBEAT_URL
              valueFrom:
                secretKeyRef:
                  name: heartbeat-secrets
                  key: backup-heartbeat-url
          restartPolicy: OnFailure
          backoffLimit: 2

Advanced CronJob with Sidecar

For more complex scenarios, use a sidecar pattern:
apiVersion: batch/v1
kind: CronJob
metadata:
  name: data-processing
spec:
  schedule: "0 */6 * * *"  # Every 6 hours
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          # Main job container
          - name: processor
            image: your-app/data-processor:latest
            command: ["/app/process-data"]
            volumeMounts:
            - name: shared-status
              mountPath: /shared
              
          # Heartbeat sidecar
          - name: heartbeat
            image: curlimages/curl:latest
            command:
            - /bin/sh
            - -c
            - |
              # Wait for main container to finish
              while [ ! -f /shared/status ]; do
                sleep 5
              done
              
              # Check if job succeeded
              if [ "$(cat /shared/status)" = "success" ]; then
                curl -m 5 --retry 3 \
                  https://ping.checklyhq.com/your-heartbeat-id
              fi
            volumeMounts:
            - name: shared-status
              mountPath: /shared
              
          volumes:
          - name: shared-status
            emptyDir: {}
          restartPolicy: OnFailure

Node.js and JavaScript

Built-in HTTPS Module

import https from 'https';

async function pingHeartbeat(url: string): Promise<void> {
  return new Promise((resolve, reject) => {
    const options = {
      timeout: 5000,
    };

    const req = https.get(url, options, (res) => {
      console.log(`Heartbeat ping status: ${res.statusCode}`);
      resolve();
    });

    req.on('error', (error) => {
      console.error(`Heartbeat ping failed: ${error.message}`);
      reject(error);
    });

    req.on('timeout', () => {
      req.destroy();
      reject(new Error('Heartbeat ping timeout'));
    });
  });
}

// Usage in your job
async function runScheduledJob() {
  try {
    // Your job logic here
    await processData();
    await generateReports();
    
    // Only ping on success
    await pingHeartbeat('https://ping.checklyhq.com/your-heartbeat-id');
    console.log('Job completed successfully');
  } catch (error) {
    console.error('Job failed:', error);
    // Don't ping on failure - let heartbeat monitor alert
    process.exit(1);
  }
}

Using Axios

import axios from 'axios';

const HEARTBEAT_URL = 'https://ping.checklyhq.com/your-heartbeat-id';

async function pingHeartbeat(source?: string): Promise<void> {
  try {
    const headers: Record<string, string> = {};
    if (source) {
      headers['Origin'] = source;
    }

    const response = await axios.get(HEARTBEAT_URL, {
      timeout: 5000,
      headers,
      // Axios retry configuration
      validateStatus: (status) => status < 500, // Don't throw on 4xx
    });
    
    console.log(`Heartbeat sent successfully: ${response.status}`);
  } catch (error) {
    console.error('Failed to send heartbeat:', error.message);
    throw error;
  }
}

// In your scheduled job
async function scheduledTask() {
  try {
    console.log('Starting scheduled task...');
    
    // Your job logic
    await syncDatabase();
    await updateCache();
    await sendReports();
    
    // Send success ping
    await pingHeartbeat('newsletter-service');
    console.log('Scheduled task completed successfully');
    
  } catch (error) {
    console.error('Scheduled task failed:', error);
    // Exit with error code - don't send heartbeat
    process.exit(1);
  }
}

Fetch API (Modern Browsers/Node.js 18+)

async function pingHeartbeat(url, source = null) {
  const headers = {
    'Content-Type': 'application/json'
  };
  
  if (source) {
    headers['Origin'] = source;
  }

  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 5000);

    const response = await fetch(url, {
      method: 'GET',
      headers,
      signal: controller.signal
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      throw new Error(`Heartbeat failed: ${response.status}`);
    }

    console.log('Heartbeat sent successfully');
  } catch (error) {
    if (error.name === 'AbortError') {
      console.error('Heartbeat request timed out');
    } else {
      console.error('Heartbeat request failed:', error);
    }
    throw error;
  }
}

Vercel Cron Jobs

Monitor your Vercel cron jobs across different routing patterns:
// app/api/cron/newsletter/route.js
export async function GET(request) {
  try {
    console.log('Starting newsletter job...');
    
    // Your cron job logic
    const subscribers = await getSubscribers();
    await sendNewsletter(subscribers);
    await updateMetrics();
    
    // Send success heartbeat
    const heartbeatUrl = 'https://ping.checklyhq.com/your-heartbeat-id';
    const response = await fetch(heartbeatUrl, {
      method: 'GET',
      headers: {
        'Origin': 'vercel-newsletter'
      }
    });
    
    console.log(`Heartbeat sent: ${response.status}`);
    
    return new Response(JSON.stringify({ 
      success: true, 
      processed: subscribers.length 
    }), {
      status: 200,
      headers: { 'Content-Type': 'application/json' }
    });
    
  } catch (error) {
    console.error('Newsletter job failed:', error);
    
    // Don't send heartbeat on failure
    return new Response(JSON.stringify({ 
      success: false, 
      error: error.message 
    }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' }
    });
  }
}

Python Examples

Using Requests Library

import requests
import sys
import time

def ping_heartbeat(url, source=None, timeout=5, retries=3):
    """Send heartbeat ping with retry logic."""
    headers = {}
    if source:
        headers['Origin'] = source
    
    for attempt in range(retries):
        try:
            response = requests.get(url, headers=headers, timeout=timeout)
            response.raise_for_status()
            print(f"Heartbeat sent successfully: {response.status_code}")
            return True
            
        except requests.exceptions.RequestException as e:
            print(f"Heartbeat attempt {attempt + 1} failed: {e}")
            if attempt < retries - 1:
                time.sleep(1)  # Wait before retry
            
    print("All heartbeat attempts failed")
    return False

def run_scheduled_job():
    """Example scheduled job with heartbeat monitoring."""
    heartbeat_url = "https://ping.checklyhq.com/your-heartbeat-id"
    
    try:
        print("Starting data processing job...")
        
        # Your job logic here
        process_data()
        generate_reports()
        cleanup_temp_files()
        
        print("Job completed successfully")
        
        # Send success heartbeat
        if not ping_heartbeat(heartbeat_url, source="data-processor"):
            print("Warning: Failed to send heartbeat ping")
            
    except Exception as e:
        print(f"Job failed: {e}")
        # Don't send heartbeat on failure
        sys.exit(1)

if __name__ == "__main__":
    run_scheduled_job()

Django Management Commands

# management/commands/send_newsletter.py
from django.core.management.base import BaseCommand
from django.conf import settings
import requests

class Command(BaseCommand):
    help = 'Send weekly newsletter'
    
    def handle(self, *args, **options):
        try:
            self.stdout.write('Starting newsletter job...')
            
            # Newsletter logic
            subscribers = self.get_subscribers()
            sent_count = self.send_newsletters(subscribers)
            
            self.stdout.write(f'Newsletter sent to {sent_count} subscribers')
            
            # Send heartbeat
            heartbeat_url = settings.NEWSLETTER_HEARTBEAT_URL
            response = requests.get(heartbeat_url, timeout=5)
            response.raise_for_status()
            
            self.stdout.write(
                self.style.SUCCESS('Newsletter job completed successfully')
            )
            
        except Exception as e:
            self.stdout.write(
                self.style.ERROR(f'Newsletter job failed: {e}')
            )
            raise

PowerShell Examples

For Windows environments and Azure functions:
# Basic heartbeat ping with error handling
function Send-HeartbeatPing {
    param(
        [string]$Url,
        [string]$Source = $null,
        [int]$TimeoutSec = 5,
        [int]$MaxRetries = 3
    )
    
    $headers = @{}
    if ($Source) {
        $headers["Origin"] = $Source
    }
    
    for ($i = 0; $i -lt $MaxRetries; $i++) {
        try {
            $response = Invoke-RestMethod -Uri $Url -Headers $headers -TimeoutSec $TimeoutSec -MaximumRetryCount 0
            Write-Host "Heartbeat sent successfully"
            return $true
        }
        catch {
            Write-Warning "Heartbeat attempt $($i + 1) failed: $($_.Exception.Message)"
            if ($i -lt ($MaxRetries - 1)) {
                Start-Sleep -Seconds (2 * ($i + 1))
            }
        }
    }
    
    Write-Error "All heartbeat attempts failed"
    return $false
}

# Example backup script
try {
    Write-Host "Starting backup process..."
    
    # Your backup logic
    & "C:\Scripts\DatabaseBackup.exe"
    if ($LASTEXITCODE -ne 0) {
        throw "Database backup failed with exit code $LASTEXITCODE"
    }
    
    & "C:\Scripts\UploadToCloud.exe"
    if ($LASTEXITCODE -ne 0) {
        throw "Cloud upload failed with exit code $LASTEXITCODE"
    }
    
    Write-Host "Backup completed successfully"
    
    # Send heartbeat
    $heartbeatUrl = "https://ping.checklyhq.com/your-heartbeat-id"
    Send-HeartbeatPing -Url $heartbeatUrl -Source "windows-backup-server"
    
} catch {
    Write-Error "Backup process failed: $($_.Exception.Message)"
    exit 1
}

Advanced Patterns

Conditional Heartbeats

Sometimes you only want to ping under certain conditions:
def conditional_heartbeat_example():
    """Only ping heartbeat if certain conditions are met."""
    
    # Run your job
    results = process_data()
    
    # Only ping if we processed a significant amount of data
    if results['processed_count'] > 100:
        ping_heartbeat(
            url="https://ping.checklyhq.com/your-heartbeat-id",
            metadata={'processed': results['processed_count']}
        )
        print(f"Heartbeat sent - processed {results['processed_count']} items")
    else:
        print(f"Skipped heartbeat - only processed {results['processed_count']} items")

Multi-Step Job Monitoring

For complex jobs with multiple phases:
#!/bin/bash
set -e

HEARTBEAT_URL="https://ping.checklyhq.com/your-heartbeat-id"
JOB_FAILED=false

# Function to send heartbeat on success
send_success_heartbeat() {
    if [ "$JOB_FAILED" = false ]; then
        curl -m 5 --retry 3 -H "Origin: multi-step-job" "$HEARTBEAT_URL"
        echo "Success heartbeat sent"
    fi
}

# Trap to ensure heartbeat is sent on successful completion
trap send_success_heartbeat EXIT

# Step 1: Download data
echo "Step 1: Downloading data..."
if ! download_data.sh; then
    echo "Data download failed"
    JOB_FAILED=true
    exit 1
fi

# Step 2: Process data  
echo "Step 2: Processing data..."
if ! process_data.sh; then
    echo "Data processing failed"
    JOB_FAILED=true
    exit 1
fi

# Step 3: Upload results
echo "Step 3: Uploading results..."
if ! upload_results.sh; then
    echo "Results upload failed" 
    JOB_FAILED=true
    exit 1
fi

echo "All steps completed successfully"
# Heartbeat will be sent by the EXIT trap

Troubleshooting

Common Issues

Problem: Pings fail due to network timeouts or slow connections.Solution: Always configure timeouts and retries:
# Good: With timeout and retry
curl -m 5 --retry 3 --retry-delay 2 https://ping.checklyhq.com/your-id

# Add connection timeout too
curl --connect-timeout 10 -m 30 --retry 3 https://ping.checklyhq.com/your-id
Problem: Pings are ignored due to blocked user agents.Blocked user agents: Twitterbot, Slackbot, Googlebot, Discordbot, Facebot, TelegramBot, WhatsApp, LinkedInBotSolution: Use custom user agents:
curl -A "MyApp/1.0" https://ping.checklyhq.com/your-id
Problem: Using unsupported HTTP methods.Supported: GET, POST
Not supported: PUT, DELETE, PATCH
Solution: Stick to GET or POST:
# Good
requests.get(heartbeat_url)
requests.post(heartbeat_url, json=metadata)

# Bad - will return error
requests.put(heartbeat_url)
Problem: Pings sent even when job fails.Solution: Structure your code correctly:
# Good: Ping only on success
try:
    run_job()
    ping_heartbeat()  # Only reached if run_job() succeeds
except Exception:
    # Don't ping on failure
    logging.error("Job failed")
    
# Bad: Always pings
try:
    run_job()
except Exception:
    logging.error("Job failed")
finally:
    ping_heartbeat()  # Always runs!

Testing Your Implementation

Before deploying, test your heartbeat integration:
  1. Manual ping test: Use the Checkly UI to send manual pings
  2. Timeout test: Temporarily block network access to verify timeout behavior
  3. Failure test: Force your job to fail and confirm no ping is sent
  4. Retry test: Add temporary network delays to test retry logic
Start with a short grace period (like 5 minutes) while testing, then increase it to your production requirements once you’re confident in the timing.