Home/Vercel cron timeouts
Run the job for hours. Return in under a second.
Vercel Hobby functions stop at 10 seconds. Pro stops at 60. The cron route only has to start the pipeline. GraphIngest runs the rest on managed infrastructure, for up to two hours on this template, and the route is already done.
- 01
Cron hits your route
Vercel calls /api/cron/sync on the schedule in vercel.json. That function is still subject to the platform timeout.
- 02
The route returns
triggerFlowRun hands the work off and responds with the run id. The HTTP call finishes in under a second.
- 03
The pipeline keeps going
sync-source runs for each source, in parallel, with retries. A two-hour timeout is a normal graph setting here.
Paste this route
Save it as app/api/cron/sync/route.ts. The flow id comes from the dashboard after you deploy the pipeline below. The API expects that id.
import { GraphIngestClient } from "graphingest";
export async function GET(request: Request) {
const secret = process.env.CRON_SECRET;
if (secret && request.headers.get("authorization") !== `Bearer ${secret}`) {
return new Response("Unauthorized", { status: 401 });
}
const flowId = process.env.GRAPHINGEST_FLOW_ID;
if (!flowId) {
return Response.json({ error: "GRAPHINGEST_FLOW_ID is not set" }, { status: 500 });
}
const client = new GraphIngestClient();
const res = await client.triggerFlowRun(flowId, {
sources: [
{ name: "users", apiUrl: "https://jsonplaceholder.typicode.com/users" },
{ name: "posts", apiUrl: "https://jsonplaceholder.typicode.com/posts" },
],
});
const runId = (res as { data?: { id?: string } }).data?.id ?? null;
return Response.json({ runId, status: "dispatched" });
}Set GRAPHINGEST_API_KEY and GRAPHINGEST_FLOW_ID in the Vercel project. If you set CRON_SECRET, Vercel sends it as a bearer token and the route checks it.
The pipeline it starts
This is the part that used to live inside the cron handler. timeout_seconds=7200 is two hours. .map() syncs every source at once, and each source retries on its own.
from graphingest import node, graph, deploy, RetryPolicy
import requests
@node(name="sync-source", cache_ttl=1800, max_retries=3)
def sync_source(source: dict) -> dict:
resp = requests.get(source["api_url"], timeout=30)
data = resp.json()
return {
"source": source["name"],
"records_synced": len(data) if isinstance(data, list) else 1,
}
@graph(
name="nightly-sync",
timeout_seconds=7200, # 2 hours
retry_policy=RetryPolicy(max_retries=2, delay_seconds=30, backoff_factor=2),
)
def nightly_sync(sources: list[dict]):
results = sync_source.map(sources)
total = sum(r["records_synced"] for r in results)
return {"sources_synced": len(results), "total_records": total}
if __name__ == "__main__":
deploy()Run it
pip install graphingest requests, then runsdk/python/examples/vercel_cron_timeout.py. The TypeScript twin issdk/typescript/examples/vercel_cron_timeout.ts.- Copy the flow id from the dashboard into
GRAPHINGEST_FLOW_ID. - Put
sdk/typescript/examples/vercel_cron_route.tson the cron path and deploy the Next.js app. - Trigger the cron. The route responds immediately, and the run shows up on the dashboard while the sync continues.