Cloudflare Subrequests

Cloudflare just blew up the subrequest limit on Workers. The old ceiling was 1,000 per invocation on paid plans. The new default is 10,000. But the real change is the configurable max: 10 million. That’s a 10,000x increase from where we were yesterday.

If you’ve never hit the subrequest wall, this means nothing to you. If you have, you know exactly how annoying it was.

What changed

Every fetch() call a Worker makes counts as a subrequest. That includes calls to external APIs, but also calls to Cloudflare’s own services like D1, R2, KV, Vectorize, and Durable Objects. The old cap was 1,000 per request on paid plans. 50 on free.

As of today, paid plans get 10,000 by default. And you can push it all the way to 10 million with one line in your Wrangler config:

{
  "limits": {
    "subrequests": 50000
  }
}

Or in TOML:

[limits]
subrequests = 50_000

Free plan stays at 50 external / 1,000 to CF services. No change there.

Why this matters

The 1,000 cap forced bad architecture. Here’s where it actually hurt:

Workflows. The whole point of Workflows is long-running, multi-step orchestration. But every step that makes a fetch() eats a subrequest from the same pool. A Workflow that calls an AI provider, writes to D1, stores a file in R2, and updates a vector index is 4 subrequests per iteration. At 1,000 total, you cap out at ~250 iterations before the whole thing errors out. The workaround was splitting into sub-Workflows or fan-out via Queues. Ugly. Unnecessary. Gone.

Durable Object WebSockets. Long-lived WebSocket connections on DOs accumulate subrequests over the life of the connection. A chat session where each message triggers 3-5 backend calls (AI inference, DB read/write, vector search) would hit 1,000 after a few hundred messages. Not theoretical. This was a real failure mode for anyone building agents on DOs.

Cron batch jobs. A cron-triggered Worker that needs to process 500 items, each requiring 3 API calls? That’s 1,500 subrequests. Previously impossible in a single invocation. The fix was shoving items into a Queue so separate consumers each stayed under the limit. Now you just loop.

The safety valve

You can also set the limit lower to protect against runaway code:

{
  "limits": {
    "subrequests": 10,
    "cpu_ms": 1000
  }
}

Subrequests and cpu_ms work together here. Cap subrequests to prevent a loop from hammering your upstream APIs. Cap CPU to kill anything that’s burning cycles without making external calls. A simple API gateway Worker that should only make 2-3 calls? Set subrequests to 10 and cpu_ms to something tight. A runaway loop won’t rack up a surprise bill.

What I’m changing

I had several Workers that existed purely to fan out work and reset the subrequest counter via Service Bindings. That entire pattern is dead now. One Worker, one Workflow, one invocation. No more workarounds to dodge an arbitrary ceiling.

If you’re on the paid plan, update your Wrangler config and redeploy. The new default applies automatically, but setting an explicit limit per Worker is still worth doing.

Changelog entry