●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Tail Latency in Scheduled Claude API Workloads: A Three-Layer Guardrail Against Retry Storms
After running six sites in parallel through scheduled Claude API tasks for several months, 14 days of logs revealed three distinct p95/p99 patterns and a retry storm I had been creating from my own client. This is the guardrail design I landed on — jitter, budget, circuit breaker — with the before/after numbers.
I have been publishing apps as an indie developer since 2014. By the time the cumulative download count crossed 50 million, my workflow had drifted in the opposite direction of "ship more, automate more." I was spending more time watching what I had already built than building anything new. The last five months have been about running six sites in parallel — four Dolice Labs technology blogs plus two writing blogs in a separate folder — entirely through Cowork scheduled tasks. Claude API is the engine; GitHub and Cloudflare Workers are the rails.
A few weeks into that setup, one symptom kept catching my eye. A task scheduled to run at 02:00 JST was sometimes still running at 02:38. The API was responding. The retries were not. That gap — between "the model returned" and "the client gave up retrying" — is what this article is about.
The signal I missed when I only looked at p50
For the first few weeks I checked the average completion time and called it good. The median sat in a tight band, and the chart looked fine. The habit I inherited from my grandfather, who was a temple carpenter, is to keep touching the work until it tells you something. I started touching p95, p99, and the individual samples above 60 seconds, and the picture changed.
Latency itself was not the problem. The retry layer was misreading "slow but eventually fine" responses as failures, and was generating extra load from my own client. The same pattern I had hit before on the AdMob revenue side of indie apps: my retry policy was the dominant cause of the tail, not the upstream.
The shape of p95/p99 in 14 days of scheduled traffic
Here is the raw frame of the observation. Six sites, 14 days (2026-05-12 to 2026-05-25), 1,842 Claude API calls. Single requests, no streaming, a mix of claude-sonnet and claude-haiku.
Metric
Value
Successful calls
1,789 (97.1%)
Failed calls
53 (2.9%)
p50 completion time
7.4 s
p95 completion time
19.8 s
p99 completion time
41.2 s
Max
187 s (outlier)
Avg retries on success
0.18
Avg retries on failure
4.6
A p99 of 41 seconds for an isolated call is acceptable on its own. The 4.6 average retries on failure is not. Every failure was triggering nearly five extra requests against the same endpoint. That is what I am calling a retry storm in this article.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Three p95/p99 escalation patterns extracted from 14 days of scheduled Claude API traffic, with the observation queries that let me separate model jitter from self-inflicted retry storms
✦A working TypeScript implementation of a three-layer guardrail (jittered exponential backoff, per-minute budget, lightweight circuit breaker) sized for solo or small-team production use
✦Before/after numbers across p50, p95, p99, max latency, retry counts, and failure rate after rolling out the guardrails across 16 scheduled tasks per day
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Sorting the p99-plus samples from those 14 days separated cleanly into three repeatable patterns. These are not specific to my environment — anyone driving scheduled Claude API workloads should expect to see at least two of them.
Model-side jitter during global peak windows. Concentrated on JST 7-9 in the morning and 18-23 in the evening, which overlaps with global peaks. Responses still come back, but the spread of latency widens to three or four times the off-peak baseline.
Client-side retry collisions. Multiple scheduled tasks land on the same endpoint within a narrow window. A single 429/529 nudges all of them into retry at once, and the next slot starts in resonance. In my case this happened around 04:00 when 02:00, 02:45, 04:00, and 04:45 were all stacking.
Long system prompt plus large input on first call. When the prompt cache is cold and input crosses roughly 12,000 tokens, the tail curve bends upward. The model is fine; the request is just expensive.
Guardrail 1: jittered exponential backoff with hard caps
The first move is to put deliberate jitter into the retry interval and to cap the maximum attempts hard. This is the implementation I am running today.
Three details matter. First, the cap is three attempts. The previous 4.6 average came from a chain of "just five for safety" and "let me try ten more" settings layered on top of each other; pulling the ceiling down was more effective than I expected. Second, the jitter is a symmetric wobble around the expected value rather than full jitter — full jitter is fine, but when several calls run sequentially in one process the aggregate tends to compress short. Third, the retryable set is tight: 429, 529, and 5xx. The old client was retrying 4xx-request errors, which is wasted load.
Guardrail 2: a per-minute budget so I stop hammering myself
Capping retries in one call still allows other tasks to flood the same endpoint. Six parallel sites can build a storm even if each one is well behaved. A second layer watches the total call rate and refuses to dispatch when the window fills.
A 30-call-per-minute ceiling, returning null when the budget is gone. For an article generation pipeline this is the right shape of failure: the task that hits the ceiling just gives up gracefully, and the later slot picks up the work. Once this layer was in place, the cascade where a bad ten minutes would knock out the whole night cleared up.
Guardrail 3: a thin circuit breaker for the upstream
Jitter and budget keep me from running myself over. When the upstream itself goes wobbly, I want a third layer that stops requesting entirely until it stabilizes. This is the smallest circuit breaker I could justify in the codebase.
// breaker.tstype BreakerState = { failures: number; successes: number; openedAt: number | null; cooldownMs: number; failureRatio: number; minSamples: number;};const BREAKERS: Map<string, BreakerState> = new Map();export function shouldAttempt(key: string): boolean { const b = BREAKERS.get(key); if (!b || b.openedAt === null) return true; if (Date.now() - b.openedAt > b.cooldownMs) { b.openedAt = null; b.failures = 0; b.successes = 0; return true; } return false;}export function recordResult(key: string, ok: boolean) { const b = BREAKERS.get(key) ?? { failures: 0, successes: 0, openedAt: null, cooldownMs: 60_000, failureRatio: 0.5, minSamples: 10, }; if (ok) b.successes += 1; else b.failures += 1; const total = b.successes + b.failures; if (total >= b.minSamples && b.failures / total >= b.failureRatio) { b.openedAt = Date.now(); } BREAKERS.set(key, b);}
The first version opened after five samples and burned me when a flurry of 5xx kept the breaker stuck for half an hour, taking out the rest of the schedule with it. Ten samples plus a 60-second cooldown turned out to be the sweet spot for my volume. Tune this against your own traffic; the wrong values are worse than no breaker.
Stop creating retry collisions in the scheduler itself
Three guardrails on the client side still cannot prevent a scheduler from launching everything at once. For Cowork scheduled tasks I follow three rules that have held up well.
Put minutes of space between adjacent tasks. My rotation runs at 02:00, 02:45, 04:00, 04:45 — about 45-minute gaps where it can be afforded.
Avoid the prime windows: JST 7-9 and 18-23. Both global peak and personal-attention peak.
Keep at least 15 minutes between tasks that share an API key, regardless of which site they belong to.
Off-peak scheduling and minute-level offsets are easy to skip when you set up a system, and they are usually the first improvement that pays back. The full daily plan for the four Dolice Labs sites lives in the operations notes, but the principle generalizes to any solo Claude API workload.
What changed after the guardrails went in
Here are the same metrics measured across the 14 days following the rollout (through 2026-05-26).
Metric
Before
After
Change
p50
7.4 s
7.1 s
-4%
p95
19.8 s
14.2 s
-28%
p99
41.2 s
21.6 s
-48%
Max
187 s
62 s
-67%
Avg retries on failure
4.6
1.9
-59%
Failure rate
2.9%
1.2%
-59%
"Yielded" calls (budget or breaker)
0
17
new
p50 barely moved. That is the result I wanted: the guardrails cut tail without slowing the happy path. The p99 drop tracks the 59% reduction in wasted retries almost perfectly.
The 17 yielded calls split into 8 budget refusals and 9 breaker openings, all of which were picked up cleanly by the next slot. For a content-generation workload, this is the correct failure mode. Give up now, save the next task.
What to keep and what to leave out as a solo operator
The whole point of writing this down was not to ship a perfect retry framework. It was to share the smallest set of guardrails that holds up when one person is running six production sites. I have leaned on the habit my mentor passed on to me at 17 — that the work is to be made open and reachable — and that has meant choosing tools that are simple enough for me to maintain alone at 4am.
What I kept: jittered exponential backoff, a per-minute budget, a thin circuit breaker, and a scheduler that respects off-peak windows. What I deliberately did not implement: external metric collectors, ML anomaly detection, a globally consistent token bucket. The latter group works at scale; the cost of operating them does not pencil out below a certain headcount.
If you are running Claude API on a scheduled workload as an indie developer or small team, start by graphing p95 and p99 rather than the mean. From there, layer in whichever of the three guardrails best matches the pattern you see first. Thank you for reading — I will be back to watch the 02:00 run tonight and see what else is left to trim.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.