●MODEL — Claude Sonnet 5 becomes the default across all plans, with stronger planning, tool use, and autonomy●PRICE — Sonnet 5 launches at $2 input / $10 output per million tokens through August 31●MODEL — Sonnet 5 nears Opus 4.8 performance at a lower price for always-on agents●CODE — Claude Code adopts Sonnet 5 as default with a native 1M-token context window●CODE — Claude Code adds sandbox credential blocking and org-level model restrictions●CLOUD — Claude is generally available in Microsoft Foundry on Azure with Azure-native access●MODEL — Claude Sonnet 5 becomes the default across all plans, with stronger planning, tool use, and autonomy●PRICE — Sonnet 5 launches at $2 input / $10 output per million tokens through August 31●MODEL — Sonnet 5 nears Opus 4.8 performance at a lower price for always-on agents●CODE — Claude Code adopts Sonnet 5 as default with a native 1M-token context window●CODE — Claude Code adds sandbox credential blocking and org-level model restrictions●CLOUD — Claude is generally available in Microsoft Foundry on Azure with Azure-native access
Introductory Pricing Has an End Date — Effective-Dated Cost Forecasts for the Sonnet 5 Price Step
Claude Sonnet 5's introductory $2/$10 pricing ends on 2026-08-31 and reverts to $3/$15. A static price map will quietly understate your September forecast by a third. Here is an effective-dated price table and forecast design that absorbs the step.
On the first morning of July I was switching my automation over to Claude Sonnet 5, which had just become the default model. The change itself took a few lines. But while updating the monthly cost sheet that goes with it, I stopped. Would this estimate still be true in September?
Sonnet 5's $2 / $10 per million tokens (input / output) is an introductory price that runs through August 31, 2026. From September it reverts to the standard $3 / $15. The estimate I had just written would be wrong in two months — and it would go wrong silently, with no exception and no failing test. As an indie developer running several unattended pipelines on a thin budget, I worry less about code that breaks loudly than about billing forecasts that drift quietly. This article is the design I settled on: making price validity windows a first-class part of the cost model.
What changes, when, and by how much
First, the facts. Sonnet 5 shipped on 2026-06-30 with this price structure:
Model
Window
Input (/MTok)
Output (/MTok)
claude-sonnet-5 (introductory)
2026-06-30 – 2026-08-31
$2
$10
claude-sonnet-5 (standard)
2026-09-01 –
$3
$15
claude-opus-4-8
open-ended
$5
$25
Notice that both input and output multiply by exactly 1.5. That detail matters later: whatever your input/output mix, the Sonnet 5 portion of your bill goes up by exactly 50%.
One more subtlety is the cutover boundary. Which timezone's "August 31" closes the window depends on the billing system, and for forecasting purposes you do not need to know. Round toward the expensive side. I assume the standard price applies from August 31 in my local time — one day early. A forecast that comes in slightly high never hurts a decision; one that comes in low does.
A static price map starts lying in September
Here is the shape of the problem — the common implementation with unit prices hard-coded:
This is correct through August 31 and uniformly too cheap from September 1. No exception. Types check. Tests stay green. The only thing that disagrees is the invoice.
Let me size the gap with my own numbers. My pipelines consume roughly 6.0M input and 0.9M output tokens per day on the Sonnet line — 180M in, 27M out per 30-day month.
That is $315 a month of difference, and the static map forecasts September at $630 against a real $945 — an understatement of about 33%. A forecast that is off by a third is barely a forecast.
✦
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
✦How much your monthly bill actually jumps when the introductory window closes, worked out from real token volumes
✦A working TypeScript price book with validFrom / validUntil windows that fails closed on gaps and overlaps
✦How the model-selection break-even shifts across the price step, and how to separate permanent decisions from window-limited ones
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.
The core fix is simple: store prices not as a map of model → rate but as rows of model × period → rate. Accounting systems have called this effective dating for decades; we just borrow it.
// After: each price row carries its validity windowtype PriceWindow = { model: string; inPerMTok: number; outPerMTok: number; validFrom: string; // ISO date, inclusive validUntil: string | null; // ISO date, exclusive; null = open-ended};const PRICE_BOOK: PriceWindow[] = [ { model: "claude-sonnet-5", inPerMTok: 2, outPerMTok: 10, validFrom: "2026-06-30", validUntil: "2026-08-31" }, // rounded one day toward the expensive side { model: "claude-sonnet-5", inPerMTok: 3, outPerMTok: 15, validFrom: "2026-08-31", validUntil: null }, // standard price { model: "claude-opus-4-8", inPerMTok: 5, outPerMTok: 25, validFrom: "2026-06-30", validUntil: null },];function resolvePrice(model: string, onDate: string): PriceWindow { const hits = PRICE_BOOK.filter( (w) => w.model === model && w.validFrom <= onDate && (w.validUntil === null || onDate < w.validUntil) ); if (hits.length === 0) { // fail closed: never let an unpriced date pass through as $0 throw new Error(`no price window: ${model} on ${onDate}`); } if (hits.length > 1) { throw new Error(`overlapping price windows: ${model} on ${onDate}`); } return hits[0];}function estimateCostOn( model: string, onDate: string, inTok: number, outTok: number): number { const p = resolvePrice(model, onDate); return (inTok / 1e6) * p.inPerMTok + (outTok / 1e6) * p.outPerMTok;}
Three deliberate choices here:
Dates are ISO strings compared lexicographically. Going through Date objects invites timezone interpretation, and day-granularity decisions do not need clock time.
Zero matching rows is an exception. Silently pricing an unknown date at $0 hides the hole until the invoice arrives. This is the same reasoning I used in a fail-closed unit-price registry that survives new model launches — that article rejected unknown models; this one rejects unknown dates of known models.
Overlapping rows are also an exception. If a data-entry mistake makes two windows overlap, the resolved price becomes an accident of filter order. Non-deterministic price resolution is undebuggable.
Draw the price step into the monthly forecast
Once price is a function of date, forecasting reduces to "look up the right rate for each day and accumulate."
// Build monthly cost from daily token consumptionfunction forecastMonthly( model: string, dailyInTok: number, dailyOutTok: number, fromDate: string, // e.g. "2026-07-01" days: number): Map<string, number> { const byMonth = new Map<string, number>(); const cur = new Date(fromDate + "T00:00:00Z"); for (let i = 0; i < days; i++) { const iso = cur.toISOString().slice(0, 10); const cost = estimateCostOn(model, iso, dailyInTok, dailyOutTok); const month = iso.slice(0, 7); byMonth.set(month, (byMonth.get(month) ?? 0) + cost); cur.setUTCDate(cur.getUTCDate() + 1); } return byMonth;}const f = forecastMonthly("claude-sonnet-5", 6_000_000, 900_000, "2026-07-01", 92);// 2026-07 → $651.0 (31 days × $21)// 2026-08 → $661.5 (30 days introductory + Aug 31 at standard: 30×$21 + 1×$31.5)// 2026-09 → $945.0 (30 days × $31.5)
Look at the August line: the edge of the step is already visible, because of the conservative rounding. That is the practical difference from a static map — the step appears in the forecast itself, so when someone asks about the September number you can answer, immediately, that it is not a price increase but the end of a discount. The accumulation style is the same one I described in forecasting monthly token costs from usage data; only the price lookup became date-dependent.
If you use discounted line items such as cache reads or the Batches API, give each line item its own PriceWindow rows. There is no guarantee an introductory multiplier applies uniformly across line items, and the lazy shortcut of "multiply the model by 1.5" will drift again depending on your mix.
Catch expiring windows in CI, not on the morning of September 1
Effective dating has one operational weakness: forgetting the successor row. If a window closes and no row follows it, every forecast past that date throws. Throwing is exactly what fail-closed promised — but discovering it on September 1 is too late, so we test coverage ahead of time.
// Guarantee the price book covers the whole forecast horizonfunction assertCoverage(model: string, fromDate: string, horizonDays: number): void { const cur = new Date(fromDate + "T00:00:00Z"); for (let i = 0; i < horizonDays; i++) { const iso = cur.toISOString().slice(0, 10); resolvePrice(model, iso); // throws on any gap or overlap cur.setUTCDate(cur.getUTCDate() + 1); }}// Test: every model must be priced for the next 180 daystest("price book covers 180-day horizon", () => { const today = new Date().toISOString().slice(0, 10); for (const model of new Set(PRICE_BOOK.map((w) => w.model))) { assertCoverage(model, today, 180); }});
The 180 days are not magic: 90 for a quarterly forecast plus 90 of slack. With this test in place, the working habit becomes "the day you read a dated pricing announcement, you add the closing row and the successor row as a pair" — because adding only one of them fails CI the same day. Price increases and discounts are the same kind of change to this table.
Do not let an introductory price make permanent decisions
The mechanics end here; what remains is judgment. During the introductory window, Sonnet 5 looks 60% cheaper than Opus 4.8 on both input and output. From September the gap narrows to 40%. $2 versus $5 feels very different from $3 versus $5.
Where this bites is retry-adjusted break-evens. When you move a stage down from Opus to Sonnet and absorb the quality gap with retries, Sonnet's effective cost is (1 + retry rate) times its list price. At the introductory rate — 40% of Opus — Sonnet wins as long as the retry rate stays under 150%, i.e. up to 2.5 executions on average. At the standard rate — 60% of Opus — the break-even drops to 67%. A "Sonnet is good enough" call made in July can stop being true in September.
So I split decisions by their lifetime:
Permanent decisions use the standard price. Default model choice, routing thresholds, per-stage model assignment — all computed at $3 / $15. The introductory price is a tailwind that makes those decisions temporarily more profitable, never their justification.
Movable bulk work gets pulled into August. Re-classifying old logs, regenerating embeddings, batch-summarizing archives — anything "whenever" runs while the rate is two-thirds. I have about 200M input tokens of reprocessing scheduled for August; that alone is the difference between $400 and $600.
Re-verify migration economics at the September rate. I covered the mechanics of the cutover itself in re-warming prompt caches when you switch models; if the payback math behind your migration used the introductory price, redo it at $3 / $15 before September does it for you.
Reconcile the first September invoice against the forecast. The only real test of your effective-dated rows is the bill. If they disagree, either your rounding direction or your line-item split is wrong.
Wrap-up — what to do before the end of August
The work is small. Add a validity column to your price table, insert the two rows for the 8/31 boundary today, and put the coverage check in CI. Then recompute permanent design decisions at $3 / $15, and pull whatever heavy one-off processing you can into the discount window. That removes the future where your forecast is silently a third too low on the morning of September 1.
Discounts come with end dates. Giving your data structures a place to store those end dates is what makes a discount safe to use at full speed. If you are juggling your own dated announcements, I hope this design saves you a September surprise.
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.