Skip to content

Build1 publisher3 min readPublished

A model string one character off bills cached tokens at four times the rate

Model identifiers are untyped strings, so a one-character slip on a multi-vendor gateway picks up a different cache-read rate with no error and nothing in the logs. A dev.to post turns the pricing page into a CI check.

The Engineer · Build desk

Illustration accompanying A model string one character off bills cached tokens at four times the rate

What happened

  • A dev.to post read a gateway pricebook at 2026-09-19T23:31:02Z and found 33 models, each with input and output prices per 1M tokens and, for most entries, a cache-read price.
  • Version suffixes in the shared namespace use a hyphen, a dot or a word, so no single invented pattern catches every near-duplicate and names have to be compared against each other.
  • The post publishes an illustrative script, standard library plus difflib, that flags names above a 0.85 similarity threshold whose price fields disagree.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost A team that adds prompt caching months after choosing a model inherits a price column nobody compared at selection time, and the bill moves without anyone editing the config.
  • exposure The script's field names are placeholders the post tells you to verify, so anyone who drops it into CI as-is is trusting a vendor JSON shape that has not been checked.
  • constraint The control compares names against names, so it will not notice a cache-read price that moves under an identifier you already ship.
  • decision Someone has to decide whether the repo's constant or the gateway's pricebook is authoritative for a model id, because the script assumes the pricebook is.

Both spellings are real entries, so both calls succeed and both responses look fine [9]. Model identifiers are opaque strings with no type system behind them, an editor will not autocomplete one unless you built a constant, and a test suite will not fail on a string that is valid for a model you did not want [8]. The gateway concentrates the problem, because it folds several vendors' naming conventions into one namespace and each of the dangerous pairs is one character apart [10].

The gap to price is in the cache-read column. On the claude pair it is a factor of four [17]. The post gives input and output prices per 1M tokens and does not spell out the unit on the cache-read column [3]; if cache reads bill in the same per-million unit, the 0.3 difference works out to 300 dollars per billion cached-read tokens [18]. The grok pair's gap is about 67 percent [19]. glm-5.2 and glm-5.3 differ in every field, cache reads included, 0.169 against 0.22, roughly 30 percent apart [6][20].

The script the post publishes fetches the pricebook, groups names by similarity, compares the price fields of each near-duplicate pair, warns on any divergence, and treats the pricebook as the source of truth [12]. It uses the standard library plus difflib's SequenceMatcher, with SIMILARITY_THRESHOLD set to 0.85, and the post labels it "Illustrative example. Not production code." [13]. Consider where a fixed ratio threshold bites hardest: "grok-4.5" is eight characters, so one differing character is 12.5 percent of the string, against 6.25 percent for the sixteen-character "gemini-3.7-flash" [22]. Turn the threshold up to cut false positives and the short names fall out of the comparison first.

The gemini pair is the one a price comparison cannot flag. gemini-3.7-flash and gemini-3.8-flash match on all three fields shown, and the post calls that its own trap: price alone will not tell them apart [7]. A warning conditioned on divergent prices stays quiet there, so pinning that identifier takes the repo-side constant, not the pricebook diff [8].

The post is blunt about the alternative. The usual advice is "read the pricing page carefully", and, as the post wrote, "That's not a control. It's a hope." [14]. It is also careful about what the numbers are: a lower cache-read price means cached input tokens are billed at a lower rate, and the source file contains price fields only, so nothing in it speaks to latency, throughput or quality [15]. Vendors ship point releases into a shared namespace whichever way their own convention runs, with a hyphen, a dot or a word [11]. Eight of the pricebook's 33 entries sit in the four pairs the post calls out [21].

What to watch

  • A second read of the same pricebook: whether the four pairs' cache-read prices still stand where they did on 19 September.
  • Whether the 33-entry list accumulates more one-character pairs as vendors ship point releases into the shared namespace.
  • A gateway that echoes the resolved model id and its billed rate in the response; that moves the check off name similarity.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories