n4nAI

tiktoken vs tokenizers: counting tokens across models

A practical head-to-head comparing tiktoken vs huggingface tokenizers for token counting across models, covering speed, coverage, ergonomics, and cost.

n4n Team4 min read952 words

Audio narration

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

When you need to estimate cost before sending a request to an LLM, the choice between tiktoken vs huggingface tokenizers determines whether your counts match the provider’s billing. OpenAI’s API bills by tokens counted with tiktoken’s vocabularies; open-weight models served through Hugging Face or vLLM use their own tokenizers. Pick wrong and your pre-flight size checks will be off by 10–30% on non-OpenAI models.

Capabilities and Model Coverage

tiktoken’s scope

tiktoken is a narrow tool. It embeds the exact BPE merges and vocab files for OpenAI’s model families: cl100k_base for GPT-3.5/GPT-4, o200k_base for GPT-4o and o1, plus legacy encodings like p50k. You address it by model name or encoding name.

import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
tokens = enc.encode("Function calling requires exact token counts.")
print(len(tokens))  # integer count

It has no knowledge of Llama, Mistral, or Qwen. If you pass a non-OpenAI model name it raises KeyError unless you manually load a custom .tiktoken file (rarely done).

Hugging Face tokenizers’ scope

Hugging Face tokenizers (exposed via transformers.AutoTokenizer) loads any tokenizer.json from the Hub or local disk. That covers every open-weight model with a published tokenizer, including exact matches for vLLM-served models.

from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B")
ids = tok("Function calling requires exact token counts.").input_ids
print(len(ids))

The tradeoff: you must know which tokenizer revision matches the weights your inference server runs. A Llama-3-8B-Instruct build from a different commit may ship a modified chat template but identical vocab; token counts stay stable, but chat-template wrapping changes the prefix tokens.

Price and Cost Model

Neither library charges you. Both are open-source (tiktoken: MIT, Hugging Face tokenizers: Apache-2.0). The cost dimension is accuracy of prediction.

If you route through a gateway that does per-token usage metering across 240+ models behind one OpenAI-compatible endpoint, accurate client-side counts still let you reject oversized prompts before they incur server-side spend. A 200k-token document silently truncated by a provider is cheaper than a rejected request, but only if you knew the limit.

tiktoken gives you the exact count OpenAI will bill. HF tokenizers gives you the exact count your self-hosted or HF-inference model will see—but only if you loaded the right file. Mismatched tokenizer versions produce silent off-by-N errors that surface as unexpected max_tokens truncations.

Latency and Throughput

Both libraries are Rust cores with Python bindings. For a 1k-character string, both encode in sub-millisecond to low-single-digit-millisecond range on a modern CPU. The real difference is warm-up.

tiktoken loads its embedded vocab from the package directory; no network. First encoding_for_model call after import costs a few milliseconds.

HF AutoTokenizer.from_pretrained triggers a Hub fetch on first use unless the repo is cached. Even cached, it deserializes a larger JSON structure and instantiates a Rust tokenizer object—typically 20–100ms cold, negligible warm. In a serverless cold start, that delay and the ~1GB transformers dependency tree matter.

For batch processing, both support parallel encodes via separate encoder instances. tiktoken is trivially thread-safe per encoder; HF tokenizers are also thread-safe but heavier in memory per loaded vocab.

Ergonomics and Dependency Footprint

tiktoken’s API is three calls: encoding_for_model, encode, decode. No config, no chat templates.

# count only, no extra machinery
def count_openai(text, model="gpt-4o"):
    return len(tiktoken.encoding_for_model(model).encode(text))

HF forces you into the transformers ecosystem if you use AutoTokenizer, or you can use the lean tokenizers crate directly:

from tokenizers import Tokenizer
tok = Tokenizer.from_file("llama3.json")
print(len(tok.encode("Hello").ids))

But most teams just pull transformers for the model name resolution. That brings torch-adjacent imports and a much larger container. For a CLI that only needs counts, tiktoken is a 300KB wheel; HF path is tens of MB.

Ecosystem and Maintenance

tiktoken tracks OpenAI’s model releases. When GPT-4o launched, o200k_base landed within days. It will never support non-OpenAI models by design.

HF tokenizers is the default for the entire PyTorch/TensorFlow transformer world. If your stack already loads models via transformers, counting tokens is free. You also get chat templates, so you can count the exact tokens after applying a prompt template:

tok = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.2")
prompt = tok.apply_chat_template([{"role":"user","content":"Hi"}], tokenize=False)
print(len(tok(prompt).input_ids))

That capability is absent in tiktoken; you must hand-build the OpenAI chat markup.

Limits and Edge Cases

  • Special tokens: tiktoken exposes encode_ordinary (ignores specials) and encode (respects them). HF distinguishes add_special_tokens=True/False. Forgetting this double-counts BOS/EOS.
  • Streaming: Both support incremental encoding, but tiktoken’s Encoder is stateless; HF requires tok.encode_batch for many strings.
  • Unicode normalization: tiktoken uses OpenAI’s exact NFKC rules; HF uses the tokenizer’s configured normalizer. Emoji and CJK text will tokenize differently across the two even for “equivalent” models.
  • Version drift: HF tokenizers pinned to a transformers version may change behavior on upgrade. tiktoken is frozen per encoding hash.

Head-to-Head Comparison

Dimension tiktoken Hugging Face tokenizers
Model coverage OpenAI only (cl100k, o200k, p50k) Any HF-published vocab (Llama, Mistral, Qwen, etc.)
Cost to use Free, MIT Free, Apache-2.0
Cold-start latency <5ms, no network 20–100ms + Hub fetch if uncached
Dependency weight ~300KB wheel transformers + tokenizers (tens of MB)
Chat templates Manual string build Built-in apply_chat_template
Special token control encode / encode_ordinary add_special_tokens flag
Exact billing match OpenAI APIs only Self-hosted / HF inference only
Maintenance locus OpenAI releases Broad OSS community

Which to Choose

Solely calling OpenAI or Azure OpenAI: Use tiktoken. It is the ground-truth counter for those endpoints, has zero config, and adds no heavy deps. Wrap it in a one-liner and move on.

Mixed fleet (OpenAI + open-weight via vLLM/TRT-LLM): Load tiktoken for gpt-* model names and HF tokenizers (not full transformers if you can avoid it) for everything else. Key off a model registry:

def count_tokens(text, model):
    if model.startswith("gpt") or model.startswith("o1"):
        return len(tiktoken.encoding_for_model(model).encode(text))
    else:
        from tokenizers import Tokenizer
        return len(Tokenizer.from_file(f"tokenizers/{model}.json").encode(text).ids)

Serverless or edge with strict cold starts: tiktoken wins on footprint. If you must support open models there, pre-bake the specific tokenizer.json into the image and use the lean tokenizers lib, not transformers.

Prompt engineering with chat templates: If you need to count tokens after applying a model’s official chat template (common for Llama-3, Mistral), HF transformers is the only sane path. Reimplementing those templates by hand is a bug farm.

Cost guardrails in a gateway: When you already receive per-token metering from the backend, use tiktoken or HF locally only to enforce max_tokens pre-checks and to show users estimates. Don’t treat client counts as authoritative for billing—rely on the provider’s returned usage object.

Pick the library that matches the model you actually send, not the one with the nicer README.

Tagstiktokentokenizerscomparisontoken-counting

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 token counting & cost estimation libraries posts →