Per-model pricing is going to wreck your price catalog

Announcements
Read time: 3 min

Arnon Shimoni
✓ Expert opinion
Imagine you need to raise prices 5% across the board… With Solvimon that's easy, but at a data infrastructure company I spoke to last month, that costs >1,200 API calls.
That's because their billing lead, that has owned the system for 4 years, has a catalog with about 25 metrics, multiplied by region, multiplied by instance profile like gneral purpose, CPU-optimised, and so on. Call it 600 live price points on a slow day.
If you price anything per model, that shape is coming for you, and I'd know because I had this problem at an audiobook company I worked for where we had 209 price points but with 166k entry points (via coupons, campaigns, and payment methods that gave extra discounts, etc.).
Because there's no "update" operation on pricing usually, the change has to be scripted with an end-date on every existing price point, then create its replacement, then archive the old one, then migrate… Between two and four writes per price, strictly ordered, because getting it wrong means a bad time for everyone.
I don't know what their exact limit was… But tools like Stripe often limit you to 100 calls a minute, but that means they'd have about 15 minutes window to get things right…

Changing what you charge degrades the thing people are paying for. And if the number turns out wrong, there's no undo, but there will be a second script to fix it, etc.
Every billing system already does this in theory
There's a price object and an API that writes to it and you can change a price in the UI in about 8 seconds. Stripe does it, Chargebee does it, Zuora does it, and Solvimon does it too. It's been solved for a decade, and if you need to change 600 of them, you write a loop, which is what loops are for. Right?
That's a fair description of the world and it's how everyone built, but the assumption doing the heavy lifting here is that a price is a row (notice how I didn't say "load bearing"?).
With row-based pricing you create rows, you end-date rows, you archive rows, you create more rows.
Best I can think about it, It's like editing a restaurant menu by walking around crossing items out on each printed copy, during service, while people are ordering. Bad service! I know some restaurants do that, but we should be better.
"Fine, but that's a 600-price-point problem. I have twelve."
That's what I assumed too… 600 price points was at the far end of the curve, exotic, safe to ignore.
But I'm telling you I had 209 at a normal B2C… Plus, this week I sat in on a call with the billing ops lead at a payments company running roughly 17,000 merchants. My colleague João asked what she most needed from us, and the answer was "the ability to update anything at mass scale". That is at the top of the list above anything else.
Same shape as the data infrastructure company, with the labour moved to our side of the table. Nobody has a mechanism, so a price change becomes a favour someone performs for you.
Their off-the-shelf merchants are the easy half. The custom tier is the other one: hundreds of distinct pricing configurations across thousands of merchants, same structure, different numbers in every row.
The arithmetic gets worse from here, for a specific reason: AI pricing dimensions multiply. Per model is one axis. Add context-window tiers, regions with different inference economics, and a split between cached and uncached tokens, and each axis multiplies the last. Supporting a new model adds a column to that matrix, and the matrix is your catalog. A company that shipped with 12 prices in 2023 can be sitting on several hundred today without anyone having decided to expand anything.
Neither company in this post sells AI. They reached 600 price points and 17,000 merchants on ordinary usage-based pricing, the boring kind, with none of the axes above. Per-model pricing has more of them, and it's arriving now.
What a row-shaped price costs you
No atomicity. Call 700 of 1,200 fails. Half the catalog is new, half is old, and there's no transaction. Someone writes a second script to undo the first. That script also does 1,200 calls.
No dry run. You can't ask what a change would do, because the change has no existence until you run it. The plan and the execution are the same object.
No review. A 5% increase is a commercial decision your CFO should see. What you can hand them is a Python file.
Ordering is load-bearing and unenforced. End-date before create, and usage lands against no price. Create before end-date, and it rates twice. I've heard both.
Rollback is a support ticket. Every system I've looked at will move a customer from plan version 3 to version 5. Going back to 3 means someone builds a schedule by hand.
That last one is the tell. If prices were documents, rolling back would mean applying yesterday's document. The fact that it needs a human with a script is the whole argument compressed into one line.
Infrastructure sorted this out around 2012
Nobody configures servers by PATCHing them one at a time. You declare the desired state as a document, the tool diffs it against reality, shows you a plan, and applies it. Terraform, Kubernetes manifests, whichever.
The insight was the move from a sequence of mutations you perform to a state you declare.
Pricing never got that. Which is strange, because a price list has every property that makes declarative config work: structured, reviewable, changes rarely and consequentially, and the blast radius is denominated in revenue rather than latency.
The closest thing I've seen working is unglamorous. Generate the changes as draft schedules, show the customer the whole set, approve in bulk. Draft is the word doing the work. It splits "here is what would happen" from "make it happen," which is plan and apply, and it puts approval with the person who owns the commercial decision rather than the person who owns the script.
Bulk matters as much. When Joao described that approach, her immediate question was whether approval was per-item. Per-item across 200 merchants defeats the point, and she said so mid-sentence.
Prices have a time dimension
The Terraform comparison is seductive and only half right.
Declarative config assumes one desired state and it's now. A price has a desired state per point in time. You're raising the rate on 1 November for new contracts, grandfathering 40 enterprise accounts until renewal, honouring a 2-year lock for 3 of them, and back-dating a correction to August because someone fat-fingered a rate card in July. apply means make it true now. Pricing needs make it true from this date, for this population, without touching what already rated.
The document you want is a timeline. Harder to build, harder to diff (what does a diff between two timelines even render as?), and much harder to reason about when two changes overlap the same price point with different effective dates.
I don't have a clean answer and I don't think anyone does. Declarative pricing is the right direction, effective-date semantics are unsolved, and anyone claiming otherwise should be asked to show you the diff view for two overlapping schedules.
The counter-argument comes from my own side
When that payments company asked for bulk conditional price updates, Joao's response was that no customer had ever asked for it before. Bulk version migration, once. Updating specific prices based on conditions, never.
So either this is a real need that surfaces first at companies with four and five-figure customer counts, or it's two loud data points and I'm pattern-matching on noise. Both readings fit what I have. (I believe the first, or I'd have stopped at 400 words. The sample is still 2.)
The other honest objection: declarative config has its own failure mode. The plan reads clean, the apply succeeds, and the result is wrong, because what you declared wasn't what you meant. Terraform users know that feeling. A document makes you reviewable, which is a smaller claim than correct, and still worth having.
Two questions
If you're running more than about 50 price points, go find out how your team changes one. Ask for the mechanics. Ask whoever owns it to show you the script. Then ask what happens if it fails at call 700, and who reviewed it before it ran.
I've asked those two questions of maybe a dozen companies this year. The most common answer to the first is a pause. The most common answer to the second is "the engineer who wrote it."
If that's you, go buy something where price changes are versioned plans with overrides, so changing a default doesn't mean rewriting every customer's schedule, and where the webhook carries the previous and the new version of the resource so your cache doesn't need a follow-up read to learn what happened. We built both at Solvimon because customers made us. The full declarative timeline, we don't have either. Draft schedules with bulk approval is the closest anyone has got, and it's what I'd shortlist on ahead of any feature checklist.
And if you're still running the script: end-date before create. Losing a few hours of rated usage is recoverable. Double-rating your largest merchant is a conversation.
Next one is about the other thing that keeps coming up on these calls: the date a charge accrues and the date it invoices are different dates, and almost nothing models both.
Thanks to Joao Reis and Rutger van Engelenhoven for the calls this came out of.
Ready for billing v2?
Solvimon is monetization infrastructure for companies that have outgrown billing v1. One system, entire lifecycle, built by the team that did this at Adyen.


