n4nAI

Comparing retrieval quality across embedding models

A head-to-head comparison of five leading embedding models for RAG, covering retrieval quality, cost, latency, and ergonomics to guide your selection.

n4n Team4 min read928 words

Audio narration

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

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.

Tagsragembeddingscomparisonretrieval

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 rag pipeline observability posts →