How to Run Scheduled AI Agents on Vercel with Cron Jobs
Use Vercel's built-in cron triggers to run AI agent workflows on a schedule, without spinning up a separate worker service.
How to Run Scheduled AI Agents on Vercel with Cron Jobs
If your application already lives on Vercel, adding a scheduled AI agent does not require a separate Railway worker or a standalone cron service. Vercel has native cron support built into its serverless functions. You define a schedule in one config file, write a standard API route handler, and Vercel calls it on the interval you set.
This tutorial shows you how to set that up end to end: a Next.js API route that runs an AI agent task, secured with a secret header, triggered on a cron schedule, and observable through Vercel's log dashboard.
What you will need
- A Vercel account (cron jobs are available on the Hobby plan, limited to one per day; Pro unlocks more)
- A Next.js project deployed to Vercel
- An OpenAI or Anthropic API key
- Basic familiarity with API routes in Next.js
Step 1: Understand how Vercel cron works
Vercel cron sends a GET request to an API route in your application at the schedule you define. The route runs like any other serverless function, with a maximum execution time of 10 seconds on Hobby or 15 minutes on Pro.
The schedule and the route are linked in vercel.json. You do not need an external scheduler.
One important detail: Vercel sends requests to cron routes from its own infrastructure, not from a browser. You protect the route with a CRON_SECRET environment variable that Vercel automatically includes as an Authorization header on every cron call. Your handler checks for it and rejects anything that does not match.
Step 2: Create the API route
Create the file at app/api/agent/route.ts (App Router) or pages/api/agent.ts (Pages Router).
Here is the App Router version:
// app/api/agent/route.ts
import { NextRequest, NextResponse } from "next/server";
export const maxDuration = 300; // seconds, requires Pro for values over 10
export async function GET(req: NextRequest) {
const authHeader = req.headers.get("authorization");
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
try {
await runAgentTask();
return NextResponse.json({ ok: true, ran: new Date().toISOString() });
} catch (err) {
console.error("Agent task failed:", err);
return NextResponse.json({ error: "Task failed" }, { status: 500 });
}
}
async function runAgentTask() {
// Replace with your actual agent logic
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY!,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-3-5-haiku-20241022",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Check the task queue and process the next pending item.",
},
],
}),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
console.log("Agent response:", data.content[0].text);
}
The maxDuration export tells Vercel how long to keep the function alive. On Hobby, the hard cap is 10 seconds. On Pro, you can set it up to 900 seconds (15 minutes). For agents doing substantial work, Pro is required.
Step 3: Configure the cron schedule
Add or update vercel.json in your project root:
{
"crons": [
{
"path": "/api/agent",
"schedule": "0 8 * * *"
}
]
}
The schedule field accepts standard 5-field cron syntax. Examples:
| Schedule | Meaning |
|---|---|
| 0 8 * * * | Every day at 8am UTC |
| */30 * * * * | Every 30 minutes |
| 0 9 * * 1-5 | Weekdays at 9am UTC |
| 0 */4 * * * | Every 4 hours |
Vercel cron uses UTC. Adjust accordingly for your timezone.
To add multiple agent tasks, add multiple entries to the crons array pointing to different routes.
Step 4: Set environment variables
In the Vercel dashboard, go to your project, then Settings, then Environment Variables. Add:
CRON_SECRET: a random string you generate, for example:
openssl rand -hex 32
ANTHROPIC_API_KEY: your Anthropic API key- Any other secrets your agent logic needs
Vercel injects these at runtime. They are not visible in your source code or build output.
After adding or changing environment variables, redeploy the project for them to take effect on existing functions.
Step 5: Deploy and verify
Push your changes to the branch connected to Vercel:
git add vercel.json app/api/agent/route.ts
git commit -m "feat: add scheduled agent cron route"
git push
After the deploy completes, go to the Vercel dashboard for your project. Under the Cron Jobs tab, you will see your scheduled routes listed with their next run time.
To trigger a manual test run without waiting for the schedule, call the route directly with your secret:
curl -H "Authorization: Bearer YOUR_CRON_SECRET" \
https://your-project.vercel.app/api/agent
You should get back {"ok":true,"ran":"..."}. Check the Vercel Logs tab to see the function output, including anything you wrote to console.log.
Step 6: Handle errors and timeouts
Serverless functions can fail silently if you are not watching. Add two safeguards:
Log structured output. Instead of plain console.log, log JSON so you can filter in Vercel's log viewer:
console.log(JSON.stringify({ event: "agent_tick", status: "success", duration_ms: elapsed }));
console.error(JSON.stringify({ event: "agent_tick", status: "error", message: err.message }));
Set a fetch timeout. Vercel will kill your function when maxDuration is reached, but a hanging external API call wastes the entire window. Add an AbortController:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 20_000); // 20 second limit
const response = await fetch(url, {
signal: controller.signal,
// ... other options
});
clearTimeout(timeout);
This ensures your function returns a clean error rather than hitting the hard timeout.
Step 7: Check cron execution history
In the Vercel dashboard, the Cron Jobs tab shows a history of recent executions: timestamp, duration, and status code. A 200 means the function ran and returned successfully. A 401 means the authorization check failed (wrong or missing secret). A 500 means the agent task threw an error.
For proactive alerting when cron jobs fail, pair this with an uptime monitor that calls your route on a known schedule and alerts you when it stops responding. The monitoring setup is covered in a separate tutorial.
When to use Vercel cron vs. a dedicated worker
Vercel cron is the right choice when:
- Your application is already on Vercel and you want one fewer service to manage
- The task completes in under 5 minutes (comfortable within Pro limits)
- The schedule aligns with cron expressions (not event-driven)
A dedicated Railway worker (covered in the Railway deployment tutorial) is a better fit when:
- The task runs continuously rather than on a schedule
- You need more than 15 minutes of execution time per run
- You want full control over the runtime environment and dependencies
Both patterns are valid. The Vercel approach adds zero infrastructure. The worker approach adds flexibility. For most scheduled AI agent tasks, the Vercel cron approach is enough to get started.
What to build next
Once your cron route is running, the next useful additions are:
- A Postgres or Redis task queue so your cron route picks up work items rather than hardcoding logic into the handler. This lets you add work items from other parts of your application and have the cron runner process them.
- A
lastRuntimestamp stored in your database so you can detect missed executions from within your application, not just from the Vercel dashboard. - A separate
/api/agent/statusroute that returns the last execution time and result, so your monitoring layer has something to poll.
The cron configuration is a single JSON entry. The work is in making the agent logic reliable enough to run unattended.