Cron Jobs Without a Server: HTTP Scheduling for JAMstack and Serverless

You shipped your app on a modern stack — Next.js on Vercel, a database on Supabase or PlanetScale, auth with Clerk. No servers. No DevOps.

Then you need a cron job.

Suddenly you’re looking at AWS, reading about EventBridge Scheduler IAM policies, or spinning up a $5/month VPS just to run crontab -e. The modern stack doesn’t have a good answer for “do this thing every hour.”

Until now.

What If Cron Was Just a URL?

# Run a cleanup job every day at midnight UTC
curl -X POST "https://zeplo.to/https://your-app.com/api/cleanup?_cron=0|0|*|*|*&_token=YOUR_TOKEN"

That’s a complete cron job. No server, no scheduler service, no YAML config. Zeplo calls your endpoint on the cron schedule you specify. If your endpoint fails, it retries automatically.

Common Cron Patterns

# Every hour
_cron=0|*|*|*|*

# Every weekday at 9am UTC
_cron=0|9|*|*|1-5

# Every Monday at 8am UTC
_cron=0|8|*|*|1

# First day of every month at midnight
_cron=0|0|1|*|*

# Every 15 minutes
_cron=*/15|*|*|*|*

The syntax is standard cron but with pipes (|) instead of spaces since it lives in a URL parameter.

Real Use Cases

Database Cleanup

// /app/api/cleanup/route.ts
export async function POST() {
  // Delete expired sessions
  await db.sessions.deleteMany({
    where: { expiresAt: { lt: new Date() } }
  });
  
  // Archive old notifications
  const thirtyDaysAgo = new Date(Date.now() - 30 * 86400 * 1000);
  await db.notifications.updateMany({
    where: { createdAt: { lt: thirtyDaysAgo }, archived: false },
    data: { archived: true },
  });
  
  return Response.json({ cleaned: true });
}

Daily Reports

// /app/api/daily-report/route.ts
export async function POST() {
  const yesterday = getYesterdayRange();
  
  const stats = {
    signups: await db.users.count({ where: { createdAt: yesterday } }),
    orders: await db.orders.count({ where: { createdAt: yesterday } }),
    revenue: await db.orders.aggregate({ _sum: { amount: true }, where: { createdAt: yesterday } }),
  };
  
  await sendSlackMessage('#metrics', formatReport(stats));
  
  return Response.json(stats);
}

Subscription Renewal Checks

// /app/api/check-renewals/route.ts
export async function POST() {
  const expiringSoon = await db.subscriptions.findMany({
    where: {
      expiresAt: { lt: new Date(Date.now() + 3 * 86400 * 1000) },
      reminderSent: false,
    },
  });
  
  for (const sub of expiringSoon) {
    await sendEmail(sub.user.email, 'renewal-reminder', { daysLeft: getDaysUntil(sub.expiresAt) });
    await db.subscriptions.update({ where: { id: sub.id }, data: { reminderSent: true } });
  }
  
  return Response.json({ reminded: expiringSoon.length });
}

Setting Up Cron Jobs

You have two options:

Option 1: One-time setup via curl

Run this once to register the cron schedule:

curl -X POST "https://zeplo.to/https://your-app.com/api/cleanup?_cron=0|0|*|*|*&_retry=3&_token=YOUR_TOKEN"

Option 2: Set up during deployment

Add a post-deploy script that registers your schedules:

// scripts/setup-cron.js
const ZEPLO_TOKEN = process.env.ZEPLO_TOKEN;
const APP_URL = process.env.NEXT_PUBLIC_URL;

const schedules = [
  { path: '/api/cleanup', cron: '0|0|*|*|*' },           // Daily midnight
  { path: '/api/daily-report', cron: '0|9|*|*|1-5' },    // Weekdays 9am
  { path: '/api/check-renewals', cron: '0|*/6|*|*|*' },  // Every 6 hours
];

for (const { path, cron } of schedules) {
  await fetch(`https://zeplo.to/${APP_URL}${path}?_cron=${cron}&_retry=3&_token=${ZEPLO_TOKEN}`, {
    method: 'POST',
  });
  console.log(`Scheduled: ${path} (${cron})`);
}

Cron + Delay: Powerful Combinations

Combine cron schedules with delays for sophisticated workflows:

// Every Monday, send a weekly summary — but delay 30 minutes
// so the data pipeline finishes first
await fetch(
  `https://zeplo.to/https://your-app.com/api/weekly-summary?_cron=0|9|*|*|1&_delay=1800&_token=${TOKEN}`,
  { method: 'POST' }
);

Managing Schedules

All your cron schedules are visible in the Zeplo dashboard. You can:

  • See all active schedules and their next run time
  • View execution history with full request/response logs
  • Pause or delete schedules
  • Manually trigger a scheduled job for testing

Comparison With Alternatives

Approach Setup Time Cost Delay Support Retry Dashboard
VPS + crontab Hours $5-20/mo No DIY No
AWS EventBridge Hours ~$1/mo No Limited CloudWatch
GitHub Actions Minutes Free (limited) No No Yes
Vercel Cron Minutes Free (limited) No No Logs only
Zeplo Seconds Free (500/mo) Yes Yes Yes

Getting Started

  1. Sign up free
  2. Create an API endpoint in your app that does the work
  3. Register the schedule with one curl command

Your cron job is running. No servers harmed.


Zeplo turns any HTTP endpoint into a scheduled job. Add cron, delay, and retry to your serverless app. Try it free →

Return to Blog