LLM vendor lock-in: the 30% your per-token price comparison misses

Switching LLM vendors is rarely blocked by the request format. Pexon finds the real cost in prompts, evaluation sets and caching. Anthropic documents that its newer tokenizer produces roughly 30 percent more tokens for identical text, so per-token price comparisons do not transfer between vendors. A gateway makes routing a configuration change rather than a rewrite.

The request format is the cheapest thing you will ever have to move

Changing model vendor looks like a three-line job, and for the request format it genuinely is. Anthropic publishes a compatibility layer that lets you keep the OpenAI SDK, point the base URL at api.anthropic.com, swap the key and write a Claude model name. The call returns. Nothing in your code knows anything changed.

Anthropic's own documentation then tells you not to ship it. The compatibility layer, in their words, is primarily intended to test and compare model capabilities and is not considered a long-term or production-ready solution for most use cases. That is the vendor arguing against the easy version of portability on their own docs page, which is a more useful signal than any analyst note on the subject.

The reason is in one sentence further down that page, and it is the sentence worth pinning above the migration ticket: most unsupported fields are silently ignored rather than producing errors.

Read the list below as failure modes, not as a feature matrix. None of them throw. Your integration tests pass, your latency dashboard looks normal, and the properties your application was quietly relying on are gone.

That is the shape of the whole problem, and the API layer is the mild version of it. Everything expensive about switching vendors fails the same way: successfully.

  • The strict flag for function calling is ignored, so tool-use JSON is no longer guaranteed to follow your schema.
  • response_format is ignored. Code that assumed JSON mode now parses whatever came back.
  • Prompt caching is not supported through the compatibility layer at all — the cost model of a long shared prefix disappears without a line changing.
  • seed, logprobs, presence_penalty, frequency_penalty and service_tier are ignored.
  • System and developer messages are hoisted and concatenated into a single initial system message, newline-separated, because Anthropic supports one. A prompt that placed instructions mid-conversation no longer does.

The 30 percent that makes a per-token price comparison meaningless

A token is not a unit of measurement. It is a vendor's internal accounting decision, and it moves — including underneath you, from the same vendor, without a price change.

Anthropic documents that models from Claude Opus 4.7 onward use a newer tokenizer, and that the same input text produces approximately 30 percent more tokens than on earlier models. The docs go further and instruct customers not to reuse token counts measured on an older model to estimate cost or context-window fit. Identical traffic, identical published rate per million tokens, a bill roughly a third larger.

Now hold that next to the slide everyone actually decides on.

A competing vendor quoting twenty percent less per million tokens is more expensive than the incumbent if its tokenizer emits thirty percent more of them, and nobody in the procurement meeting has that number, because it is not on a pricing page. It lives in the counting endpoint, which is free to call and rate-limited separately from inference, so there is no cost and no capacity argument for skipping it.

The second-order effect is worse than the invoice and lands earlier. Context-window fit is measured in the same moving unit, so a prompt that fitted comfortably on one model can stop fitting on another. That is not a configuration change. That is a chunking strategy, a retrieval budget and a set of evaluations that all assumed a number which is no longer true.

Our practice, and it is not sophisticated: no migration estimate leaves this company until the same real prompt has been counted against every candidate model's own counter. It takes minutes. It is the only version of the number that survives contact with an invoice.

# Never compare vendors on a pricing page. Compare on the counter.
# https://platform.claude.com/docs/en/build-with-claude/token-counting

curl -s https://api.anthropic.com/v1/messages/count_tokens \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5",
       "messages":[{"role":"user","content":"<your real production prompt>"}]}'
# -> {"input_tokens": N}

# Same body, next candidate model. The endpoint counts under the tokenizer
# of the model you name, so N moves even inside one vendor's line-up.
# Multiply each N by that model's published input price, then compare.
# Counting is free and has its own rate limit, separate from inference.

Row one of the table below is a day's work once a gateway exists. Row five is the quarter nobody scheduled.

A migration is four projects, and only the first one gets budgeted

LayerHow it movesWhat it costsHow it fails
Request and response formatOne alias in a gateway configHours, once the gateway existsLoudly — a schema mismatch throws
Prompts and system instructionsRe-tuned per model, then re-evaluatedThe evaluation set is the asset; the prompt is disposableQuietly — output reads fine and is worse where it matters
Token economicsRe-counted with the target model's own counterRoughly 30% swings are documented inside one vendor's line-upOn the invoice, a month later
Prompt cacheRebuilt cold; the minimum cacheable length is per modelCache reads are 0.1x base input price, so losing them is 10x on that spanSilently — no error is returned
Provider-specific tool surfaceRebuilt, or droppedThe actual rewrite: server-side tools, batch endpoints, MCP connectorsAt design time, if you are lucky

