Workers 50 subrequests on free: poll one Sume queue, not 100 jobs

A free Cloudflare Worker gets 50 subrequests per invocation (10,000 paid). Polling 100 Sume jobs one by one fails; one bulk-queue read does not.

5 min readSume
All posts

A Cloudflare Worker on the free plan gets 50 subrequests per invocation, and on the paid plan 10,000. A cron Worker that checks 100 Sume jobs with one GET /v1/jobs/{id}/status each runs out of subrequests on the free plan, so read one Sume bulk-run queue instead, which reports every item in a single call.

The Cloudflare numbers below come from its Workers limits page, read on 2026-10-02.

What are the Workers limits that shape a poller?

Cloudflare adds that each subrequest in a redirect chain counts, and that fetch calls, KV, Cache, R2 and Queues operations all share the six-connection limit. A poller that fans out 100 status reads at once therefore queues behind six at a time even on the paid plan.

Cloudflare Workers limits, read 2026-10-02
LimitFreePaid
Subrequests per invocation5010,000 (can be raised)
Connections waiting for response headers66
CPU time per HTTP request10 ms30 s default, 5 min max
Cron Trigger wall clock15 min15 min

Why does one-job-per-call polling not scale?

The jobs and results docs give you one status endpoint per job. For 100 jobs that is 100 subrequests per sweep, twice the free allowance, and it is wasteful on any plan because most jobs are still processing on each sweep. The next_poll_after_seconds hint helps you skip jobs that are not due, but you still need a read per due job.

What replaces it?

If the jobs are runs of one saved Format, submit them with POST /v1/formats/{handle}/{slug}/bulk-runs: 1 to 100 items and a concurrency window of 1 to 16. The bulk runs docs say GET /v1/format-run-queues/{queue_id} returns counts and a per-item status, so one subrequest tells you the whole batch's state. Read the finished items' receipts at GET /v1/format-runs/{run_id} only for the items that completed.

This works only for Format runs. Separate generation jobs, such as 100 image requests, have no queue object; for those, either use the paid plan's limit or take each result by webhook.

What does the scheduled handler look like?

This cron handler reads one queue. completed on the queue means every item is terminal, not that all succeeded, so it checks counts.failed as well. The queue id comes from a variable you stored when you submitted; the API key is a secret binding.

export default {
  async scheduled(_event: ScheduledEvent, env: { SUME_API_KEY: string; QUEUE_ID: string }) {
    const res = await fetch(`https://api.sume.com/v1/format-run-queues/${env.QUEUE_ID}`, {
      headers: { Authorization: `Bearer ${env.SUME_API_KEY}` },
    });
    if (!res.ok) {
      console.log('queue read failed', res.status);
      return;
    }
    const { data } = (await res.json()) as {
      data: { status: string; counts: { total: number; failed: number; completed: number } };
    };
    console.log('sume queue', data.status, data.counts);
    if (data.status === 'completed' && data.counts.failed > 0) {
      console.log('some items failed; inspect them one by one');
    }
  },
};

What about the six-connection limit?

Cloudflare says up to six connections can wait for response headers at once. If you must read many jobs on the paid plan, send them in small batches rather than one Promise.all over a hundred, and expect a sweep to take several round trips. A slow status call holds one of the six, so one sluggish response slows the rest.

Also keep the Worker's own work small. The free plan's 10 ms CPU limit per HTTP request is about CPU time, not waiting, so awaiting a fetch does not use it up, but parsing a large JSON receipt does. The queue receipt is small, which is another reason to prefer it over many separate reads.

Is the paid plan always the answer?

It removes the 50 limit, but not the cost of reading a job per call or the six-connection ceiling. A design that reads one queue, or receives webhooks, stays cheap on both plans. Choose by what you are polling: Format runs have a queue receipt, plain generation jobs do not, and for those a webhook per job is the lighter route.

Remember the numbers can change. Cloudflare lists them on a page you can reread, so confirm the current values before you commit a design to them.

Cloudflare Queues are a different tool: they carry your own messages, as in Cloudflare Workers webhook to a Queue. They do not poll Sume for you. The queue object also has no webhook of its own, so something on your side has to keep reading it, or you can register a webhook on each item instead.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume