Pass a scheduled function to run work on a cron trigger instead of in response to a request. Apiker exports it as the Worker's scheduled handler.
1export const handleScheduled = async ({ state }) => {2 const settings = await getDueSettings(state); // however your app tracks work to do3 const ids = Object.keys(settings);4 if (!ids.length) return;5 6 // Resume where the last run left off, in fixed-size batches7 const { currentIndex = 0 } = (await state("Common", "scheduled-job").get("processing-state")) || {};8 const batchSize = 20;9 const endIndex = Math.min(currentIndex + batchSize, ids.length);10 11 for (let i = currentIndex; i < endIndex; i++) {12 const id = ids[i];13 const info = await state("Common", id).get("info");14 15 if (!info?.entitlements?.includes("your-feature")) continue; // gate on entitlement16 17 try {18 await runDueWork(id, settings[id]);19 } catch (err) {20 console.error(`Scheduled job failed for ${id}`, err); // one bad entry can't stall the batch21 }22 }23 24 await state("Common", "scheduled-job").put("processing-state", {25 currentIndex: endIndex >= ids.length ? 0 : endIndex26 });27};The function receives the same state factory your handlers use, plus the Cloudflare event, env and execution context. Batching a fixed number of entries per run, tracking a resume cursor in Common, gating on entitlements, and catching errors per entry is the shape a real scheduled job needs once it processes more than a handful of records.
It's wired in like any other apiker.init option, alongside your usual routes and objects:
apiker.init({
routes,
objects,
exports,
firewall: {
limitRequestsPerMinute: 140
},
adminPanel: true,
scheduled: handleScheduled
});Declaring the schedule
Cloudflare decides when to call it, so the cron itself lives in your configuration:
[triggers]
crons = ["0 * * * *"]Apiker wraps the callback in a try/catch and only logs an uncaught error, so a bad run cannot break the next one — but that only protects across runs. Within a single run an unhandled error still stops the loop early, which is why the batch above catches errors per entry.


