Skip to main content

Cron Jobs

Schedule background tasks on Vercel with DirectoryLaunch — secure cron endpoints with CRON_SECRET, read the cron config and add your own jobs.

Cron jobs are scheduled HTTP requests that Vercel fires on a timetable. DirectoryLaunch ships with two.

What's scheduled

vercel.json
"crons": [
  { "path": "/api/cron/competitions",     "schedule": "15 8 * * *" },
  { "path": "/api/cron/winner-reminders", "schedule": "0 9 * * *" }
]
EndpointWhenWhat it does
/api/cron/competitions08:15 UTC dailyCloses competitions that have ended, picks winners, triggers notifications
/api/cron/winner-reminders09:00 UTC dailyEmails winners who haven't yet claimed their prize

Both paths are regular Next.js API routes under app/api/cron/. You can run them manually with curl — useful for testing.

How cron URLs are protected

Anyone on the internet could hit https://your-site.com/api/cron/competitions otherwise. To prevent that, every cron endpoint checks this header:

Authorization: Bearer ${CRON_SECRET}

Vercel automatically adds the header when it calls your cron endpoints, using the CRON_SECRET env var. If CRON_SECRET is missing or wrong → the endpoint returns 401 Unauthorized.

Generating a good CRON_SECRET

openssl rand -hex 32

Paste that into both .env.local and Vercel → Settings → Environment Variables.

Vercel plan limits

PlanCron rate limit
Hobby (free)1 cron, runs up to once per day
ProMultiple crons, any schedule
EnterpriseNo limits

If you're on Hobby, the default config will hit the limit. Either:

  • Keep one cron and disable the other
  • Upgrade to Pro

Reading cron schedules

The format is standard Unix cron: min hour day month weekday. Examples:

ExpressionWhen
0 * * * *Every hour on the hour
*/15 * * * *Every 15 minutes
0 9 * * *09:00 UTC daily
0 9 * * 109:00 UTC every Monday
0 0 1 * *Midnight UTC on the 1st of each month

Use crontab.guru to sanity-check.

UTC, always

Vercel cron schedules are UTC. Convert from your local time before scheduling.

Adding a new cron job

Fastest path — hand the whole job to AI:

Or do it by hand:

1. Create the endpoint

app/api/cron/my-task/route.ts
import { NextRequest } from 'next/server';
 
export async function GET(req: NextRequest) {
  // Verify the request is from Vercel cron
  const auth = req.headers.get('authorization');
  if (auth !== `Bearer ${process.env.CRON_SECRET}`) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }
 
  // Do your work
  // ...
 
  return Response.json({ ok: true });
}

2. Register the schedule

Edit vercel.json:

"crons": [
  { "path": "/api/cron/my-task", "schedule": "0 */6 * * *" }
]

3. Deploy

Vercel reads vercel.json on deploy and wires the schedule. Changes to schedules require a redeploy.

Testing locally

Manually hit your endpoint with the secret:

curl -H "Authorization: Bearer $CRON_SECRET" \
  http://localhost:3000/api/cron/competitions

It should return { "ok": true } (or whatever your handler returns).

Monitoring

Vercel Settings → Cron Jobs shows each cron's last run status and logs. Failures appear in your project's Logs tab with a cron- prefix.