Choosing an embedding model
There are a lot of embedding models. Most comparisons rank them by benchmark score, which is the least useful way to choose, because the scores are close, they change monthly, and they were measured on somebody else’s documents.
This page organises them by the things that don’t change month to month: who runs them, what they cost you in storage, what languages they handle, and where each one will surprise you.
Three questions decide it
Section titled “Three questions decide it”Almost every model choice comes down to these. Answer them and the field narrows to two or three candidates.
1. Can your text leave your infrastructure? If no, you need a self-hosted open model, and the decision is nearly made. If yes, hosted APIs are less work and generally better.
2. English only, or multilingual? Multilingual models cost a little quality on English in exchange for handling everything else. Getting this wrong is expensive to fix later — it’s a full re-embed.
3. How many vectors will you store? Under a million, dimensions barely matter. Above that, 3,072 versus 768 is the difference between a comfortable machine and an uncomfortable bill.
Hosted API models
Section titled “Hosted API models”You send text, you get vectors, somebody else runs the GPUs.
| Model | Provider | Dimensions | Max input | Notable |
|---|---|---|---|---|
text-embedding-3-small |
OpenAI | 1,536 (reducible) | 8,191 tokens | The sensible default. Cheap, fast, good enough for most work |
text-embedding-3-large |
OpenAI | 3,072 (reducible) | 8,191 tokens | Better on hard retrieval; double the storage |
| Embed v4 | Cohere | 256 / 512 / 1,024 / 1,536 | 128k tokens | Multimodal — text and images in one vector space. 100+ languages |
voyage-3-large |
Voyage AI | 2,048 / 1,024 / 512 / 256 | 32k tokens (v3.5) | Strong on legal, finance and code. Quantization-aware training |
gemini-embedding-001 |
3,072 (reducible to 1,536 / 768) | 2,048 tokens | Well integrated with Vertex AI and BigQuery |
Open models you can self-host
Section titled “Open models you can self-host”Download the weights, run them yourself. Nothing leaves your network, and there’s no per-token cost — you’re paying in hardware and operations instead.
| Model | From | Dimensions | Max input | Licence | Notable |
|---|---|---|---|---|---|
| BGE-M3 | BAAI | 1,024 | 8,192 tokens | Apache 2.0 | Dense, sparse and multi-vector from one model. 100+ languages |
multilingual-e5-large |
Microsoft | 1,024 | 512 tokens | MIT | Strong multilingual baseline. Needs prefixes (below) |
nomic-embed-text-v1.5 |
Nomic | 768 | 8,192 tokens | Apache 2.0 | Fully open: weights, code and training data. Reducible dimensions |
all-MiniLM-L6-v2 |
SBERT | 384 | ~256 word pieces | Apache 2.0 | Tiny and fast. What the 5-minute guide uses |
BGE-M3 is the interesting one if you were planning hybrid search. It produces dense vectors, sparse (keyword-style) weights and multi-vector representations from a single pass — so you get the hybrid retrieval benefit without running a separate keyword index.
all-MiniLM-L6-v2 is older and weaker than everything else here, and still frequently the right choice for a prototype: it’s 90 MB, runs on a laptop CPU, and gets you working code before you’ve decided anything.
The prefix trap
Section titled “The prefix trap”This one silently ruins retrieval, and there is no error message.
Several open models were trained with instruction prefixes, and they expect you to use them. Feed a query in unprefixed and quality quietly drops.
# E5 family — different prefix for queries and documentsdocument_vector = model.encode("passage: The spare set is in the kitchen drawer.")query_vector = model.encode("query: where did I put my keys?")
# Nomic — same idea, different wordsdocument_vector = model.encode("search_document: The spare set is in the kitchen drawer.")query_vector = model.encode("search_query: where did I put my keys?")
# BGE-M3 and the OpenAI models — no prefixes, just pass the textdocument_vector = model.encode("The spare set is in the kitchen drawer.")Note the asymmetry: documents and queries get different prefixes. That’s the point — the model learned that a short question and a long passage play different roles, and the prefix tells it which is which.
Shrinking vectors
Section titled “Shrinking vectors”Several models now support Matryoshka dimension reduction: they’re trained so the most important information sits at the front of the vector, letting you truncate with less loss than you’d expect.
Supported by OpenAI’s -3 models, Cohere Embed v4, Voyage, Gemini and nomic-embed-text-v1.5.
response = client.embeddings.create( model="text-embedding-3-large", input=texts, dimensions=256, # instead of 3072)That’s a twelvefold storage reduction, and truncated -3-large still beats some full-size older models. Whether it beats your current setup is an empirical question — measure it. But measure it before you provision a bigger machine, not after.
What I’d actually do
Section titled “What I’d actually do”Starting out, hosted: text-embedding-3-small. Cheap, fast, well documented, good SDKs in every language on this site. Upgrade when you have a measurement showing the model is your bottleneck, which for most projects never happens.
Starting out, self-hosted: all-MiniLM-L6-v2 to get working, then BGE-M3 when you’re serious. BGE-M3’s hybrid output is worth more in practice than a couple of benchmark points.
Multilingual: BGE-M3 or Cohere Embed v4. Don’t try to make an English-first model work across languages.
Images alongside text: Cohere Embed v4, or CLIP if you’d rather self-host.
Long or specialised documents (legal, finance, code): try Voyage — that’s where it consistently reports its biggest margins.
And remember it’s a one-way door
Section titled “And remember it’s a one-way door”Whichever you pick, changing your mind means re-embedding everything — vectors from different models are not comparable, and mixing them degrades results with no error message. Plan it as a migration, and keep the source text so you can.
References
Section titled “References”- MTEB leaderboard — live cross-provider rankings
- OpenAI — Embeddings guide — models, dimensions and the
dimensionsparameter - Cohere — Embed v4 announcement and embeddings docs
- Voyage AI — text embeddings — model list, dimensions and quantization options
- Google — Gemini Embedding model — note the single-input and 2,048-token limits
- BGE-M3 · multilingual-e5-large · nomic-embed-text-v1.5 · all-MiniLM-L6-v2 — model cards, including the required prefixes
- Matryoshka Representation Learning — why truncating these vectors works
- BEIR — the benchmark suite most of these scores come from