Cloudflare Cron Triggers: 5 free, 250 paid, one Sume bulk run

Free Workers accounts get 5 Cron Triggers, Paid get 250. Use one trigger to fan out many Sume Format runs through a bulk queue instead of one cron each.

5 min readSume
All posts

Cloudflare allows 5 Cron Triggers per account on the Free plan and 250 on Paid, so a schedule per customer or per campaign runs out quickly. Use one trigger that reads your list of jobs and sends them to Sume as a single bulk run of up to 100 Format runs, keyed to the scheduled time so a double fire cannot spend twice.

What are Cloudflare's cron limits?

Cloudflare's limits page gives the per-account cap and the time budgets. A Cron Trigger invocation, like a Queue consumer or a Durable Object alarm, can run for up to 15 minutes. CPU time on Paid for a cron is 30 seconds when the interval is under one hour and 15 minutes when it is one hour or more.

None of these is a reason to wait for a video inside the handler. They are reasons to submit, record and return.

Cloudflare Cron Trigger limits (read 2026-10-02)
LimitFreePaid
Cron Triggers per account5250
Subrequests per invocation5010,000
Max duration of a cron invocation15 minutes15 minutes
CPU time, interval under 1 hourNot listed for cron30 seconds
CPU time, interval 1 hour or moreNot listed for cron15 minutes

Why one trigger instead of one per campaign?

Each additional schedule uses part of the 5 or 250. More importantly, many triggers firing at the same minute each make their own submit, and a Sume workspace has concurrency limits on how many jobs run at once. A single handler that builds one queue lets you set the window yourself.

Sume's bulk run takes concurrency from 1 to 16 and 1 to 100 items. The queue receipt reports counts for the whole batch, and polling it is one request however many items it holds.

How do I make a double fire harmless?

Cloudflare's page does not promise exactly-once, and a retry of your own can overlap a schedule, so assume a repeat. Build the Idempotency-Key from controller.scheduledTime. A replay of the same key and body returns the original queue instead of a new one; the bulk docs note that replaying a spent key returns 202 with the old queue.

If your item list changes between two fires, the body differs and the same key is 409 idempotency_conflict. That is the signal you want: fix the key derivation, or include a version in it, rather than retrying blindly.

export default {
  async scheduled(controller: ScheduledController, env: { SUME_API_KEY: string; JOBS: string }) {
    const items = (JSON.parse(env.JOBS) as string[]).slice(0, 100).map((brief) => ({
      instruction: brief,
      generation_spend_cap_usd: 5,
    }));
    const res = await fetch("https://api.sume.com/v1/formats/acme/daily-promo/bulk-runs", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${env.SUME_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `cron-${controller.scheduledTime}`,
      },
      body: JSON.stringify({ concurrency: 4, items }),
    });
    if (!res.ok) throw new Error(`sume ${res.status} ${await res.text()}`);
  },
};

Where do the results go?

The bulk queue has no webhook of its own; put communication.webhook_url on each item if you want a signed format.run.terminal per child, or poll GET /v1/format-run-queues/{id} from the next scheduled run. A second cron that reads yesterday's queue and writes results is a clean fit for a trigger limit this tight.

Branch on counts.failed, not on queue completed, because a completed queue only means every item is terminal.

What does Sume not do?

Sume does not schedule anything from these docs for a Worker you own; the clock is yours. Its Actions have their own schedule trigger, covered elsewhere in the docs, but that is a different surface from calling Formats from Cloudflare. Also note there is no public list-queues or cancel-queue endpoint, so cancel a child run, not the queue.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume