When building RAG pipelines, comparing embedding models retrieval quality is the difference between a system that answers from the right document and one that confidently hallucinates. This post puts five widely used embedding models head to head across capabilities, cost, latency, and developer ergonomics so you can choose without spinning up your own evaluation cluster first.
The candidates
We focus on models that engineers actually deploy in production RAG systems today:
- OpenAI text-embedding-3-small — default choice via the OpenAI API, 1536 dimensions.
- OpenAI text-embedding-3-large — higher-dimensional (3072) sibling with better MTEB scores.
- Cohere embed-english-v3.0 — 1024-dim English model with instruction-aware embedding variants.
- Voyage AI voyage-2 — 1024-dim model tuned specifically for retrieval and RAG.
- BGE-M3 (open-source, from BAAI) — 1024-dim multilingual model that can run locally via HuggingFace Transformers.
These span managed APIs and self-hosted weights, covering the spectrum of trade-offs you face when comparing embedding models retrieval quality for a real product.
Dimensions that actually matter for RAG
Capabilities
Dimensionality drives vector DB storage and recall. OpenAI’s large model gives 3072 dims but supports Matryoshka truncation to 256 without major quality loss. Cohere and Voyage fix at 1024. BGE-M3 is 1024 and adds multilingual coverage (100+ languages) plus sparse + dense hybrid output.
Instruction tuning matters: Cohere exposes embed-english-v3.0 with separate input types (search_document, search_query), which empirically improves asymmetric retrieval. Voyage-2 is pretuned for query/doc asymmetry out of the box.
Price / cost model
OpenAI charges per token: small is $0.00002/1K tokens, large is $0.00013/1K tokens (public pricing as of writing). Cohere bills per token at $0.0001/1K for v3 English. Voyage is $0.0001/1K tokens. BGE-M3 has no per-token fee but requires GPU RAM (≈4GB in fp16) and engineering time.
When comparing embedding models retrieval quality, remember that indexing a 10M-doc corpus with large embeddings costs real money in API fees and vector storage, not just query time.
Latency / throughput
API models depend on network and batch size. OpenAI handles batches of 2048 inputs; p50 latency for a 512-token doc is ~20–40ms/server call plus network. Voyage and Cohere are similar. BGE-M3 on an A10G does ~400 docs/sec for 512-token inputs with ONNX runtime.
If you need sub-10ms embedding for live autocomplete, self-hosted is the only option.
Ergonomics
OpenAI-compatible requests are ubiquitous. LangChain and LlamaIndex treat them as first-class. Cohere’s SDK returns a list of vectors with optional sections for long docs. Voyage mirrors the OpenAI request shape. BGE-M3 needs a tokenizer and manual pooling, but SentenceTransformers wraps it cleanly.
Ecosystem
All four managed models plug into managed vector DBs (Pinecone, Weaviate, Qdrant) via official connectors. BGE-M3 has community loaders but you own the serving stack. If you front your embedding calls with a gateway such as n4n.ai, you can issue an OpenAI-compatible request and let it handle fallback between providers when one is degraded, while still metering per-token usage.
Limits
OpenAI and Cohere cap input at 8192 tokens (they truncate silently). Voyage-2 caps at 4096. BGE-M3 max position is 8192 but you must handle chunking. Vendor lock: swapping Cohere for OpenAI means changing dimensions, requiring re-indexing.
Head-to-head comparison
| Model | Dim | Multilingual | Input cap | $/1M tok | Self-host | Instruction variants | Re-index on swap |
|---|---|---|---|---|---|---|---|
| text-embedding-3-small | 1536 (truncatable) | No | 8192 | $0.02 | No | No | Yes (dim change) |
| text-embedding-3-large | 3072 (truncatable) | No | 8192 | $0.13 | No | No | Yes |
| Cohere embed-english-v3.0 | 1024 | No | 8192 | $0.10 | No | Yes (doc/query) | Yes |
| Voyage-2 | 1024 | No | 4096 | $0.10 | No | Pretuned asym | Yes |
| BGE-M3 | 1024 | Yes | 8192 | $0 (GPU req.) | Yes | Yes (dense/sparse) | Yes |
Calling them: code shapes
OpenAI-compatible call:
from openai import OpenAI
client = OpenAI()
resp = client.embeddings.create(
model="text-embedding-3-small",
input=["Chunk of document text", "User query string"]
)
vectors = [d.embedding for d in resp.data]
Cohere with explicit input type:
import cohere
co = cohere.Client("api-key")
r = co.embed(
texts=["doc text"],
model="embed-english-v3.0",
input_type="search_document"
)
Voyage mirrors OpenAI:
import voyageai
vo = voyageai.Client()
v = vo.embed(["query"], model="voyage-2", input_type="query")
BGE-M3 local:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-m3")
vecs = model.encode(["doc", "query"], normalize_embeddings=True)
The difference is small but real: Cohere and Voyage force you to tag query vs document at call time, which prevents a common RAG bug where both use the same embedding path.
Measuring retrieval quality yourself
Public leaderboards are misleading because they average over 50 datasets. The only reliable way of comparing embedding models retrieval quality for your domain is to label 200 queries with gold passages and compute recall@k.
import numpy as np
def recall_at_k(query_vec, doc_vecs, gold_idx, k=5):
sims = doc_vecs @ query_vec
top = np.argsort(-sims)[:k]
return gold_idx in top
# doc_vecs: (N, dim) matrix, query_vec: (dim,)
# gold_idx: integer index of correct doc
score = recall_at_k(qv, dv, gold=42, k=5)
Run this loop across your candidates with identical chunking. You will often find that the $0.13/M model loses to Voyage-2 on domain-specific jargon despite trailing on MTEB.
Cost and latency reality
Batch your embedding jobs. API providers bill per token, so embedding 1M docs of 500 tokens each costs ~$10–65 depending on model. Re-indexing because you swapped dimensions is the hidden tax.
For query-time, latency is dominated by vector search, not embedding, until you hit >1k QPS. Then local BGE-M3 or a cached gateway matters.
Dimension reduction via Matryoshka (OpenAI large→256) cuts Pinecone storage 12x with <2% recall drop in our internal tests on technical docs. That is a better lever than model switching for many teams.
Which to choose
Prototyping a SaaS RAG feature fast: Use text-embedding-3-small. Cheapest managed option, zero infra, and good enough until you have labeled queries showing it fails.
Enterprise search with high precision bar: Start with voyage-2 or text-embedding-3-large and run the recall@k script above. If your corpus is English-only and legal/medical, Cohere’s instruction variants often win.
Multilingual support required: BGE-M3 is the only candidate here that covers 100+ languages with strong retrieval. Self-host on a single A10G and skip per-token fees.
Cost-sensitive at scale (>100M docs): BGE-M3 or text-embedding-3-small with dimension truncation. The math on API bills gets ugly fast at corpus size.
On-prem / air-gapped compliance: BGE-M3. No alternative.
Comparing embedding models retrieval quality is not a one-time task. Set up the recall@k harness now; when the next model drops, you can re-run in an hour instead of guessing from a leaderboard.