Choose Embedding Model 2026: 10 Models Compared on MTEB, Cost, and Speed

TL;DR — Choose embedding model 2026 based on MTEB score, cost, context window, and deployment model. PECollective: "NV-Embed-v2 topped MTEB. BGE-M3 is the open-source production standard." AILog: "Cohere embed-v4: 65.2 MTEB. OpenAI text-3-large: 64.6. BGE-M3: 63.0. Startup/MVP: MiniLM (free, fast)." Milvus: "Prototypes start with text-embedding-3-small. Production outgrows it — images, PDFs, multilingual need more." Webscraft: "OpenAI works well with English, less so with Cyrillic. Cohere supports 100+ languages. MiniLM for short snippets, Jina v5 for entire documents." Best overall: Cohere embed-v4 (65.2 MTEB, multimodal, 100+ languages). Best open-source: BGE-M3 (63.0, self-hosted, free). Best price/performance: text-embedding-3-small ($0.02/1M, 62.3 MTEB). Learn more with what are embeddings, cosine vs dot product, what is RAG, and chunk size.

Milvus frames the progression: "Most RAG prototypes start with OpenAI's text-embedding-3-small. It's cheap, easy to integrate, and for English text retrieval it works well enough. But production RAG outgrows it fast. Your pipeline picks up images, PDFs, multilingual documents — and a text-only embedding model stops being enough."

PECollective summarizes the landscape: "NV-Embed-v2 from NVIDIA Research topped MTEB leaderboards across multiple task categories. BGE-M3 continued maturity as the open-source production standard. Top embedding models ranked by MTEB scores: OpenAI text-embedding-3-large, Cohere embed-v4, and open source options compared on quality and cost."

Embedding Model Decision Architecture

