If you only need a steady ping to a public route on a fixed cadence, Cron Jobs can stay while Schedules is in beta - but Schedules is where the roadmap lives: per-schedule IANA time zones, private function targets, runtime schedules aimed at Queue topics, and toggles without redeploying. Paid usage is metered ($5 per 1M runs and $10 per 1M creations on Pro/Enterprise as of 6 November 2026, per Vercel's changelog). Migration is mostly a vercel.json reshape; the foot-gun is POST-by-default path schedules versus legacy GET Cron hits, and never keep crons and schedules for the same work or you fire twice.
Sources I used: changelog (5 Nov 2026), blog (4 Nov 2026), limits, migration, all read 6 November 2026 - a snapshot, not your invoice.
What Vercel launched (4–5 November 2026)
Vercel introduced Schedules on 4 November 2026 and called public beta on 5 November 2026. Schedules is the successor to Cron Jobs; existing crons keep running and a migration guide exists - no forced cutover.
Cron Jobs were mostly "path + cron in UTC". Schedules keeps static config in vercel.json (or Build Output API), adds runtime create/cancel via @vercel/schedules (SDK/CLI/API), and defaults new Preview static schedules to inactive so a branch doesn't wake production chores by accident.
Comparison table: Schedules vs Cron Jobs
| Vercel Schedules (public beta, Nov 2026) | Legacy Cron Jobs | |
|---|---|---|
| Status | Public beta; successor product, active development | Still supported; existing configs keep running |
| Static config | schedules object in vercel.json / Build Output API | crons array in vercel.json |
| Time zone | IANA time zone per schedule | UTC only |
| HTTP method (path targets) | Path schedules default to POST | Cron Jobs used GET |
| Private targets | Function targets with experimentalTriggers type schedule/v1beta - no public URL, scheduler invokes only | Path-based hits are public; protect with CRON_SECRET etc. |
| Dynamic schedules | Runtime create/change/cancel (one-off and recurring) via SDK/CLI/API; dynamic schedules target Queue topics | Not a first-class runtime API in the same way |
| Enable/disable | Toggle and run on demand without redeploying | Changes typically tied to config/deploy patterns you already know |
| Preview / custom env | New static schedules start inactive on Preview to avoid surprise prod work | Same class of "watch what fires where" concerns, less built-in guard |
| Jitter window | Hobby: 15 minutes; Pro/Enterprise: default 1 minute, configurable 1–15 | Cron Jobs on Hobby: at most once per day, hour precision; Schedules on Hobby at most every 15 minutes (per Vercel's docs snapshot) |
| Retries | Functions and paths: no automatic retries. Queue topics: retries apply | Side effects still hinge on your handler design |
| Delivery guarantees | Duplicates possible - handlers must be idempotent; use execution ID | Treat as at least once |
Sources: limits, migration, changelog, 6 November 2026.
Static vs dynamic schedules (and targets)
Static schedules (in git): named vercel.json entries on a cron string. Targets: private Function, HTTP path, or Queue topic. Private functions use experimentalTriggers with type schedule/v1beta - no public URL; only the scheduler invokes them. Path targets are normal HTTP and stay public unless you add real auth.
Gotcha: path schedules default to POST; Cron Jobs used GET. The migration guide tells you to set requestMethod to GET when porting handlers that still expect GET.
Dynamic schedules (runtime via SDK/CLI/API) create one-time or recurring work against Queue topics - useful when an AI flow should enqueue a follow-up instead of polling a table. Pair that with durable AI agents: schedules handle when; queues and idempotency handle exactly-once in the real world.
A compact static sketch with a private function, IANA time zone, and the experimentalTriggers hook (shape from Vercel's docs; adapt names to your project):
{
"functions": {
"api/reports/scheduled.ts": {
"experimentalTriggers": [{ "type": "schedule/v1beta" }]
}
},
"schedules": {
"nightly-report": {
"schedule": "0 7 * * *",
"timezone": "Europe/Madrid",
"function": "api/reports/scheduled"
}
}
}
The handler side uses @vercel/schedules (short illustrative example):
import { handleSchedule } from "@vercel/schedules";
export const POST = handleSchedule(async (metadata) => {
// idempotent work using metadata.executionId in your datastore
console.log(`${metadata.name} run ${metadata.executionId}`);
});
Dynamic scheduling targets a Queue topic and can carry a payload - useful for agent follow-ups ("remind me to re-check this lead in 36 hours") without cron-sprawl in your database:
import { Schedules } from "@vercel/schedules";
await Schedules.create({
name: "follow-up-lead-4821",
// topic must match your Queue consumer wiring
target: { topic: "agent-followups" },
expression: { type: "cron", cron: "0 9 * * 1-5" },
timezone: "Europe/Madrid",
payload: { leadId: "4821", step: "second-touch" },
});
If you already run site work through one disciplined agent profile, AI webmaster agent is the playbook-shaped version of that idea - runtime schedules plus a queue are the infrastructure-shaped version.
Pricing and limits (6 November 2026)
From the Schedules changelog and limits (6 November 2026):
- Pro / Enterprise: usage billed from $5 per 1M runs and $10 per 1M creations (metered beyond what your plan includes - check the pricing page for included credits, I am not inventing them).
- Hobby included monthly: 500 schedule creations and 5,000 runs; over that, Hobby blocks creates/runs until the next billing period (upgrade to Pro to lift the cap).
- Schedules per team: 1,000 (Hobby), 10,000 (Pro), 100,000 (Enterprise). On Hobby, a deployment can declare up to 100 static schedules.
- Payload: 16 KB. Execution history: 90 days.
Cron Jobs still bill as Function invocations. Hobby Cron Jobs: ≤100 crons/project, ≤once/day with hour precision; Pro/Enterprise Cron Jobs can go ≤once/minute (per Vercel's Cron docs - verify if you are frequency-bound).
Jitter: Schedules on Hobby use a 15-minute window; Pro/Enterprise default 1 minute, tunable 1–15 minutes. Check your plan in the dashboard before you assume the 1-minute default.
Worked example (marginal math only): if a Pro workspace had 2,000,000 billable schedule runs in a month after included allowance, extra runs at $5/1M are ~$10 on that slice - your dashboard is authoritative; I am illustrating the published rate, not forecasting your bill.
Migrate checklist (GET gotcha, do not keep both)
Per migrate from Cron Jobs: swap crons for named schedules in one deploy, add "requestMethod": "GET" if handlers still assume Cron's GET, remove crons so you do not double-fire, ship to production, confirm in the dashboard Schedules tab, vercel schedules invoke <name>, then optionally move path → private function for less public surface. Treat every handler as at least once and dedupe on execution ID + your own keys.
Before / after shape (illustrative):
{
"crons": [{ "path": "/api/jobs/nightly", "schedule": "0 3 * * *" }]
}
{
"schedules": {
"nightly": {
"path": "/api/jobs/nightly",
"schedule": "0 3 * * *",
"requestMethod": "GET"
}
}
}
When agents need Schedules
Replace tight polling with enqueue + wake at time: dynamic Schedules.create against a Queue topic gets you a follow-up without holding a function open. It does not replace durable logs - see durable AI agents - but it buys IANA time zones and retries on the queue side where Cron only gave you a naked HTTP GET in UTC.
For CI-shaped problems (Turborepo checks, GitHub-only triggers), read Vercel CI vs GitHub Actions - that is not what Schedules fixes.
When to wait (or stay on Cron Jobs)
- UTC-only crons + CRON_SECRET on a path are enough, and you do not need runtime schedules - Cron Jobs still do that; Schedules is just where new knobs ship.
- Public beta ⇒ read changelog when upgrading - limits and semantics move.
- GitHub/macOS/release automation stays in Actions; Schedules trigger Vercel workloads.
Site context: joancomadran.dev is Next.js on Vercel with no vercel.json crons/schedules here - so this is a field guide, not a migration diary for this repo.
FAQ
When did Vercel Schedules go to public beta?
5 November 2026 (changelog); product post 4 November 2026 (blog). Cron Jobs keep working; migration exists - checked 6 November 2026.
Do path-based Schedules use GET like Cron Jobs did?
Default is POST for path schedules; Cron used GET. Set "requestMethod": "GET" when porting unless you switch the handler to POST (migration guide).
What does Schedules pricing look like on Pro as of 6 November 2026?
Changelog + limits (6 November 2026): marginal rates from $5/1M runs and $10/1M creations on Pro/Enterprise; 10k/100k schedules/team on Pro/Enterprise; Hobby 500 creates + 5k runs/month included.
Which schedule targets get automatic retries?
Function + path: no auto-retry. Queue topic: retries. Still idempotent - duplicates possible; use execution ID (limits, 6 November 2026).
About the author
I'm Joan Comadran, an industrial engineer turned AI engineer and full-stack developer in Barcelona. I build Next.js on Vercel (this site) and write about agents and tools - see durable AI agents and Vercel CI vs GitHub Actions. I have not migrated Schedules in this repo; claims above cite Vercel docs dated 6 November 2026 and what this codebase contains. For schedules, queues, and agent-shaped products, that's what I do.