How to Handle Webhooks in Cron Jobs and Scheduled Scripts
Cron jobs run on a schedule, not on-demand. Traditional 'Push' webhooks don't work for batch scripts. Learn how FetchHook's pull-mailbox model turns webhooks into a queue your scheduled scripts can consume.
Cron + FetchHook Pattern
# Run every 15 minutes
*/15 * * * * /usr/bin/python3 /home/user/process_webhooks.py
# process_webhooks.py pulls from FetchHook mailbox
# Processes all events that arrived since last run
# No server needed, no open ports#Why are webhooks difficult for cron jobs?
Webhooks are 'Push'-based: the sender calls your endpoint in real-time. But cron jobs are ephemeral—they wake up, do work, and shut down. You can't receive a 'Push' when you're not running. The traditional solution is to keep a server alive 24/7 just to accept webhooks, which defeats the simplicity of cron.
#When does it make sense to poll instead of push?
If your workflow is already batch-oriented (hourly reports, daily syncs, nightly processing), forcing real-time 'Push' webhooks adds unnecessary complexity. A pull-based pattern aligns perfectly with scheduled execution: your cron job wakes up, pulls all accumulated events from the mailbox, processes them in batch, and exits.
#How does FetchHook's pull mailbox model work with cron jobs?
FetchHook acts as a persistent buffer. When webhooks arrive from your external service (Stripe, GitHub, Shopify, etc.), they're stored in your private mailbox for up to 24 hours. Your cron job simply fetches the latest events via API, processes them, and FetchHook marks them as consumed.
Python Cron Script Example
import requests
import os
API_KEY = os.getenv("FETCHHOOK_API_KEY")
SOURCE_ID = "stash_abc123"
def process_batch():
# Pull all events accumulated since last run
response = requests.get(
f"https://api.fetchhook.app/api/v1/{SOURCE_ID}",
headers={"Authorization": f"Bearer {API_KEY}"}
)
if response.status_code != 200:
print(f"Failed to fetch events: {response.status_code}")
return
events = response.json().get("events", [])
for event in events:
event_type = event["payload"].get("type", "unknown")
print(f"Processing: {event_type}")
# Your batch logic here
# - Update database
# - Generate report
# - Send notifications
print(f"Processed {len(events)} events")
if __name__ == "__main__":
process_batch()#Is this more cost-effective than pure polling?
Yes. Pure polling means hitting the source API every N minutes whether there's new data or not. With FetchHook, the source service pushes to your mailbox (free ingress), and your cron job only fetches when it runs. You get the efficiency of webhooks without the infrastructure burden of a 24/7 server.
Cost Comparison
Approach | Server Cost | API Calls/Day | Complexity
------------------ | ----------- | ------------- | ----------
Traditional Polling| $0 | 1,440 | Low
24/7 Webhook Server| $5-50/mo | 0 | High
FetchHook + Cron | $0 | 96 | Low#What are real-world scenarios where this pattern shines?
This pattern is ideal for: (1) Nightly database syncs triggered by Stripe webhooks, (2) Hourly report generation from GitHub activity, (3) Daily batch processing of e-commerce orders from Shopify, (4) Scheduled data pipeline tasks that need webhook inputs without maintaining infrastructure.
Node.js Cron Example (Hourly Sync)
const axios = require('axios');
const API_KEY = process.env.FETCHHOOK_API_KEY;
const SOURCE_ID = 'stash_abc123';
async function hourlySync() {
try {
const response = await axios.get(
`https://api.fetchhook.app/api/v1/${SOURCE_ID}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const events = response.data.events || [];
// Process events in batch
for (const event of events) {
await processEvent(event);
}
console.log(`Synced ${events.length} events`);
} catch (error) {
console.error('Sync failed:', error.message);
process.exit(1);
}
}
async function processEvent(event) {
// Your sync logic: update DB, trigger workflows, etc.
console.log(`Processing ${event.payload.type}`);
}
hourlySync();Agent Protocol Instruction