The middle rows are where estimates go wrong, because they are the rows that cannot be inspected from the outside. A prompt that was tuned against one model's instruction-following behaviour will produce plausible output on another and worse output on the cases that matter. The only artefact that detects this is an evaluation set built from your own failures, which means the evaluation set — not the prompt — is the portable asset. Teams that never built one do not have a migration problem; they have a measurement problem that a migration exposes.

One honest note on ownership: rows two and three have different owners in most organisations, and neither of them is the platform team that will be asked how long the switch takes.

Caching is where a switch gets expensive without telling you

Prompt caching is not an optimisation you turned on. It is an economic model your architecture was built inside. Anthropic prices cache reads at 0.1 times the base input token price and five-minute cache writes at 1.25 times, which is why a large shared prefix — the frozen system prompt, the tool definitions, the retrieved policy documents — is affordable at all.

The minimum cacheable prefix is per model, and it does not move in the direction you would guess. Anthropic documents 512 tokens for Claude Opus 5, 1,024 for Claude Opus 4.8 and Claude Sonnet 5, 2,048 for Claude Opus 4.7, and 4,096 for Claude Opus 4.6 and Claude Haiku 4.5. The newest model has the lowest threshold and an older Opus has the highest, so the number is not monotonic across generations and cannot be reasoned about from release order.

A 3,000-token system prompt therefore caches on four of those six models — it clears 512, 1,024, 1,024 and 2,048 — and does not cache on the two that require 4,096, Claude Opus 4.6 and Claude Haiku 4.5. Same vendor, same API, same application code, one string changed.

And the failure is documented as silent: requests to cache fewer than the minimum are processed without caching, and no error is returned. The only place it surfaces is the usage object, where cache_creation_input_tokens and cache_read_input_tokens both come back as zero.

Convert that into money and it is the sharpest number in this article. The cached span stops billing at 0.1x and starts billing at 1x — ten times the price on that portion of every single request, with no exception raised, no alert, no diff in the pull request. On a workload where the shared prefix is most of the input, that is the difference between a feature that ships and a feature that gets cancelled in the second month.

Every discontinuity in this section comes from one vendor's documentation about its own models. Assume the ones between two vendors are larger, and assume the second vendor documents them less well.

The exit path is a configuration file, or it does not exist

An exit strategy that is not a file somebody can edit is a sentence in a slide deck. The concrete version is boring: every application calls one internal endpoint with an alias like chat-default, the alias maps to a provider route, and switching vendors means editing the map and reloading. No application holds a provider key, so no application needs a deployment to change model.

That is the entire mechanism. It is worth having precisely because it is unimpressive.

What a gateway does not do deserves more airtime than what it does. It does not port your prompts. It does not port your evaluation set. It does not port cache economics, because those are per model and per provider. It does not port a provider-specific tool surface — server-side execution, batch endpoints, an MCP connector — and if your agent was built on one, the gateway routes a request that the other side cannot answer.

The strongest argument against the whole approach is that a gateway is itself a dependency, and a lowest-common-denominator one. Route everything through a compatibility layer and you converge on the intersection of what every provider supports, which is a plain chat completion — meaning you pay a frontier-model premium for features the abstraction forbids you from using. That objection is correct and it is not hypothetical.

Our answer is two-part rather than a rebuttal. First, run the gateway as software inside your own tenant, so the exit from the gateway is the thing you were doing before it existed: direct SDK calls with a key. Second, make it the default path and not a mandate. Workloads that genuinely need a vendor-specific capability call that vendor directly and are recorded as deliberately non-portable, with a named owner. A register of five deliberate exceptions is a governable position. An undocumented assumption that everything is portable is not.

# One alias, two routes. Switching vendors edits this file, not the apps.
# https://docs.litellm.ai/docs/proxy/configs

model_list:
  - model_name: chat-default
    litellm_params:
      model: anthropic/claude-opus-5
      api_key: "os.environ/ANTHROPIC_API_KEY"
  - model_name: chat-fallback
    litellm_params:
      model: <second-provider>/<second-model>
      api_key: "os.environ/FALLBACK_API_KEY"

litellm_settings:
  fallbacks: [{"chat-default": ["chat-fallback"]}]
  num_retries: 3

We sell Claude engineering and we sell the layer that makes Claude replaceable

Both of those are on our price list, and saying so in the same sentence is the point of this article.

A consultancy whose recommendation cannot be reversed is not advising you. It is taking a position on your behalf, with your budget, on a time horizon it will not be around for. We think Claude is the right default for most of the enterprise work we do, and we would rather argue that case every year against a live alternative than win it once by making the alternative expensive to evaluate.

There is a second, less noble reason, and it is the one that holds up under pressure. Every number in this article came from the documentation of the vendor we work with most. Those discontinuities are visible because Anthropic writes them down — the tokenizer change, the per-model cache minimums, the silent-ignore behaviour of its own compatibility layer. A vendor that documents its sharp edges is easier to build an exit from than one that does not, and the ones you cannot see are the ones that set the schedule.

So the recommendation is not vendor-neutral. It is reversible, which is a different and more useful property.

