Swapping your LLM edits a prompt. Swapping your embedding model re-embeds the corpus, rebuilds the index and invalidates every retrieval number you have ever measured — vectors from two models sit in different spaces and cannot be compared, so there is no gradual migration and no A/B test cheaper than the migration itself. This is the one component in a retrieval stack you choose under lock-in, which is why the deciding numbers are bytes per vector and who controls the model's lifecycle. Leaderboard rank is the least useful input on the table.
At a glance
Four models that cover the four postures actually available in 2026: the incumbent everyone already calls, the multimodal one, the one that optimises the storage bill, and the one you can put on your own disk and never lose.
| Model | Access | Default vector | List price |
|---|---|---|---|
| OpenAI text-embedding-3-large | First-party API; Azure | 3072 dims, float32 — 12 KiB | $0.13 / 1M tokens |
| Cohere Embed v4 | Cohere API, Bedrock, Azure AI Foundry, SageMaker | 1536 dims, float32 — 6 KiB | $0.12 / 1M text tokens; $0.47 / 1M image tokens |
| voyage-3.5 | Voyage API; also via AWS Marketplace and Azure | 2048 dims, int8 available — 2 KiB | $0.06 / 1M tokens |
| Qwen3-Embedding-8B | Open weights, Apache-2.0 | up to 4096 dims, float32 — 16 KiB | Your GPU, your electricity |
Two of those cells deserve to be read twice. Cohere's 128k input is a different order of magnitude from OpenAI's roughly 8k, and it changes what a "chunk" is allowed to be. Qwen3-Embedding-8B ranked first on the MTEB multilingual leaderboard when Qwen published it in June 2025, at 70.58 — the only entry here whose weights you can pin, mirror and still be running unchanged in 2031.
The choice you make once
Two models, two spaces, no bridge
An embedding is a coordinate in a space that one particular model invented during training. Cosine similarity between a vector from model A and a vector from model B is not a weak signal; it is a category error, in the same way that a latitude compared against a Fahrenheit reading is. There is no projection you can learn cheaply that fixes this, no dual-write period where both models serve the same index, and no way to migrate ten percent of the corpus and measure. The unit of change is the whole corpus.
Every other component in the stack degrades gracefully by comparison. A generation model can be swapped behind a router and evaluated on live traffic. A reranker sits on top of retrieval and can be turned off. A vector database can be dual-written and cut over per collection. The embedding model is the only one where the migration is atomic and the rollback is "did you keep the old index".
The bill is not the problem
People price this migration as an API charge, which is the small part:
# 20M chunks at ~400 tokens each tokens = 20_000_000 * 400 # 8.0e9 # The embed pass, at list price openai_3_large = tokens / 1e6 * 0.13 # $1,040 voyage_3_5 = tokens / 1e6 * 0.06 # $ 480 # The part that is not the bill: # both indexes resident until cutover -> 2x vector storage # every retrieval baseline -> re-measured from zero # top-k, reranker cut-off, hybrid mix -> re-tuned per index # rollback -> only if you kept index A
A thousand dollars is nothing. Six weeks of an engineer re-tuning top-k and reranker thresholds, re-labelling a golden set because the old relevance judgements were made against different neighbours, and defending a change that moves some queries backwards is the actual price. Teams do not avoid re-embedding because it is expensive in dollars. They avoid it because it is expensive in confidence.
Which means the choice compounds in one direction
If the migration is this awkward, the rational move is to pick the model whose constraints you will not outgrow, not the one that is 1.5 points ahead this quarter. The constraints that bite later are input length (you will want bigger chunks), output width (you will want a smaller footprint), modality (a scanned PDF will arrive) and lifecycle (the model will be deprecated). Quality rank, the thing every comparison leads with, is the constraint that ages best on its own — a two-point retrieval gap closes when you fix chunking, and it never becomes a migration.
The four, and what each is actually for
OpenAI text-embedding-3-large — the default that stopped being the frontier
It is on every tutorial, in every framework's default configuration, and it is the model your team already has a key for. It is also the widest vector in this comparison at 3072 float32 dimensions, capped at roughly 8k tokens of input, text only, float only, and the most expensive per token at $0.13. Matryoshka training means you can ask for 1024 or 256 dimensions and cut storage proportionally with modest quality loss — which most teams never do, because the default works and nobody owns the vector-store line item.
Pick it when retrieval is a supporting feature rather than the product, your corpus is small enough that storage is a rounding error, and the value of "nobody has to evaluate anything" exceeds the value of the difference. That is a real and common situation. Just make it a decision rather than a default.
Cohere Embed v4 — the one that deletes a pipeline
Embed v4 takes text and images in the same request, interleaved, with a 128k-token input window, configurable output from 256 to 1536 dimensions, and int8, uint8 and binary output types. Text is $0.12 per million tokens; images are billed separately at $0.47 per million image tokens.
The reason to choose it is not a benchmark delta. It is that a slide, a chart, a scanned invoice or a screenshot can be embedded as what it is, rather than being pushed through an OCR-and-layout pipeline that you then have to own, monitor and re-run when the parser improves. If your corpus is genuinely visual, Embed v4 removes a whole subsystem from your architecture — and a subsystem removed beats two points of nDCG every time. If your corpus is plain text, you are paying twice the price of voyage-3.5 for capability you will never call.
voyage-3.5 — the one that reads the storage bill
voyage-3.5 supports 2048, 1024, 512 and 256 dimensions through Matryoshka learning, with float, int8, uint8, binary and ubinary output, a 32k input window, and the lowest list price here at $0.06 per million tokens. Voyage's own figure for the combination it recommends — int8 at 2048 dimensions — is an 83% reduction in vector-database cost against float at 3072 dimensions, at higher retrieval quality. The cost half of that claim is not a benchmark; it is division. 2048 bytes against 12,288 bytes is 83.3%, and you can verify it with a calculator.
The quality half is a vendor's number on a vendor's evaluation and should be treated as such. But the asymmetry is what matters: if the quality claim is merely not worse, the storage arithmetic alone decides the question for any corpus large enough to have a vector-store line item.
Qwen3-Embedding-8B — the one you can freeze
Apache-2.0 weights, up to 4096 dimensions, a 32k input window, 100-plus languages, and the top of the MTEB multilingual leaderboard at 70.58 when Qwen published it in June 2025. It is also 8 billion parameters you now have to serve, at 16 KiB per vector if you take the full width, on hardware you provision for an embedding workload whose shape is bursty by nature — quiet during serving, saturated during a re-index.
You do not pick this for the leaderboard position. You pick it for the one property nothing on the API side can offer: the model cannot be deprecated out from under you. A checkpoint on your own storage is the only version pin that actually holds, and for a corpus you intend to keep for a decade, that is a different kind of argument than a price per million tokens. The 0.6B and 4B members of the same family exist precisely so that this posture does not require an 8B serving budget.
Cross-cutting comparison
Storage is the axis that compounds
The embed pass is charged once; the vectors are stored forever, and on managed vector databases the price is a function of bytes resident and bytes searched. Across the four defaults, the same twenty-million-chunk corpus is 328 GB of raw vectors under Qwen3 at full width, 246 GB under OpenAI, 123 GB under Cohere and 41 GB under voyage-3.5 at int8. That difference does not appear on the model invoice at all — it appears on a different vendor's bill, which is exactly why it survives so many model comparisons unmentioned.
Three of the four give you a lever here and it is nearly free to pull: Matryoshka training means shorter vectors degrade gracefully rather than catastrophically, and int8 output typically costs a fraction of a point of recall for a four-fold reduction. The teams paying the most for storage are almost always the ones that never passed a dimensions or embedding_types argument, not the ones that picked the wrong model.
Quality differences are smaller than your chunking
Every one of these models will be within a few points of the others on a public retrieval benchmark, and the ranking will reorder between benchmarks, domains and languages. Meanwhile the gap between a naive 512-token fixed-width chunker and one that respects document structure is routinely larger than the gap between the best and worst model here — and the gap between no reranker and a reranker is larger still. If you have not yet tuned chunking, the model comparison is measuring your chunker.
This is also why MTEB rank is a bad primary input: it is a leaderboard that models are now trained with knowledge of, on tasks that are not yours, aggregated in a way that hides the domain you care about. Use it to build a shortlist of three. Decide between them on 200 queries from your own corpus with your own relevance judgements, which is a two-day job and the only measurement that transfers.
Multimodality is an architecture change wearing a benchmark's clothes
Only Cohere takes an image here. That reads like one row in a matrix and behaves like a fork in the pipeline. The text-only path requires a parsing stage — OCR, layout reconstruction, table extraction, figure captioning — that has its own failure modes, its own vendor, its own bill and its own upgrade treadmill, and that quietly determines your retrieval ceiling because nothing downstream can recover information the parser dropped. Embedding the page directly does not make that problem disappear so much as move it inside a model you do not have to operate.
The honest caveat: it also makes the failure opaque. When a parser mangles a table you can look at the parse. When a multimodal embedding misses the number in the corner of a chart, there is nothing to inspect. Choose the visual path when your documents are visual, not because it sounds more modern.
Lifecycle is the deprecation you cannot ride out
When a generation model is retired you change a string, re-run your evals and absorb some behaviour drift. When an embedding model is retired, you re-embed the corpus — the migration this whole post is about, on the provider's schedule rather than yours. Every API model here carries that exposure; the open-weight one does not, because a checkpoint on disk has no end-of-life date. That is the single strongest argument for open weights in the retrieval layer, and it has nothing to do with cost or privacy.
The mitigation, if you stay on an API — and most teams should — is to keep the ingredients of a re-embed permanently ready: the chunk text stored alongside the vectors rather than only in the source system, an embed job that can run end-to-end from cold, and a retrieval eval set that is version-controlled and re-runnable in an afternoon. Teams that hold those three can absorb a forced migration in a week. Teams that do not discover during the deprecation window that they cannot reproduce their own index.
When to pick which
| Situation | Pick | Because |
|---|---|---|
| Retrieval is a feature, corpus under a few million chunks | OpenAI text-embedding-3-large | The difference will not repay the evaluation time. Pass dimensions=1024 and move on. |
| Corpus is slides, scans, screenshots, charts | Cohere Embed v4 | Deletes the parsing subsystem rather than optimising it, and 128k input lets a chunk be a document. |
| Tens of millions of chunks and a vector-store bill you can see | voyage-3.5, int8 | Lowest price per token and a quarter of the bytes; the storage saving dwarfs the token saving. |
| Data residency, air-gap, or a corpus you must serve for a decade | Qwen3-Embedding-8B | The only option with no deprecation date. Start at 0.6B or 4B unless you have measured the need for 8B. |
| Heavily multilingual, especially non-Latin scripts | Qwen3 or Cohere, shortlisted; decide on your own queries | Multilingual rank moves most between benchmarks, so this is the case where public numbers transfer worst. |
| You have not tuned chunking yet | None of them — fix chunking first | Otherwise the comparison is measuring your chunker, and you will re-embed again afterwards anyway. |
FAQ
Can I migrate embedding models incrementally, one collection at a time?
You can migrate one index at a time if your corpus is partitioned into indexes that are never searched together. You cannot mix models within a single searchable space: similarity between vectors from different models is meaningless, so a partially migrated index returns nonsense for the fraction it has not caught up on.
Does a smaller vector always mean worse retrieval?
Not proportionally. Models trained with Matryoshka representation learning concentrate the most important information in the leading dimensions, so truncation degrades gently — and quantisation-aware training makes int8 output close to free in quality terms. Measure it on your own queries; the loss from 2048 float to 2048 int8 is usually far smaller than teams expect, and the storage saving is exactly four-fold.
Is MTEB rank useless?
It is useful for exclusion and misleading for selection. A model far down the table is unlikely to surprise you; the ordering of the top ten does not predict which one wins on your corpus. Use it to pick three candidates, then spend two days building 200 labelled queries from your own data — that measurement is the one that survives contact with production.
What actually happens when an API embedding model is deprecated?
You get a notice window, and then you re-embed everything before it closes. There is no version pin that helps, because the old vectors stay valid only as long as the endpoint that produced compatible ones stays up. Keep chunk text stored next to your vectors and keep your embed job runnable from cold, or the window will find you unable to rebuild your own index.
Should I self-host embeddings to save money?
Rarely for cost alone. At $0.06 per million tokens an eight-billion-token corpus embeds for a few hundred dollars, which does not pay for a GPU. Self-host for lifecycle control, data residency or a genuinely enormous re-embed cadence — and if it is only cost, a 0.6B model on a CPU box will already be cheap enough that the 8B one is irrelevant.
Further reading
On this wiki:
- Embeddings: Meaning as Geometry — why "similar becomes close" is the whole mechanism, and what that implies about mixing spaces.
- Chunking & vector search intuition — the stage that usually explains a retrieval gap people blame on the model.
- Choosing a Vector Database — the bill the bytes-per-vector chart actually lands on.
- Hybrid Search & Reranking — the two levers that move recall more than a model swap.
- Model Deprecation & Migration — the operational drill this post argues you should be ready to run.
- Local Knowledge Bases — the three independent dials behind the word "local", one of which is the embedding step.
Sources:
- voyage-3.5 and voyage-3.5-lite — dimensions, quantisation types and the 83% storage claim.
- Announcing Embed Multimodal v4 and Cohere embeddings documentation — input window, output dimensions and
embedding_types. - New embedding models and API updates — the
dimensionsparameter on text-embedding-3. - Qwen3 Embedding — the MTEB multilingual result and the 0.6B / 4B / 8B family.