The day after I launched a small LLM cost calculator, I noticed it listed Claude Opus at $10/$50 per million tokens. The real price was $5/$25. I had typed the rates in by hand, and I had typed them wrong.
A wrong number on a pricing page is worse than no number. The page looks authoritative, someone budgets against it, and nothing warns them. So I rebuilt the site around one rule: no human types a price again. Here is what that took, including the two failures that taught me the most.
Why I don't scrape the vendor pages
None of the three vendors I cover (Anthropic, OpenAI, Google) publish a pricing API. Their pricing pages are JS-rendered, so curl from CI gets an empty shell. A headless browser can render them, but the first silent DOM change turns your scraper into a machine that publishes garbage under a green checkmark. For a pricing tool, breaking loudly is a feature. Breaking silently is the worst case.
So I pull from the LiteLLM community pricing dataset instead: a public, machine-readable JSON that tracks vendor pricing pages. The tradeoff is honest: it is community-maintained, not an official feed, and it can lag a vendor change by a day. Every calculator page states this and links to the official pricing page. If the two disagree, the vendor is right.
Guardrails: refuse to publish
A daily GitHub Actions job fetches the dataset, extracts the 16 models I track, and rewrites the static pages. It aborts before writing anything if:
- the fetch fails, or a tracked model disappears upstream
- any rate moved more than 50% since the last publish
- any context window moved more than 20% (upstream sometimes flips between 1,000,000 and 1,048,576 — both "1M", which must pass — while a typo like 200K to 2M must not)
The philosophy: stale beats wrong. A one-day-old correct price is mildly annoying. A fresh wrong price is a trap.
The guard fired for real — and I almost missed it
On July 30, OpenAI cut GPT-5.6 Luna by 80% ($1.00 to $0.20 input, $6.00 to $1.20 output). My 50% guard tripped exactly as designed: the job failed, the site stayed on the old price, and nothing false went out.
What I had not designed was any way to hear about it. The workflow failed quietly for five days before I noticed. The fix I should have shipped on day one: on failure, the workflow now opens a GitHub issue, which lands in my inbox as an email. Repeat failures comment on the same issue instead of piling up new ones.
Verifying the change by hand also caught something I would have blindly published otherwise: an upstream correction that dropped two models' context windows from 1,050,000 to 272,000. The vendor docs list a 400K window with 272K max input, so the old figure had simply been wrong. Guarding context windows, not just prices, earned its keep the first time it mattered.
The gotcha nobody warns you about
Commits pushed with the default GITHUB_TOKEN do not trigger other workflows. This is intentional — it is how GitHub prevents infinite workflow loops — but it means that if your cron job commits updated pages and you rely on a separate on: push workflow to deploy, nothing deploys. Every check is green, the repo has fresh numbers, and the live site serves the old ones indefinitely.
The fix is boring: the deploy step lives inside the same workflow, right after the commit step. A personal access token also works, but that trades a footgun for a secret to rotate.
The part that looks like a bug but is load-bearing
The sync commits every day even when no rate moved, because the "last synced" date stamps change. That looks like noise, and I nearly suppressed it. Then I learned that GitHub automatically disables scheduled workflows on public repos after 60 days without repository activity. The noisy daily commit is what keeps the cron alive. I kept it, and made the commit message say honestly whether rates moved or only dates did.
What it adds up to
The site is static HTML with no backend; the daily job is the entire moving part. The output I actually wanted all along is the change log: dated entries for every rate movement, with percentages. Vendor pages tell you what a model costs today and quietly erase what it cost last month. Tracking started in late July, and its first real entries are those July 30 OpenAI cuts.
The result is at getllmcalc.com, and the change log it produces is at getllmcalc.com/pricing-changes.
Top comments (0)