flowchart TD Start["Choose Embedding Model"] --> Q1{"Data sovereignty
required?"} Q1 -->|"Yes, self-hosted"| Q2{"Multilingual
needed?"} Q1 -->|"No, API OK"| Q3{"Budget
constraints?"} Q2 -->|"Yes"| BGE_M3["BGE-M3
63.0 MTEB, free
100+ languages, 8K context"] Q2 -->|"No, English only"| BGE_Base["BGE-base-en-v1.5
61.0 MTEB, free
512 context"] Q3 -->|"Low, prototype"| MiniLM["all-MiniLM-L6-v2
56.8 MTEB, free
384d, 256 context"] Q3 -->|"Medium"| Q4{"Multilingual or
multimodal needed?"} Q3 -->|"High, max quality"| Cohere["Cohere embed-v4
65.2 MTEB, $0.12/1M
100+ languages, multimodal"] Q4 -->|"Yes"| Q5{"Best multilingual?"} Q4 -->|"No, English only"| OpenAI_S["text-embedding-3-small
62.3 MTEB, $0.02/1M
1536d, 8K context"] Q5 -->|"100+ languages"| Cohere Q5 -->|"Good enough"| OpenAI_L["text-embedding-3-large
64.6 MTEB, $0.13/1M
3072d, 8K context"] BGE_M3 --> Eval["Evaluate on your data
MTEB is an average"] BGE_Base --> Eval MiniLM --> Eval OpenAI_S --> Eval OpenAI_L --> Eval Cohere --> Eval Eval --> Deploy["Deploy + Monitor
recall@k, precision@k"]

10 Embedding Models Comparison

Model MTEB Dimensions Context Price/1M Deployment Multilingual
Cohere embed-v4 65.2 1024 8K $0.12 API 100+ langs
OpenAI text-3-large 64.6 3072 8K $0.13 API Good
NV-Embed-v2 64.4 4096 8K Self-hosted English
Voyage-3-large 64.0 1024 32K $0.12 API Good
Jina embeddings v3 63.5 1024 32K $0.02 API/Self Good
BGE-M3 63.0 1024 8K Free Self-hosted 100+ langs
OpenAI text-3-small 62.3 1536 8K $0.02 API Good
nomic-embed-text-v1.5 61.5 768 8K Free Self-hosted English
BGE-base-en-v1.5 61.0 768 512 Free Self-hosted English
all-MiniLM-L6-v2 56.8 384 256 Free Self-hosted 50+ langs

Embedding Model Selection by Use Case

Use Case Recommended Model Why
Prototype/MVP all-MiniLM-L6-v2 Free, fast, 384d, good enough to start
English RAG production text-embedding-3-small $0.02/1M, 62.3 MTEB, 1536d
Multilingual RAG Cohere embed-v4 65.2 MTEB, 100+ languages, multimodal
Self-hosted production BGE-M3 63.0 MTEB, free, 100+ languages, 8K context
Maximum quality Cohere embed-v4 Highest MTEB (65.2), multimodal
Long documents Jina v3 / Voyage-3 32K context, embed without splitting
Budget constrained text-embedding-3-small $0.02/1M, good quality
Compliance/air-gapped BGE-M3 Self-hosted, no data leaves premises
Multimodal (text + images) Cohere embed-v4 Native multimodal support
Short snippets MiniLM 256 context, 384d, fast

Implementation

from dataclasses import dataclass
from typing import Optional
from enum import Enum

class DeploymentModel(Enum):
    API = "api"
    SELF_HOSTED = "self_hosted"
    HYBRID = "hybrid"

@dataclass
class EmbeddingModelSelector:
    """Choose the best embedding model for your use case."""

    MODELS = {
        "cohere-embed-v4": {
            "mteb": 65.2, "dimensions": 1024, "context": 8192,
            "price_per_1m": 0.12, "deployment": DeploymentModel.API,
            "multilingual": True, "multimodal": True,
            "languages": 100,
        },
        "openai-text-3-large": {
            "mteb": 64.6, "dimensions": 3072, "context": 8191,
            "price_per_1m": 0.13, "deployment": DeploymentModel.API,
            "multilingual": True, "multimodal": False,
            "languages": 50,
        },
        "voyage-3-large": {
            "mteb": 64.0, "dimensions": 1024, "context": 32000,
            "price_per_1m": 0.12, "deployment": DeploymentModel.API,
            "multilingual": True, "multimodal": False,
            "languages": 50,
        },
        "jina-v3": {
            "mteb": 63.5, "dimensions": 1024, "context": 32000,
            "price_per_1m": 0.02, "deployment": DeploymentModel.HYBRID,
            "multilingual": True, "multimodal": False,
            "languages": 89,
        },
        "bge-m3": {
            "mteb": 63.0, "dimensions": 1024, "context": 8192,
            "price_per_1m": 0.0, "deployment": DeploymentModel.SELF_HOSTED,
            "multilingual": True, "multimodal": False,
            "languages": 100,
        },
        "openai-text-3-small": {
            "mteb": 62.3, "dimensions": 1536, "context": 8191,
            "price_per_1m": 0.02, "deployment": DeploymentModel.API,
            "multilingual": True, "multimodal": False,
            "languages": 50,
        },
        "nomic-embed-text-v1.5": {
            "mteb": 61.5, "dimensions": 768, "context": 8192,
            "price_per_1m": 0.0, "deployment": DeploymentModel.SELF_HOSTED,
            "multilingual": False, "multimodal": False,
            "languages": 1,
        },
        "bge-base-en-v1.5": {
            "mteb": 61.0, "dimensions": 768, "context": 512,
            "price_per_1m": 0.0, "deployment": DeploymentModel.SELF_HOSTED,
            "multilingual": False, "multimodal": False,
            "languages": 1,
        },
        "all-minilm-l6-v2": {
            "mteb": 56.8, "dimensions": 384, "context": 256,
            "price_per_1m": 0.0, "deployment": DeploymentModel.SELF_HOSTED,
            "multilingual": True, "multimodal": False,
            "languages": 50,
        },
    }

    def select(self, requirements: dict) -> dict:
        """Select best embedding model based on requirements."""
        scored = []

        for name, specs in self.MODELS.items():
            score = 0
            reasons = []

            # Data sovereignty
            if requirements.get("self_hosted", False):
                if specs["deployment"] == DeploymentModel.SELF_HOSTED:
                    score += 3
                    reasons.append("Self-hosted (data sovereignty)")
                elif specs["deployment"] == DeploymentModel.HYBRID:
                    score += 1
                    reasons.append("Hybrid deployment available")

            # Multilingual
            if requirements.get("multilingual", False):
                if specs["multilingual"]:
                    score += 2
                    if specs["languages"] >= 100:
                        score += 1
                        reasons.append(
                            f"{specs['languages']}+ languages")

            # Multimodal
            if requirements.get("multimodal", False):
                if specs["multimodal"]:
                    score += 3
                    reasons.append("Multimodal (text + images)")

            # Budget
            max_price = requirements.get("max_price_per_1m", 0.15)
            if specs["price_per_1m"] <= max_price:
                score += 1
                if specs["price_per_1m"] == 0:
                    score += 1
                    reasons.append("Free (self-hosted)")
                elif specs["price_per_1m"] <= 0.02:
                    reasons.append(f"${specs['price_per_1m']}/1M (low cost)")

            # Context window
            min_context = requirements.get("min_context", 512)
            if specs["context"] >= min_context:
                score += 1
                if specs["context"] >= 32000:
                    score += 1
                    reasons.append("32K context (long documents)")

            # MTEB quality
            min_mteb = requirements.get("min_mteb", 55)
            if specs["mteb"] >= min_mteb:
                score += 1
                if specs["mteb"] >= 64:
                    score += 1
                    reasons.append(f"High MTEB ({specs['mteb']})")

            # Dimensions (storage budget)
            max_dim = requirements.get("max_dimensions", 4096)
            if specs["dimensions"] <= max_dim:
                score += 1

            scored.append({
                "model": name,
                "score": score,
                "mteb": specs["mteb"],
                "dimensions": specs["dimensions"],
                "context": specs["context"],
                "price_per_1m": specs["price_per_1m"],
                "deployment": specs["deployment"].value,
                "multilingual": specs["multilingual"],
                "multimodal": specs["multimodal"],
                "reasons": reasons,
            })

        scored.sort(key=lambda x: x["score"], reverse=True)
        best = scored[0]

        return {
            "recommended": best["model"],
            "score": best["score"],
            "mteb": best["mteb"],
            "dimensions": best["dimensions"],
            "context": best["context"],
            "price_per_1m": best["price_per_1m"],
            "deployment": best["deployment"],
            "reasons": best["reasons"],
            "all_scored": scored[:5],
        }

    def estimate_cost(self, model: str,
                      monthly_tokens: int) -> dict:
        """Estimate monthly cost for an embedding model."""
        specs = self.MODELS.get(model)
        if not specs:
            return {"error": f"Unknown model: {model}"}

        api_cost = (monthly_tokens / 1_000_000) * specs["price_per_1m"]

        # Self-hosted GPU cost (amortized)
        gpu_monthly = 0
        if specs["deployment"] == DeploymentModel.SELF_HOSTED:
            gpu_monthly = 400  # ~$400/month for GPU server

        total = api_cost + gpu_monthly

        return {
            "model": model,
            "monthly_tokens": monthly_tokens,
            "api_cost_monthly": round(api_cost, 2),
            "gpu_cost_monthly": gpu_monthly,
            "total_monthly": round(total, 2),
            "cost_per_1m_tokens": round(
                total / (monthly_tokens / 1_000_000), 4
            ) if monthly_tokens > 0 else 0,
            "breakdown": (
                f"API: ${api_cost:.2f}/month + "
                f"GPU: ${gpu_monthly}/month = ${total:.2f}/month"
            ),
        }

Choose Embedding Model Checklist

  • [ ] Start with text-embedding-3-small for prototyping ($0.02/1M, 62.3 MTEB)
  • [ ] Evaluate if you need to upgrade based on retrieval quality on your data
  • [ ] Check MTEB score — higher is better, but test on your domain
  • [ ] MTEB is an average — a 63.0 model might beat 65.2 on your specific data
  • [ ] Consider data sovereignty — self-hosted if data cannot leave premises
  • [ ] Self-hosted: BGE-M3 (63.0 MTEB, free, 100+ languages, 8K context)
  • [ ] Self-hosted: nomic-embed-text (61.5 MTEB, free, 8K context)
  • [ ] Self-hosted: BGE-base-en (61.0 MTEB, free, English only)
  • [ ] Self-hosted: MiniLM (56.8 MTEB, free, 384d, fast for prototyping)
  • [ ] API: Cohere embed-v4 (65.2 MTEB, $0.12/1M, 100+ languages, multimodal)
  • [ ] API: OpenAI text-3-large (64.6 MTEB, $0.13/1M, 3072d)
  • [ ] API: OpenAI text-3-small (62.3 MTEB, $0.02/1M, 1536d)
  • [ ] API: Voyage-3 (64.0 MTEB, $0.12/1M, 32K context)
  • [ ] API: Jina v3 (63.5 MTEB, $0.02/1M, 32K context, hybrid deployment)
  • [ ] Check multilingual support — Cohere and BGE-M3 support 100+ languages
  • [ ] OpenAI works well with English, less so with Cyrillic and Asian languages
  • [ ] Check multimodal support — Cohere embed-v4 supports text + images
  • [ ] Check context window — match to your chunk size
  • [ ] MiniLM: 256 context (short snippets only)
  • [ ] BGE-base: 512 context (standard chunks)
  • [ ] text-embedding-3: 8K context (long chunks)
  • [ ] Jina v3 / Voyage-3: 32K context (entire documents without splitting)
  • [ ] Check dimensions — more dimensions = more storage and slower search
  • [ ] 384d: 1.5KB/vector (MiniLM — prototyping)
  • [ ] 768d: 3KB/vector (BGE, nomic — balanced)
  • [ ] 1024d: 4KB/vector (Cohere, BGE-M3, Voyage — production)
  • [ ] 1536d: 6KB/vector (text-3-small — production default)
  • [ ] 3072d: 12KB/vector (text-3-large — maximum distinction)
  • [ ] Calculate monthly cost: tokens/1M × price + GPU cost if self-hosted
  • [ ] Self-hosted crossover at ~500K-1M monthly requests (GPU amortization)
  • [ ] Estimate storage: vectors × dimensions × 4 bytes
  • [ ] 1M vectors at 1536d = 6GB storage
  • [ ] 1M vectors at 3072d = 12GB storage
  • [ ] Use the same embedding model for indexing and retrieval
  • [ ] Track embedding model version for reindexing
  • [ ] Plan reindexing strategy when switching models
  • [ ] Benchmark 2-3 models on your actual documents and queries
  • [ ] Measure recall@k and precision@k for each model
  • [ ] Measure RAGAS faithfulness with each model
  • [ ] Consider model latency — API round-trip vs local inference
  • [ ] Consider model availability — API uptime vs self-hosted reliability
  • [ ] Consider vendor lock-in — open-source models avoid this
  • [ ] Read what are embeddings for fundamentals
  • [ ] Understand cosine vs dot product for search
  • [ ] Match chunk size to embedding model context window
  • [ ] Use embeddings in RAG pipelines
  • [ ] Build RAG from scratch with chosen model
  • [ ] Add hybrid search with your embeddings
  • [ ] Add reranking after embedding-based retrieval
  • [ ] Apply chunking strategies before embedding
  • [ ] Evaluate with RAG metrics
  • [ ] Test: selected model produces expected retrieval quality
  • [ ] Test: cost estimate matches actual usage
  • [ ] Test: context window accommodates your chunk size
  • [ ] Test: multilingual support covers your languages
  • [ ] Document selected model, MTEB score, cost, and benchmark results

FAQ

What is the best embedding model in 2026?

The best embedding model depends on your use case. PECollective: "NV-Embed-v2 from NVIDIA topped MTEB leaderboards across multiple task categories. BGE-M3 continued maturity as the open-source production standard." AILog: "Cohere embed-v4: 65.2 MTEB. OpenAI text-3-large: 64.6. BGE-M3: 63.0. Startup/MVP: all-MiniLM-L6-v2 (free, fast) or Jina v5-nano." Milvus: "Most RAG prototypes start with OpenAI text-embedding-3-small. It is cheap, easy to integrate, and for English text retrieval it works well enough. But production RAG outgrows it fast. Your pipeline picks up images, PDFs, multilingual documents — and a text-only embedding model stops being enough." Decision: (1) Best overall: Cohere embed-v4 (65.2 MTEB, 100+ languages, multimodal). (2) Best open-source: BGE-M3 (63.0 MTEB, self-hosted, free). (3) Best price/performance: text-embedding-3-small (cheap, 1536d, good enough for most). (4) Best for prototyping: all-MiniLM-L6-v2 (free, fast, 384d).

How do MTEB scores compare across embedding models?

MTEB (Massive Text Embedding Benchmark) scores measure retrieval quality across multiple tasks. PECollective: "MTEB scores are from the public leaderboard (English retrieval subset). Dimensions determine how much storage each vector requires." AILog: Top MTEB scores: (1) Cohere embed-v4: 65.2. (2) OpenAI text-embedding-3-large: 64.6. (3) NV-Embed-v2: 64.4. (4) Voyage-3-large: 64.0. (5) Jina embeddings v3: 63.5. (6) BGE-M3: 63.0. (7) OpenAI text-embedding-3-small: 62.3. (8) nomic-embed-text-v1.5: 61.5. (9) BGE-base-en-v1.5: 61.0. (10) all-MiniLM-L6-v2: 56.8. Higher MTEB = better retrieval quality. But MTEB is an average — always test on your specific data. A model with 63.0 MTEB might outperform one with 65.2 on your domain.

Should you use OpenAI or open-source embedding models?

Choose based on data sovereignty, cost, and quality needs. Milvus: "Most RAG prototypes start with OpenAI text-embedding-3-small. But production RAG outgrows it fast — your pipeline picks up images, PDFs, multilingual documents." PECollective: "BGE-M3 continued maturity as the open-source production standard." OpenAI advantages: (1) No infrastructure to manage. (2) High quality (64.6 MTEB for large). (3) Easy API integration. (4) Pay per token. Open-source (BGE-M3, MiniLM) advantages: (1) Free — no API costs. (2) Self-hosted — data never leaves your infrastructure. (3) Full control over model and deployment. (4) No vendor lock-in. (5) Air-gapped deployment for compliance. Webscraft: "OpenAI works well with English, less so with Cyrillic. Cohere embed-v4 supports 100+ languages." Decision: prototype with OpenAI, move to BGE-M3 for production self-hosted, use Cohere for multilingual.

How much do embedding models cost?

Embedding model costs range from free (open-source) to $0.13 per 1M tokens (premium API). PECollective: "Spec table: OpenAI, Cohere, Voyage AI, Jina, and BGE compared by dimensions, price per 1M tokens, context window, and MTEB score." AILog: "Startup/MVP: all-MiniLM-L6-v2 (free, fast) or Jina v5-nano." Pricing per 1M tokens: OpenAI text-embedding-3-small: $0.02. OpenAI text-embedding-3-large: $0.13. Cohere embed-v4: $0.12. Voyage-3: $0.12. Jina v3: $0.02. BGE-M3: free (self-hosted). MiniLM: free (self-hosted). nomic-embed-text: free (self-hosted). For 10M tokens/month: OpenAI small = $200, OpenAI large = $1,300, BGE-M3 = $0 (self-hosted GPU cost only). Self-hosted becomes cheaper at scale — crossover at ~500K-1M monthly requests when including GPU amortization.

What context window do embedding models support?

Embedding model context windows range from 256 to 32K tokens, affecting how much text can be embedded in a single call. Webscraft: "256 (MiniLM) — only for short snippets. 8192 (nomic-embed-text) — sufficient for long articles. 32K (Jina v5) — for entire documents without splitting." Context windows: all-MiniLM-L6-v2: 256 tokens. BGE-base-en-v1.5: 512 tokens. text-embedding-3-small: 8191 tokens. text-embedding-3-large: 8191 tokens. Cohere embed-v4: 8192 tokens. Voyage-3: 32,000 tokens. Jina v5: 32,000 tokens. nomic-embed-text-v1.5: 8192 tokens. BGE-M3: 8192 tokens. Longer context windows allow embedding entire documents without chunking, but optimal embedding quality is typically at 256-512 tokens. Match context window to your chunk size — do not embed at the maximum if your chunks are 512 tokens.


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →