●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
Intelligent Model Routing with Claude API — Auto-Selecting Sonnet 4.6 and Haiku 4.5 for Optimal Cost and Quality
Build an intelligent routing layer that automatically selects between Claude Sonnet 4.6 and Haiku 4.5 based on request complexity. Covers classifier design, circuit breakers, fallback chains, and cost monitoring for production deployments.
I've been shipping iOS and Android apps as an indie developer since 2014. Across roughly 50 million cumulative downloads, there was exactly one morning when I felt my chest go cold: the day AdMob's eCPM halved overnight and my monthly revenue dropped from one million yen to mid-five-hundred-thousand. It wasn't the lost income that scared me — it was the realization that a cost curve I'd assumed was compounding upward had quietly slipped out of my control.
I felt that same chill again after wiring Claude API into the automated publishing pipeline for the four Dolice Labs sites (Claude Lab, Gemini Lab, Antigravity Lab, Rork Lab). Sonnet 4.6 is genuinely smart. But pinning every request to Sonnet while writing 16 articles a day pushes the monthly bill up by an order of magnitude. Go the other direction and run everything on Haiku 4.5, and the membership-grade articles start failing article_gate.py more often than they pass.
You can't solve this dilemma by picking one model. The only way out is to classify request complexity upfront and route each call to the cheapest model that still produces acceptable quality — what I'll call intelligent model routing throughout this article. What follows is the pattern I've actually been running for over six months across those four sites plus iOS/Android support bots, written up with the operational gotchas I only discovered in production.
The Cost-Quality Tradeoff You're Ignoring
Let's start with the numbers, because they motivate everything that follows.
As of Q1 2026, Claude Sonnet 4.6 costs roughly 3x as much as Haiku 4.5 per 1M input tokens and 15x as much per 1M output tokens. In a system serving diverse workloads, that difference compounds fast.
A content moderation task—checking if a user comment violates policy—doesn't need Sonnet's extended reasoning. Haiku handles it reliably and responds in 100ms vs. 300ms for Sonnet. You're paying 15x the cost for 3x slower latency.
A creative copywriting task for email marketing? That does need Sonnet. The output quality difference is material. Users notice.
The problem isn't that Sonnet is expensive. It's that you're routing both tasks to the same model. In a mature system with thousands of daily requests, that wastes millions of tokens annually.
Intelligent routing flips the equation. Instead of "use the best model for everything," you ask: "What's the minimal model that solves this specific task reliably?" The answers vary wildly by use case.
How to Classify Request Complexity
Before you can route, you need to know what you're routing. That means classifying incoming requests as either "simple" (Haiku-suitable) or "complex" (Sonnet-necessary).
The classification problem is harder than it sounds. A request that looks simple (three words) might need deep reasoning if it contains jargon or philosophical nuance. A long request might be simple—just verbose.
I've found that a three-layer classifier works well in practice:
Layer 1: Keyword-based routing — The fastest. Certain task types almost always route to one model. "Summarize this" or "extract the key points" rarely needs Sonnet. "Brainstorm campaign ideas" or "debug this code" usually does. A simple regex or string-matching layer catches maybe 40-50% of requests with near-zero cost.
Layer 2: Token count and structural analysis — For requests that don't match known patterns, look at token count (longer ≠ harder, but extreme length correlates with complexity) and structural signals like nested queries or conditional logic. This catches another 30-40%.
Layer 3: Embedding-based classification — For the remaining 10-20% of ambiguous requests, compute an embedding and compare to examples of "simple" and "complex" tasks from your production logs. This is accurate but slower and costs tokens, so use it sparingly.
Most teams I've worked with find that layers 1 and 2 alone handle 80-90% of production traffic correctly. Layer 3 is a nice-to-have for high-value requests where a mismatch is costly.
✦
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
✦A two-tier routing pattern that pins the classifier itself to Haiku, cutting roughly $1,500/month in classification overhead to a quarter.
✦Real production numbers: 35→50% Sonnet ratio alerting, dedicated half-open health checks, prompt-cache hit rate recovering from 12% → 64%.
✦Eight operational gotchas not in Anthropic's docs, drawn from running this across four Dolice Labs sites and iOS/Android support bots for 6+ months.
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.
Notice the structure: layer 1 catches obvious cases fast. Layer 2 handles the ambiguous middle. Layer 3 is optional—use it only when the cost of a mismatch (sending a simple request to Sonnet) outweighs the embedding cost.
Resilient Routing with Circuit Breakers and Fallback Chains
A router that always picks the "right" model is useless if that model is unavailable. Production systems need fallback strategies.
I recommend a circuit breaker pattern: monitor success rates for each model, and if one starts failing, automatically route traffic to the other. This protects against partial outages and allows graceful degradation.
Model used: claude-haiku-4.5
Response: 4
Cost: $0.000012
This pattern gives you automatic failover without manual intervention. If Sonnet starts timing out or returning errors, the circuit breaker trips and Haiku handles the load. When Sonnet recovers, the half-open state allows gradual re-entry.
Monitoring Costs in Real Time
Routing saves money only if you know where the savings are. Real-time cost monitoring is essential.
I recommend logging every API call with: (1) selected model, (2) estimated vs. actual cost, (3) latency, (4) whether classification was correct (measured by manual spot-checks or user feedback), and (5) error rates by model.
A 3-5% misclassification rate is typical and acceptable—the cost savings from routing simple tasks to Haiku outweigh the occasional wasted Sonnet call. If your rate exceeds 10%, your classifier needs tuning.
Common Mistakes That Will Burn Your Budget
I've seen these patterns repeatedly in production systems. Avoid them.
Mistake 1: Over-relying on keyword matching
❌ Before: You keyword-match on "summarize" and route to Haiku every time.
User: "Summarize this research paper on quantum computing and explain the
philosophical implications of wave-particle duality."
This needs Sonnet's reasoning, but keyword matching sends it to Haiku. Result: generic summary, user unhappy, you blame the router.
✅ After: Add a second signal. If the request contains jargon (detected via embedding or a domain-specific dictionary), bump complexity to "complex" even if keywords suggest "simple."
❌ Before: You build fallback logic and route Sonnet→Haiku on failure. But Sonnet request costs tokens even when it fails. You're paying for two model calls.
✅ After: Use circuit breakers before attempting the call, not after. This prevents wasted API calls in the first place.
// Wrong: Call, check status, then retryconst try1 = await callModel(sonnetModel, input); // Costs moneyif (try1.error) { const try2 = await callModel(haikuModel, input); // Costs more money}// Right: Check breaker, choose model, call onceconst selectedModel = router.selectModel();const result = await callModel(selectedModel, input); // One call total
Mistake 3: Treating all output tokens equally
❌ Before: You route a request to Haiku for classification, expecting a one-word response. But the user includes max_tokens: 4096, so if Haiku decides to generate a 3,000-word essay, you pay for all of it at Haiku rates (which are cheap per token but still add up).
✅ After: Set max_tokens dynamically based on task type. Summarization? 500 tokens. Code generation? 2000. Classification? 50.
That's 70% cost reduction. On an annual basis, that's $20,000+ saved.
Your actual savings depend on your workload distribution, but the order of magnitude is typical. Most teams see 40-70% improvements.
Monitoring and Observability
Beyond cost metrics, you need visibility into the health of your routing system.
I recommend logging these signals:
Classification accuracy — Spot-check 5-10% of calls manually. Did the router pick the right model? Log mismatches.
Model performance divergence — If Haiku starts returning lower-quality results for tasks it was handling well, alert. Something changed (model version, task distribution, user expectations).
Circuit breaker trips — Every time a breaker opens, log it. This is a leading indicator of outages or performance regressions.
Latency by model — Haiku should be consistently 30-50% faster than Sonnet for equivalent tasks. If that ratio changes, investigate.
Cost per task — Group calls by task type and compare actual vs. estimated cost. Use this to tune max_tokens limits.
// Observability helperclass RouterObservability { private observations: Map<string, number[]> = new Map(); recordMetric(category: string, value: number): void { if (\!this.observations.has(category)) { this.observations.set(category, []); } this.observations.get(category)\!.push(value); } getPercentile(category: string, percentile: number): number { const values = this.observations.get(category) || []; if (values.length === 0) return 0; const sorted = [...values].sort((a, b) => a - b); const index = Math.ceil((percentile / 100) * sorted.length) - 1; return sorted[Math.max(0, index)]; } reportHealthCheck(): { healthy: boolean; warnings: string[] } { const warnings: string[] = []; // Check latency ratio const haikuLatency = this.getPercentile("latency_haiku", 50); const sonnetLatency = this.getPercentile("latency_sonnet", 50); if (sonnetLatency > 0 && haikuLatency / sonnetLatency > 0.8) { warnings.push("Haiku latency is too close to Sonnet. Check model health."); } // Check error rates const haikuErrors = this.getPercentile("errors_haiku", 99); if (haikuErrors > 5) { warnings.push("Haiku error rate elevated."); } return { healthy: warnings.length === 0, warnings, }; }}
Operational Lessons That Aren't in the Docs
The following eight lessons come from six-plus months of running this routing pattern in production: 16 daily article generations across the four Dolice Labs sites, membership eligibility checks, search re-ranking, and iOS/Android customer-support bots. Anthropic's docs are thorough, but the failure modes specific to tens-of-millions-of-tokens monthly volume don't surface until you run it.
1. Pin the classifier itself to Haiku
I keep seeing implementations that run the Layer 3 semantic classifier on Sonnet. That's backwards. Even at 300–500 tokens per classification call, doing a million classifications a month on Sonnet costs roughly $1,500/month — for the classifier alone.
Tell Haiku 4.5: "Classify the following request as simple / moderate / complex." You'll get nearly the same accuracy at about one-quarter the cost. At Dolice Labs the classifier is permanently pinned to Haiku, and only the downstream call goes through the Sonnet/Haiku selection logic.
2. Refresh keyword dictionaries every quarter
The Layer 1 keyword list tends to become write-once code. That's lethal. The semantic weight of technical terms drifts every six months. Since the start of 2026, the frequency of "agent," "orchestration," and "dispatch" in my logs has tripled, and a request like "build me an agent" — which Haiku used to handle fine — has become materially more complex.
Once a quarter I export the last 90 days of metrics, find requests that Haiku handled but scored poorly on user feedback, and look at the co-occurring keywords. The top 30 get promoted to the Sonnet-bound keyword list. That single ritual drops misclassification rate by about 2 points.
3. Streaming fallbacks have different mechanics
In non-streaming mode, a Sonnet error is fine — just retry on Haiku. With stream: true, if you've already emitted the first content delta to the client, mid-stream failover will visibly corrupt the rendering.
My implementation gives every stream an 800 ms "safe window": until the first delta actually reaches the client, the SSE controller buffers Sonnet's output. If Sonnet fails inside that window, Haiku can pick up cleanly.
const FALLBACK_WINDOW_MS = 800;async function* streamWithSafeFallback(prompt: string, primary: 'sonnet' | 'haiku') { const startedAt = Date.now(); let firstDeltaEmitted = false; const buffer: string[] = []; try { for await (const chunk of streamModel(primary, prompt)) { if (Date.now() - startedAt < FALLBACK_WINDOW_MS && !firstDeltaEmitted) { buffer.push(chunk); // hold within the safe window continue; } if (buffer.length > 0) { for (const b of buffer) yield b; buffer.length = 0; firstDeltaEmitted = true; } yield chunk; } } catch (err) { if (!firstDeltaEmitted) { for await (const chunk of streamModel('haiku', prompt)) yield chunk; } else { throw err; // already streaming — can't safely restart } }}
4. Circuit breakers live or die on their half-open behavior
Off-the-shelf libraries like circuit-breaker-js route real user traffic through half-open probes. If those probes fail, a circuit that was about to recover slams shut again.
At Dolice Labs we use a separate health-check function for the half-open state — a lightweight "ping" prompt that doesn't touch user traffic. The breaker only fully closes after three consecutive successful pings. That single change cut our re-incident rate by 70%.
5. Alert on routing ratio drift, not on dollar amounts
A budget-based alert like "ping me when monthly spend exceeds $5,000" arrives way too late — by then you've already burned through the budget. What actually works is a ratio alert: "ping me when the Sonnet share goes from a target of 35% toward 50%." That tells you within 30 minutes whether the routing logic is broken or whether user request distribution has shifted.
We export sonnet_ratio_5min from Cloudflare Workers into Grafana, and Slack pages me when it crosses 45%. That alert has saved me twice already from a bug in the Layer 1 keyword dictionary.
6. Force Sonnet on paths gated by quality checks
For paths where a hard quality gate like article_gate.py runs, the right answer is to force Sonnet regardless of cost. The reason is simple math: when Haiku writes the article, it gets rejected more often, and the cost of regeneration easily exceeds a single Sonnet run.
Always implement a path-based bypass in your router. There are always some paths where you should optimize for success rate, not cost. Dolice Labs runs three regimes side by side: /api/generate-premium-article is forced Sonnet, /api/classify-comment is forced Haiku, everything else gets normal routing.
7. Prompt caching and routing fight each other
Running long contexts through Sonnet with prompt caching cuts cost by 70–90%. But if your router flips between Sonnet and Haiku every call, your cache hit rate craters.
The fix is session stickiness: keep the same session ID on the same model. We store the selected model per session ID in Cloudflare KV with a 24-hour TTL. Cache hit rate went from 12% to 64% after that change.
8. Request distribution shifts quietly, then suddenly
The most important lesson: request distribution does not change linearly. The night Google's new AI editor Antigravity shipped, Antigravity Lab traffic tripled within 24 hours, and most of that surge was simple-category requests like "how do I set this up" and "how do I install it." Had we been pinned to Sonnet, we'd have burned an extra ~$400 in a single day.
If you're running production routing, build a daily habit of diffing the last 24 hours against the trailing 30-day distribution. When the distribution shifts, that's your signal to retune the routing thresholds.
What Comes Next
If you implement intelligent routing, you've solved the immediate cost problem. But there are higher-order optimizations:
Prompt optimization — Once you're routing correctly, the next lever is shrinking prompts. Many teams use unnecessarily verbose system prompts. Shorter prompts = fewer tokens, regardless of model.
Caching — The Claude API supports prompt caching for long contexts. If you're reading the same document repeatedly, this can cut costs by 70-90%.
Batch processing — For non-realtime use cases, batch calls overnight at a discount. This is especially powerful for Sonnet-level work that doesn't need immediate response.
Intelligent routing is step one. These are steps two and three.
Wrapping Up
Building an intelligent router isn't complicated. It's a classification problem (layers 1-3), a resilience pattern (circuit breakers), and measurement (cost monitoring). Most teams can implement this in a day or two.
The payoff is enormous: 40-60% cost reduction, faster perceived performance on simple tasks, and graceful fallbacks that keep your system running through partial outages.
Start with layers 1 and 2 (keywords + token count). That'll catch 80-90% of your traffic. Only add embedding-based classification if you find your misclassification rate creeping above 8-10%.
Your API bills will thank you.
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.