The exit plan, in the order it actually gets built

  1. Put every application behind one alias. Nothing else in this list works while twelve services each hold their own provider key.
  2. Build the negative case into the evaluation set. Relevance tests prove the model answers; only a set built from your own past failures proves a replacement is not worse where it matters.
  3. Re-count your top ten production prompts against every candidate model's own counter, and store the numbers with a date. Costs and context budgets are both denominated in that number.
  4. Name the deliberately non-portable workloads. For each one, record the capability, the provider it belongs to, and what reversing it would cost in engineering weeks.
  5. Run the drill on a schedule and write down two figures each time. The evaluation pass rate on the fallback route, and tokens per request under the fallback tokenizer.

Alias first, evaluations second, counts third, drill fourth. Reversing that order produces a document rather than a capability, because every later step depends on a decision only the earlier one can make.

For EU financial entities this stopped being optional. DORA — Regulation (EU) 2022/2554 — requires in Article 28(8) that financial entities put exit strategies in place for ICT services supporting critical or important functions, and requires the exit plans to be comprehensive, documented, sufficiently tested and periodically reviewed. The regulation has applied since 17 January 2025. If a model sits inside a critical or important function, we would rewrite it is not a tested plan, and an untested plan is the specific thing the article names.

Everyone else is not exempt from the arithmetic, only from the audit. The drill below costs an afternoon a quarter and is the only way to find out whether the file you wrote in month one still describes the system you have in month nine.

# Exit drill. Run it on a schedule, not on the day you need it.
# DORA Art. 28(8) requires exit plans to be documented, tested and reviewed:
# https://eur-lex.europa.eu/eli/reg/2022/2554/oj

# 1. Flip the alias. One file, no application deploy.
sed -i 's/chat-default/chat-fallback/' gateway/routes.env

# 2. Run the evaluation set that defines "not worse" — not a smoke test.
pytest evals/ --junitxml="exit-drill-$(date +%F).xml"

# 3. Record both numbers in the exit plan, dated: evaluation pass rate,
#    and tokens per request under the fallback model's tokenizer.

Questions we get asked about LLM vendor lock-in

What does it actually cost to switch LLM vendors?

The request format is the cheap part and, behind a gateway, close to free. The cost sits in re-tuning prompts for a model that follows instructions differently, rebuilding the evaluation set that proves the new model is not worse, re-measuring token spend under a different tokenizer, and replacing any provider-specific tool surface the application depended on. Pexon's two-week blueprint is fixed at €4,900 and ends with a costed exit path rather than a slide about optionality. All prices are net and exclude VAT.

Can we just point the OpenAI SDK at Claude and be done?

You can, and Anthropic publishes the compatibility layer for exactly that. Anthropic's own documentation describes it as primarily intended to test and compare model capabilities and not a long-term or production-ready solution for most use cases. Prompt caching is not supported through it, the strict flag for function calling and response_format are ignored, and most unsupported fields are silently ignored rather than producing errors. The swap succeeds; the behaviour quietly changes.

Why can we not compare vendors on price per million tokens?

Because a token is not a fixed unit of anything. Anthropic documents that models from Claude Opus 4.7 onward use a newer tokenizer producing roughly 30 percent more tokens for the same input text than earlier models, and tells customers not to reuse counts measured on older models to estimate cost or context-window fit. If the unit moves that much inside one vendor's own line-up, a cross-vendor sticker price is not a comparison. Count the same prompt with each candidate model's own counter first, then multiply.

Does an LLM gateway remove vendor lock-in?

It removes one layer of it. A gateway turns the routing decision into a configuration change, so a switch no longer requires a deployment in every application holding its own API key. It does not port your prompts, your evaluation set, your cache economics or any provider-specific tool surface. Treat a gateway as the thing that makes a switch take weeks instead of quarters, not as a promise of parity.

Do we need a documented exit strategy for our model provider?

If you are an EU financial entity and the model sits inside a critical or important function, yes. DORA, Regulation (EU) 2022/2554, requires in Article 28(8) that financial entities put exit strategies in place for ICT services supporting critical or important functions, and that the exit plans are comprehensive, documented, sufficiently tested and periodically reviewed. The regulation has applied since 17 January 2025. Outside financial services it is not a legal obligation, and it is still the cheapest insurance available.

When is deliberate lock-in the right decision?

When the capability you are buying exists on exactly one platform and the workload genuinely depends on it, portability is a tax you pay for an option you will never exercise. The failure is not choosing depth over portability; it is choosing it without noticing. Name the dependency, price what reversing it would cost in engineering weeks, and put a review date on the decision so it gets re-argued rather than inherited.

Next step

Get the exit path costed before you need it

Two weeks, fixed price. We map which layers of your stack are actually vendor-bound, re-count your production prompts against each candidate model, and hand over a gateway design plus an exit plan with real numbers in it. The plan is yours whether or not we build it.