Choosing an Embedding Model: A Practical Benchmark

Photo by golanlevin on flickr
There is no single best model, it depends on your language, domain, and budget. A small hosted model with truncated dimensions works well for English-only, cost-sensitive projects, while a self-hosted multilingual model tends to perform better on mixed English and Indonesian corpora. The only reliable way to decide is to run a recall at k test against your own labeled query and document pairs.
MTEB aggregates scores across many task types, including classification and clustering, that may have little to do with retrieval quality for your app. A model can rank high overall while underperforming on the specific retrieval pattern RAG needs, and domain or language mismatch can flip the ranking entirely once you test on your own data.
Larger dimensions generally give a higher recall ceiling but cost more to store and query. Many modern models support Matryoshka-style truncation, letting you shrink the vector after generation while retaining most of the useful signal, so the right size is often smaller than the model's native dimension once cost and index size are factored in.
Not automatically. English retrieval scores on public leaderboards say almost nothing about Indonesian performance, since affixes and reduplication tokenize differently and many models are trained mostly on English and Chinese data. Multilingual benchmarks like MMTEB show the strongest multilingual models are not always the largest ones, so testing on real Indonesian queries is essential.
Yes, but it requires re-embedding the entire corpus, not just new documents, because vectors from different models are not comparable to each other. Budget the re-indexing time and cost as part of the decision up front rather than treating the choice of embedding model as easily reversible later.

Photo by golanlevin on flickr
Every retrieval-augmented generation project eventually hits the same fork in the road: which embedding model actually belongs in production. The MTEB leaderboard makes this look like a simple ranking problem, pick whichever model sits at the top and move on. In practice it is closer to a systems decision than a leaderboard lookup, because dimension count, token pricing, latency, and how well a model handles your specific language and domain all pull in different directions.
This post walks through the tradeoffs I actually weigh when choosing an embedding model for a client project, why aggregate benchmark scores are a starting point and not a verdict, how multilingual models behave differently on Indonesian text than their English scores suggest, and a small recall at k harness you can run against your own labeled query and document pairs before committing to anything.
The Massive Text Embedding Benchmark aggregates scores across dozens of datasets and eight task categories, including classification, clustering, and retrieval. A model's overall MTEB score is an average across tasks that may have nothing to do with your use case. A model that excels at sentence similarity or clustering can still underperform on the specific kind of asymmetric query to long document retrieval that RAG actually needs.
Do not select an embedding model purely by scrolling to the top of a public leaderboard. A half point difference in an aggregate score can flip completely once you test the model against your own queries and your own documents.
Every embedding model publishes a native vector dimension, and most modern models let you truncate that vector to a smaller size without recomputing it, a technique commonly called Matryoshka representation. Smaller vectors mean cheaper storage, faster similarity search, and lower memory pressure on your vector index, but they can quietly cost you recall on harder queries. The table below is a rough comparison of a few models commonly used for RAG in 2026.
| Model | Native dimensions | Relative cost | Notes |
|---|---|---|---|
| text-embedding-3-small | 1536 | Low | Good default for English-heavy corpora on a budget |
| text-embedding-3-large | 3072 | Medium | Higher recall ceiling, supports dimension truncation |
| multilingual-e5-large-instruct | 1024 | Free, self-hosted | Strong multilingual retrieval, needs a GPU or CPU batch job |
| bge-m3 | 1024 | Free, self-hosted | Dense, sparse, and multi-vector retrieval in one model |
A useful rule of thumb: OpenAI's own documentation notes that a large embedding model truncated down to a much smaller size can still outperform an older, unshortened model at full size. That means the honest question is rarely which model has the biggest vector, it is which model retains the most useful signal at the dimension size your infrastructure can actually afford to index and query at scale.
If any part of your corpus is in Indonesian, English-language leaderboard rankings should be treated as noise, not signal. The Massive Multilingual Text Embedding Benchmark expanded coverage to hundreds of languages precisely because English-only evaluation was hiding large performance gaps. Its own published finding is a good sanity check on the assumption that bigger models always win: the strongest publicly available multilingual model in that evaluation had only a few hundred million parameters, well below many general-purpose embedding models.
If you serve both English and Indonesian users, do not assume one embedding model is best for both. Run the same recall evaluation twice, once per language, and only standardize on a single model if the drop in the weaker language is small enough to accept.
The only benchmark that reliably predicts production behavior is one built from your own queries and your own documents. You do not need thousands of labeled pairs to get a directionally useful signal, thirty to fifty realistic query and relevant document pairs pulled from real support tickets, real search logs, or a few hours of manual labeling is enough to separate a genuinely better model from a merely more popular one. The script below computes recall at k, the fraction of queries where the correct document appears in the top k retrieved results, for any embedding model that exposes a batch embedding call.
// Minimal recall@k harness against your own labeled pairs
import { OpenAI } from "openai"
interface EvalPair {
query: string
relevantDocId: string
}
const openai = new OpenAI()
async function embed(texts: string[], model: string) {
const res = await openai.embeddings.create({ model, input: texts })
return res.data.map((d) => d.embedding)
}
function cosineSim(a: number[], b: number[]) {
let dot = 0, magA = 0, magB = 0
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i]
magA += a[i] * a[i]
magB += b[i] * b[i]
}
return dot / (Math.sqrt(magA) * Math.sqrt(magB))
}
async function recallAtK(
pairs: EvalPair[],
corpus: { id: string; text: string }[],
model: string,
k = 5
) {
const corpusVecs = await embed(corpus.map((c) => c.text), model)
const queryVecs = await embed(pairs.map((p) => p.query), model)
let hits = 0
pairs.forEach((pair, i) => {
const scored = corpus.map((doc, j) => ({
id: doc.id,
score: cosineSim(queryVecs[i], corpusVecs[j]),
}))
scored.sort((a, b) => b.score - a.score)
const topK = scored.slice(0, k).map((s) => s.id)
if (topK.includes(pair.relevantDocId)) hits++
})
return hits / pairs.length
}Run this same script against every candidate model with the same query and document pairs, and you get a like-for-like comparison that accounts for your actual chunking strategy, your actual document length, and your actual vocabulary, none of which the public leaderboard can see. It typically takes less than an hour to run against three or four candidate models once the labeled pairs exist.
Once you have recall numbers from your own data, a few practical constraints usually narrow the field down to one or two realistic choices.
A smaller, cheaper model that you actually validated against your own data will beat a bigger, more expensive model you only trusted because of its leaderboard rank. Validation time is never wasted time on this decision.
For a purely English, general-purpose RAG project on a tight budget, a small hosted model with truncated dimensions is a sound default. For anything touching Indonesian content, a self-hosted multilingual model evaluated specifically on Indonesian query and document pairs consistently outperforms English-tuned hosted models in my own testing, even when the hosted model wins on the general leaderboard. Whichever you pick, treat the choice as reversible only if you have budgeted time to re-embed the entire corpus, because embeddings from different models are never comparable to each other.