Skip to docs content
Browse documentation
On this page

Runtime and limits

How Minion runs your code in a fresh isolate, what each surface counts against on your plan, the monthly worker compute include and prepaid credits, and the per-run limits for time, memory and results.

Everything your agent runs in Minion executes on Minion's servers, not on your laptop and not inside the chat model. That covers an ad hoc execute, a scheduled job, a webhook delivery, an event handler and a package app request. This page explains what a run is, what each one counts against on your plan, and the limits every run works inside.

What a run is

A run is one call to a module's export default async function main(params).

  1. Minion checks your plan's daily limit for that surface. If you're over, the run is rejected before anything is bundled.
  2. Your code and any saved packages it imports are bundled into a single script.
  3. A sandbox creates a fresh V8 isolate (in production, a Cloudflare Dynamic Worker), runs the script, and throws the isolate away when main returns.
  4. The result, logs and status are stored as a run record you can read with minion.runs.list().

Each run gets no environment variables, no file system writes and no child processes. Your code can't reach the database or Minion's credentials. It talks to the outside world only through fetch and minion.* calls, which the host handles for it.

There is no warm state between runs. Globals, module-level variables, caches and timers are gone when main returns. Anything you need next time goes in package storage, sql(), a note or a memory.

What counts against your plan

Usage is counted per team, per UTC day. Limits reset at 00:00 UTC. Execute calls and outbound fetches also have a weekly limit, counted Monday to Sunday. The numbers below are the Free and Pro plans; the pricing page lists every limit.

Each surface has its own counter:

  • execute_calls: interactive execute calls from your agent. Free 150 a day (400 a week), Pro 500 (1,200).
  • job_runs: each scheduled or jobs.runNow run. Free 500 a day, Pro 1,500.
  • webhook_runs: each webhook delivery that runs a package export. Free 500, Pro 8,000.
  • event_runs: each event delivered to a package subscription. Free 500, Pro 8,000. This is separate from job runs so a burst of events can't starve your schedules.
  • app_requests: each request to a members-only package app. Free 500, Pro 8,000.
  • public_app_requests: each request to a public package app. Same numbers, but a separate bucket, so anonymous traffic can't use up your team's app quota.
  • capability_calls: each minion.* call made from inside a run, including remote MCP tool calls. Free 5,000 a day, Pro 100,000.
  • outbound_fetches: each fetch or integration fetch from inside a run. Free 1,000 a day (2,500 a week), Pro 15,000 (40,000).

Jobs, webhook deliveries, event handlers and package app requests run through the same engine as execute, but each is counted only by its own counter, not by execute_calls. A busy schedule can't use up the execute calls your agent needs. A job that fires 100 times a day uses 100 job runs.

Email has its own daily counters (email_sends, email_receives). Saved packages, scheduled jobs, secrets, webhook endpoints and storage bytes are count limits, not daily ones.

A few things don't cost anything:

  • Replaying a run. Pass an idempotencyKey to execute, and a repeat call with the same key returns the stored run without running again.
  • A retry of a job slot, webhook delivery or event delivery that already has a run. It isn't counted twice.

Check where you stand with minion.account.usage() or the Usage page in the app (/app/usage). When a call is over the limit it fails with a limit error that names the plan and the limit, and says when it resets.

Worker compute

Separately from the daily counters, each plan includes a monthly amount of worker compute. Minion counts each distinct worker your team runs once per UTC day, summed over the UTC month. Running the same code again on the same day doesn't add another, and a code change starts a new worker. Free includes 50 worker-compute days a month and Pro includes 2,000.

Ad hoc execute (code that isn't a saved package) may use only part of the include: half on Free, three quarters on Pro. Saved packages, jobs, webhooks, event handlers and apps can use the whole include. So repeated one-off scripts belong in a package.

Past the include, runs that would start a new worker are refused until the month resets, while code that already ran that day keeps running. On a paid plan you can keep going with prepaid credits: top up on the Team page (/app/team), or turn on auto-refill so the balance tops itself up from your saved card. Credits pay for worker-compute days past the include (about $0.004 each) up to a plan ceiling, and nobody is ever invoiced for overage. New paid subscriptions get $5 of welcome credits.

Keep the same worker warm: stable package code, and ad hoc code that varies its inputs through params.

Per-run limits

These are the same on every plan:

  • Wall clock: 90 seconds for the whole run, including time spent waiting on fetch and minion.* calls. Package app requests and sync webhook responses get 25 seconds. A one-off workflow's export gets about 270 seconds.
  • CPU: 20 seconds of synchronous JavaScript. A busy loop hits this long before the wall clock.
  • Memory: 128 MB heap for the isolate.
  • Result size: ordinary execute results have a 100 KiB soft cap. Oversized strings are truncated on UTF-8 boundaries and end with … [truncated]; oversized structured values return a small marker such as {"truncated":true,"type":"object"}. The sandbox transport has a separate hard cap. Package app responses get 1 MB.
  • Host calls: at most 1,000 fetch and minion.* calls per run, and 8 in flight at once.
  • Outbound fetch: 60 second timeout per request, 1 MB request body, 5 MB response.
  • Timers: up to 1,000 pending setTimeout or setInterval timers. All are cleared when main returns.
  • Logs: 64 KB of console output per run. Anything past that is dropped with a [logs truncated] line.
  • Concurrency: each team can have only a couple of runs in the sandbox at once. Extra runs fail fast with a busy error rather than queueing forever. Retry them shortly.

Source has its own limits. An execute module can be up to 256 KB, and the bundled script up to 2 MB. A saved package can have up to 200 files, 256 KB per file and 2 MB in total.

A run that is cut off at the CPU, memory or wall-clock limit returns no result. A caller that disconnects does not cancel its run: poll minion.runs.get instead of resubmitting side effects.

Practical tips

  • Pass inputs in params. Keep code the same and change params. Identical package-free source is cached by content hash, so it skips the bundling step and reuses the same worker.
// Good: same code every time, different params
export default async function main(params) {
	return await minion.email.send(params)
}
  • Expect a cold start every time. Code that imports a saved package is bundled fresh on every run, so it always runs the package's current published version. Small packages bundle faster.
  • Keep packages small. Import only what you need. Split unrelated work into separate packages or exports instead of one large module.
  • Return less when you need the full result. Oversized strings are truncated with a marker; oversized objects and arrays are replaced by a small marker naming their type. Project the fields you need before returning a large list.
  • Store state between runs. Use packageStorage() for cursors and sql() for rows you want to query. Module variables don't survive.
  • Batch host calls carefully. A loop of 2,000 minion.* calls hits the per-run cap and burns your daily capability calls. Use list calls and filters where they exist.
  • Set a timeout on every fetch. AbortSignal.timeout(ms) stops a slow upstream from eating your 90 seconds.
  • Move heavy work out. If a task needs more memory, more CPU or a library Minion can't bundle, run it in a service you operate. See Offload heavy work .

Choosing a surface

Pick the smallest home that fits the work:

  • A built-in capability or an existing package export, called from execute.
  • A saved package when the behavior will be reused, scheduled or shared. See Package lifecycle and Package authoring .
  • A job, webhook or event subscription when something other than your agent should start the run. See Triggers .

Related