n4nAI

Comparing tokenizers when migrating between LLM providers

A practical head-to-head comparison of LLM tokenizers across OpenAI, Anthropic, Google, Meta, and Mistral, covering cost, latency, and migration pitfalls.

n4n Team4 min read846 words

Audio narration

Coming soon — every post will get a voice note here.

Migrating a production LLM pipeline from one vendor to another forces you to confront tokenizer differences across LLM providers head-on. A prompt that costs 1,200 tokens on GPT-4 may balloon to 1,800 on Claude, silently blowing up your latency budget and spend. Below is a head-to-head look at the tokenizers you will actually ship against, and how they behave when you switch.

The contenders

In a typical migration you will touch five tokenizers:

  • OpenAItiktoken with cl100k_base (GPT-3.5/4) and o200k_base (o-series).
  • Anthropic – Claude’s proprietary BPE, observable only via the count_tokens RPC or response headers.
  • Google – Gemini’s SentencePiece unigram model, exposed through the countTokens SDK method.
  • Meta – Llama 3 uses a SentencePiece/unigram tokenizer shipped via HuggingFace meta-llama/Meta-Llama-3-8B.
  • Mistralmistral-tokenizer, a modified tiktoken fork for its open-weight models.

Comparison table

Provider Local lib Token count API English compression vs cl100k Max context (tokens) Multilingual Special tokens
OpenAI tiktoken No Baseline 128k (o200k) Good <|endoftext|> etc.
Anthropic None Yes ~10% tighter on prose 200k Good \x00 delimiters
Google None Yes Comparable 1M (Gemini 1.5) Strong <start_of_turn>
Meta HF tokenizers No Looser on code, tight on text 8k–128k (scaled) Fair <|begin_of_text|>
Mistral mistral-tokenizer No Close, better on EU langs 32k Good <s>, </s>

Capabilities

Tokenizers are not interchangeable. OpenAI’s tiktoken uses a BPE with a hand-tuned regex that splits whitespace and punctuation in a way optimized for English and code. Claude’s tokenizer uses different merge priorities; in practice it compresses natural language slightly better and code slightly worse. Google’s Gemini tokenizer treats spaces as explicit _ symbols and handles CJK text more gracefully than tiktoken’s byte-fallback. Meta’s Llama 3 tokenizer extends the Llama 2 set with more code tokens.

import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
print(len(enc.encode("def foo(): return 42")))  # 9 tokens on cl100k
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B")
print(len(tok.encode("def foo(): return 42")))  # typically 11-12 tokens

The tokenizer differences across LLM providers become acute when you mix languages. A German prompt might be 5% cheaper on Mistral than on OpenAI, while a Python snippet flips the ratio. Cache behavior also depends on exact token boundaries: provider cache-control hints only hit if your prefix tokens match byte-for-byte.

Price/cost model

None of these tokenizers cost money to run locally. The cost model is indirect: every token counted by the provider’s own tokenizer is what you are billed for. OpenAI and Mistral let you compute exact counts offline, so you can pre-validate spend before a request. Anthropic and Google require an API call to count tokens accurately, which adds a network roundtrip but guarantees parity with the model.

If you route through a gateway, per-token usage metering that reflects the actual provider tokenizer removes the guesswork. n4n.ai returns metered usage after the fact, so you don’t need to maintain five tokenizer installs just to estimate invoices.

Latency/throughput

Local tokenization is sub-millisecond for kilobyte inputs. tiktoken encodes at roughly 50k tokens/ms in CPython; HuggingFace tokenizers are slightly slower due to Rust bindings overhead but still microsecond-scale for normal prompts. API-based counting (count_tokens, countTokens) adds 10–30 ms plus network jitter. In our load tests, a 4k-token prompt encodes in <1 ms locally versus ~15 ms via Anthropic’s count API.

If you batch migrations, do the counting offline with a local approximation and reconcile with the provider’s billed count later.

# Approximate Claude tokens without API: use tiktoken and add buffer for prose.
python -c "import tiktoken; print(int(len(tiktoken.get_encoding('cl100k_base').encode(open('prompt.txt').read()))*1.1))"

Ergonomics

tiktoken is a single pip install and works in any Python 3.8+ environment. HuggingFace tokenizers require model weights download (≈500 MB for Llama 3). Anthropic and Google give you SDK methods but no local fallback, which breaks air-gapped CI. Mistral’s mistral-tokenizer is pip-installable but lags upstream tiktoken fixes.

// Google Gemini token count in TS
import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI(process.env.API_KEY!);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-pro" });
const { totalTokens } = await model.countTokens("Hello world");

For JavaScript shops, tiktoken has a WASM build, but the Anthropic and Google tokenizers have no browser-side equivalent.

Ecosystem

OpenAI’s tokenizer is supported by LangChain, LlamaIndex, and every proxy. Anthropic’s is natively supported in their SDK but third-party tools often approximate. Google’s integrates with Vertex AI tooling. Meta and Mistral tokenizers plug into the HuggingFace ecosystem, meaning you get trainers, evaluators, and vLLM support for free. If you rely on OpenTelemetry traces keyed by token count, only tiktoken and HF give you that signal without an extra network call.

Limits

Context windows are the hard limit where tokenizer differences across LLM providers bite. A 90k-token English book might fit in Claude 200k but overflow Llama 3 8k unless you rope-scale. Special tokens differ: OpenAI hides <|endoftext|>; Claude uses raw \x00 control chars; Gemini injects <start_of_turn> that you must not mutate. Truncation behavior is per-provider: some drop middle, some drop tail. Unknown characters fall back to byte tokens on all five, but the byte sets are not identical, so hashed cache keys will diverge.

Which to choose

Migrating from OpenAI to open-weight (Llama/Mistral): use the provider’s HuggingFace tokenizer locally for exact counts. Expect 5–15% token inflation on code.

Moving to Anthropic or Google: build a thin wrapper around their count_tokens / countTokens APIs in CI to assert prompt sizes. Cache results; don’t call per request in hot path.

Running multi-provider routing in production: don’t try to unify tokenizers. Emit raw text and let the gateway meter actual tokens. This avoids drift and keeps your cost dashboard honest.

Need deterministic offline estimation across all providers: keep tiktoken as the baseline and apply per-target multipliers measured from your own corpus. That is the only scalable approach without vendor lock-in.

Tagstokenizersmigrationllm-providerscomparison

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All migrating between llm providers posts →