The answer. The number that matters is CPU time: the milliseconds the isolate spends executing your code. Time spent awaiting fetch(), KV, D1 or a queue send does not count. Most requests use single-digit milliseconds, so the Free plan's 10 ms is less scary than it reads, and the Paid plan's default of 30 s can be raised to 5 minutes for hashing or parsing jobs. Wall time is a separate, generous limit: an HTTP invocation lives as long as the client stays connected; cron, queue and alarm handlers get 15 minutes. Memory is 128 MB per isolate on every plan, and a Worker that clones a big request body twice will find that ceiling before any CPU limit.
The pattern.
| Limit | Workers Free | Workers Paid |
|---|---|---|
| CPU time per HTTP request | 10 ms | 5 min (default 30 s; limits.cpu_ms) |
| CPU time per Cron Trigger | 10 ms | 30 s (< 1 h interval), 15 min otherwise |
| Wall time, HTTP | unlimited while connected | unlimited while connected |
| Wall time, cron / queue / alarm | 15 min | 15 min |
| Memory per isolate | 128 MB | 128 MB |
| Subrequests per invocation | 50 | 10,000 (raisable to 10M) |
| Simultaneous outgoing connections | 6 | 6 |
| Environment variables | 64 per Worker, 5 KB each | 128 per Worker, 5 KB each |
| Requests | 100,000 per day | unmetered |
| Cron Triggers per account | 5 | 250 |
{ "limits": { "cpu_ms": 300000, "subrequests": 2000 } } // Paid plan only; enforced on the network, not locally
Watch out.
waitUntilhas its own clock: 30 s after the response, regardless of plan.- A redirect chain counts every hop as a subrequest; a "50 subrequests" failure often means one chatty loop.
- Request body size is a zone-plan limit (100 MB on Free and Pro), not a Workers-plan limit.
Related: waituntil-buys-thirty-seconds-not-forever · what-a-worker-is-fetch-handler-plus-bindings · kill-switch-var-fail-closed-ceilings-fail-open