Documentation Index Fetch the complete documentation index at: https://checkly-422f444a-sync-playwright-reporter-changelog-v1-5-0.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Before diving into specific examples, remember these key principles:
Ping only on success - Only send pings when your task completes successfully
Include timeouts - Prevent hanging requests that could block your job
Add retries - Handle temporary network issues gracefully
Position correctly - Place pings at the very end of your success path
Use source headers - Help identify which system sent the ping
Shell Scripts and Command Line
Basic cURL Examples
The most common way to ping heartbeat monitors from shell scripts:
GET Request
POST Request
Error Handling
# 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!"
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 Router
Pages Router
SvelteKit/Other
// 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
Basic Example
Advanced Example
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 PowerShell
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, PATCHSolution : 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:
Manual ping test : Use the Checkly UI to send manual pings
Timeout test : Temporarily block network access to verify timeout behavior
Failure test : Force your job to fail and confirm no ping is sent
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.