Scheduled jobs that call an LLM API on a timer have a blind spot most teams don't think to check: metered, pay-per-call billing has no ceiling on how many times an unattended job fires while nobody's watching. I found the sharpest version of this auditing a twice-daily scheduled job on this site's own content pipeline. The account balance had hit zero and stayed there for more than a week before anything surfaced it — every scheduled call during that window failed with a plain 400 error, logged and forgotten in a background job nobody was reading in real time.

The downstream damage was worse than the outage itself: several drafts got created during that dead window as raw, unenhanced fallback saves — the actual Claude pass never ran, and nothing about the draft looked different enough to notice unless you already knew to check for it.

Why "pay per call" is the wrong shape for recurring work

Per-token API billing is a genuinely good model for work a human triggers deliberately — you ask, it runs, you pay for that one exchange, and the cost scales with how much you actually use it. The problem shows up when the same billing model gets attached to something that fires on a timer regardless of whether a human is in the loop that day. A scheduled job doesn't pause to ask "is this worth the cost today" — it runs, it bills, and it keeps running until either the material runs out or the account does.

That's a cost shape problem, not a cost rate problem. A cheap per-call price can still be the wrong shape for a workload that executes unattended, because the thing that actually breaks isn't the average cost — it's the unattended tail. Nothing about metered API billing puts a ceiling on how many times a stuck retry loop or a mis-scheduled cron fires before a human happens to look. The bill and the failure mode are the same mechanism: both scale with call count, and call count is exactly the variable a scheduled job doesn't supervise.

A subscription changes that shape entirely. Running the same recurring work as a scheduled agent session against a flat-rate plan turns "cost scales with every firing" into "cost is fixed regardless of how many times this runs within the plan's usage ceiling" — the marginal cost of one more scheduled firing drops to $0 instead of compounding against a balance. The failure mode changes too: a runaway scheduled session on a subscription can waste your own attention and the plan's usage allowance, but it can't drain a balance to zero and silently degrade every job behind it, because there's no balance to drain.

Why the drain was invisible for over a week

The reason this ran unnoticed for so long wasn't a lack of logging — the 400 errors were right there in the job's own output. It was that nothing connected "a background job is failing" to "someone should look now." A failed scheduled call doesn't page anyone by default; it just adds a line to a log file that only gets read if a human happens to open it. Meanwhile, the actual damage — content silently produced without the intended review pass — didn't look broken. It looked like a normal draft, just a worse one, and "worse" isn't a signal any automated check was watching for.

That's the pattern worth naming: a dead credential or an empty balance doesn't announce itself as a distinct failure state. It presents as "the call didn't work," gets caught by a try/catch that was written to handle transient errors, and the pipeline moves on to the next task as if nothing structural had changed. When I finally traced it — during an unrelated cleanup pass, not because anything had alerted me — the actual scope became clear: every enhancement call across that entire window had quietly fallen back to raw output.

What actually changed

Fixing this took three separate moves, not one.

Enumerate every recurring job by cost pool, not by what it does. The useful question for a scheduled job isn't "what does this do" — it's "which meter does this hit when it fires, and what happens if it fires 200 times unattended." Anthropic's own token pricing is straightforward for a call you trigger deliberately; it says nothing about how many calls a stuck schedule will make while nobody's watching, which is the actual variable that matters here. Sorting every existing schedule into "flat-rate subscription" versus "metered API" surfaced the ones quietly sitting in the wrong pool. A recurring job with no human review step in front of it and a metered API behind it is exactly the configuration that produces this failure — it's not a bug in any single run, it's a structural mismatch between how the job executes and how it's billed.

Move recurring, unattended generation onto a subscription runner. The twice-daily job now runs as a scheduled session against a flat-rate plan instead of the API. Nothing about the job's logic changed — same task, same output shape — only the substrate it executes on. The API key stays reserved for work a human deliberately dispatches, which is the case the metered model actually fits: bounded, attributable, and reviewed by someone who's already paying attention.

Write the rule down, not just the fix. The fix by itself doesn't prevent the next recurring job from getting wired up the same wrong way six months from now. The rule that actually sticks is explicit and checked before anything new gets scheduled: no unattended, timer-triggered job calls a metered LLM API. If it needs to run on a schedule with nobody watching, it runs on the subscription; if it needs the API, it runs by deliberate dispatch, not by cron.

The broader pattern

The instinct with any recurring cost is to look for the cheaper rate. That's the wrong axis for anything that fires without a human checking in. The question that actually matters for scheduled work is what the worst case costs — not the typical call, but what happens when the job runs far more (or far longer) than expected with nobody watching, because that's the scenario a schedule guarantees will eventually happen. A subscription's flat cost isn't cheaper because the per-unit rate is lower; it's cheaper because it removes the variable that was actually driving the risk.

This generalizes past Claude specifically. Any recurring, unattended workload against a metered API — image generation, embeddings, search, whatever — inherits the same shape problem. The fix is the same in each case: match the billing model to whether a human is actually in the loop when the call fires, not to which rate looks lowest on a pricing page.

FAQ

Doesn't a subscription just move the same risk somewhere else?

No — it changes what the risk actually is. A stuck metered job can drain a balance and silently degrade output with no ceiling until someone notices. A stuck subscription job burns your own attention and the plan's usage allowance, but there's no balance to hit zero, and no silent per-call billing failure mode to hide inside. The failure becomes visible (you notice the session behaving oddly) instead of invisible (a 400 error in a log nobody's reading).

How do I know if a job I already have scheduled is in the wrong cost pool?

Ask what happens if it fires far more often, or for far longer, than you expect, with nobody watching for a week. If the answer involves a balance draining and calls silently failing, it's metered work masquerading as unattended automation. If the answer is "it just runs and doesn't cost anything extra," it's already in the right pool.

Does this mean the API is never the right choice for scheduled work?

No — it's the right choice when a human is genuinely reviewing the output before anything ships, and the schedule is really just a reminder to trigger a deliberate run. The distinction is whether a person is in the loop when the call actually fires, not whether the trigger happens to be a timer.

What would have caught this sooner, short of moving off the API entirely?

A preflight check before the first call of each run that fails loudly the instant the account can't authenticate, instead of a try/catch that logs and continues. The absence of that check is really what let a dead balance run quietly for over a week — the fix described above removes the failure mode, but a loud preflight would have shortened the blast radius even before that.

Where to go from here

This is the first lesson in a running series on what actually breaks when you run Claude Code and agentic pipelines past the point of individual, supervised sessions — see the full Build with Claude course for what's live so far. If you've already measured your own interactive Claude Code costs, the subscription-versus-API audit covers that adjacent question from the personal-usage side rather than the scheduled-job side. For the model and stack decision underneath all of this, start with Which AI Model Should You Actually Use to Build Software? — then work with me or subscribe to the newsletter for the rest of this series as it ships.

Share this article
LinkedIn (opens in new tab) X / Twitter (opens in new tab)
Atticus Li

Experimentation and growth leader. CXL-certified CRO practitioner, Mindworx-certified in behavioral economics. Led 100+ in-house experiments at NRG in 2025, with project evidence and limits documented in the case studies.