IronStratum Get an API key

bge-m3: BAAI's multilingual embedding model as an API

bge-m3 is a multilingual embedding model from BAAI that turns text into 1024-dimension vectors. It reads inputs up to 8,192 tokens, covers more than 100 languages, and ships under MIT. RAG tutorials and vector-database guides keep reaching for it when the corpus is not English-only. On this platform it answers the embeddings route. One OpenAI-compatible POST with your texts. Back come dense vectors you can drop into any vector store. What follows is the exact request contract and the metering law. It also covers the three output types the model was trained for. That includes which of them a hosted route returns. The self-host routes are mapped too, with their documented potholes.

Last verified: 2026-09-25

What it is

An embedding model answers one question: how do I search text by meaning? It turns each text into a vector, so similar meanings land near each other. Search then becomes vector comparison rather than keyword matching. bge-m3, from the Beijing Academy of Artificial Intelligence, has been the default open-weight pick for multilingual corpora. Its release in early 2024 started that run. The official model card bills it as three things at once: multi-functional, multi-lingual, multi-granular. Those three words carry the whole spec.

  • Multi-functional. One pass through the model produces three retrieval outputs: a dense vector, sparse token weights, and multi-vectors. The OpenAI embeddings shape carries one vector per input, so hosted routes on that shape, including this one, serve the dense vector; the other two matter for hybrid search, explained below.
  • Multi-lingual. More than 100 working languages, trained on data covering up to 170+ languages per BAAI's documentation, with the honest caveat that coverage is unbalanced across languages.
  • Multi-granular. Inputs from short sentences to long documents up to 8,192 tokens, which is 16x the 512-token window of the older English-only BGE v1.5 models.

The numbers worth writing down, each with the source that owns it:

FactValueWhere the number comes from
Parametersabout 568MBGE documentation (2.27 GB fp32 weights in the repository)
Dense dimension1,024model card and model config
Input window8,192 tokensmodel card (config carries 8,194 position embeddings)
Languages100+ workingmodel card
LicenseMITmodel card
Base architectureXLM-RoBERTa-large, extended to 8,192 positionsBGE documentation
ReleasedJanuary 2024 (repository), February 2024 (technical report)Hugging Face repository, arXiv

Three outputs deserve their own paragraph, because pages conflict on what you actually get. The dense output is the familiar one: one normalized 1,024-dimension vector per text, compared by dot product. The sparse output is a learned keyword signal: the model gives each token present in the text a weight. A query matches a document through the weights of terms they share. It works the way BM25 does, but learned rather than counted. The multi-vector output keeps one vector per token. It scores pairs by token-to-token interaction, the ColBERT approach. That one is accurate and expensive to index. BAAI's documentation publishes the scoring math for all three. The OpenAI embeddings response shape carries one vector per input. A hosted route on that shape, including this one, serves the dense output. The sparse and multi-vector outputs come from the same model. They are consumed through local FlagEmbedding use or a hybrid-capable store. This is the part no aggregator row explains. bge-m3 is three retrieval outputs in one model. The embeddings API shape exposes one of them.

Within the BGE family the choice is generational. The v1 and v1.5 models are English or Chinese, 512 tokens. The English ones want a query instruction prepended. bge-m3 is multilingual, long-input, and needs no instruction. The card flags that as a deliberate change. When your corpus is English-only and short, the v1.5 English large model still does the job at 335M parameters. Smaller v1.5 sizes cost less again. When the corpus is mixed-language or longer than a paragraph, that is the case bge-m3 was built for.

This platform serves the model under the bge-m3 alias on its embeddings route. The lane answers calls today: one real embeddings request returned 200 on 2026-09-09, metering asserted against the billing ledger. The models index carries the current catalog. The embeddings category page introduces the route family.

Use cases

A mid-size bi-encoder, more than 100 languages, inputs to 8,192 tokens. That profile narrows the field to a short list of jobs:

  • Multilingual semantic search. The headline case. Mixed-language corpora, cross-lingual queries (search in English, hit documents in Japanese), and product catalogs whose content teams write in whatever language they write in.
  • RAG retrieval. The embedding stage of retrieval-augmented generation: embed chunks once, retrieve the top candidates per query. The model card's own pipeline recommendation for RAG is hybrid retrieval plus reranking: use the dense and sparse outputs together, then filter the shortlist with a reranker. It names Vespa and Milvus as stores that consume both.
  • Long-document embedding. 8,192 tokens is on the order of six thousand words of English in one vector, enough for full pages, legal clauses, or support tickets without chunking as aggressively. Long-document retrieval (MLDR, NarrativeQA) is one of the benchmark families in the card's evaluation.
  • Hybrid search without running two systems. The sparse output gives you BM25-style keyword matching from the same model that gives you vectors. That is the cheap way to survive vocabulary dense vectors miss: product codes, rare names, exact identifiers.
  • Clustering, dedup, classification. Any job that needs many texts compared by meaning at once. Batch requests carry an array of inputs, and the token metering sums the batch.

Local weights or a hosted route

MIT keeps the weights free to take home. For some teams running them locally is the correct call.

Keeping it local makes sense when hardware is already provisioned and the request rate is predictable. It also makes sense when you need the sparse or multi-vector outputs. Hybrid retrieval today means running FlagEmbedding yourself or a store that indexes both shapes. A plain embeddings endpoint will not do it. Fine-tuning on your own labeled pairs is a local job too. So is any corpus that legally or contractually cannot leave your infrastructure.

The hosted route is for teams that would rather not build the unglamorous half. That half is an endpoint with uptime, a ledger that agrees with every bill, and spending that halts at the wallet. There an emptied balance refuses the next request rather than billing it. Around a 568M model, the hosted value is not intelligence. It is operations.

The local routes that work, all official: BAAI's FlagEmbedding library with the BGEM3FlagModel class for all three outputs. Sentence-transformers and plain transformers work for dense vectors, as documented on the card. The Ollama library entry is a 1.2 GB download serving dense embeddings. GGUF builds exist for llama.cpp-based servers. One caution is worth carrying. Community reports document the same GGUF file producing vectors that differ in scale between two local runtimes. If you switch runtimes, re-embed the corpus rather than mixing vectors across the boundary.

API usage

Requests go to POST /v1/embeddings, authorized with a Bearer key issued by the console. The body carries model and input, and the accepted set also includes encoding_format and dimensions. Anything outside the accepted set fails with a 400 that names the offending key. An input sent as a token array instead of a string or string array is rejected the same way. Token-array shapes differ across embeddings APIs, and silently billing the wrong one helps nobody.

FieldRequiredMeaning
modelyesbge-m3
inputyesone string or an array of strings; an empty input is turned away with a 400 before any metering runs
encoding_formatnoresponse encoding hint, passed through
dimensionsnodimension control hint, passed through; the model's dense output is 1,024

The response is the model's native embeddings response, passed through verbatim. It carries a data array with one embedding per input, in input order, each a 1,024-float vector, plus a usage block. Three rules of this route save debugging time:

  1. Metering counts tokens, from the response's own usage. The count comes from the model's own usage.prompt_tokens. If that is absent the gateway falls back to total_tokens, then to the number of runes divided by four, rounded up, and which of the three was used is recorded in the ledger row. A request with 12 texts bills the sum of their tokens, not 12 units.
  2. Replays are built in. Send the same Idempotency-Key twice on this JSON route and the second attempt gets the stored answer; nothing executes again and nothing bills twice.
  3. A model answers only its own route. A chat alias called at /v1/embeddings is a plain 404, never a cross-family proxy, and bge-m3 called at a chat route is the same 404 in reverse.

curl

curl -X POST "https://api.ironstratum.com/v1/embeddings" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bge-m3",
    "input": [
      "BGE M3 is an embedding model supporting dense, sparse, and multi-vector retrieval.",
      "BM25 is a bag-of-words retrieval function that ranks documents by query term overlap."
    ],
    "encoding_format": "float"
  }'

python

import os
import requests

resp = requests.post(
    "https://api.ironstratum.com/v1/embeddings",
    headers={"Authorization": "Bearer " + os.environ["KEY"]},
    json={
        "model": "bge-m3",
        "input": [
            "BGE M3 is an embedding model supporting dense, sparse, and multi-vector retrieval.",
            "BM25 is a bag-of-words retrieval function that ranks documents by query term overlap.",
        ],
        "encoding_format": "float",
    },
    timeout=30,
)
resp.raise_for_status()

for item in resp.json()["data"]:
    print(item["index"], len(item["embedding"]))

openai-sdk

Embeddings is a native resource in the OpenAI SDK. If your code already calls OpenAI embeddings, the migration is a base-URL swap plus the model id. Nothing more.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.ironstratum.com/v1",
    api_key=os.environ["KEY"],
)

result = client.embeddings.create(
    model="bge-m3",
    input=[
        "BGE M3 is an embedding model supporting dense, sparse, and multi-vector retrieval.",
        "BM25 is a bag-of-words retrieval function that ranks documents by query term overlap.",
    ],
)

for item in result.data:
    print(item.index, len(item.embedding))

Benchmarks

Two kinds of evidence, both dated.

The hosted lane. One real embeddings request returned 200 through the live route on 2026-09-09 (task-17 E1). The usage row and price snapshot assert exactly against the billing ledger. The concurrency behavior was measured on the hosted lane during the 2026-08-28 bench window. The basis is task-16 E5b, the corrected true-concurrency grid:

MetricValueBasis
Embed latency p50, c=11.38 sE5b grid, single flight
Embed latency p50 / p95, c=81.90 s / 2.43 sE5b grid
Embed latency p50 / p95, c=324.33 s / 6.10 sE5b grid
Aggregate throughputabout 10x from c=1 to c=32 (36.9 to ~378 tokens/s, estimate)E5b; token rates are chars-per-four estimates
Concurrency ceiling8 in-flight requestsE5b seat ceiling, shipped as the monitor default
Single-flight first response, p95about 1.24 stask-17 E4 seat-monitor observation

The pattern those numbers establish: batching is positive in aggregate and negative per request. Aggregate throughput scales about 10x up to concurrency 32. The per-request median rises from 1.38 s to 4.33 s along the way. A bulk indexing job and a latency-sensitive query path therefore want different client concurrency settings. Read them as planning inputs from the two measurement windows, not as a promise of service levels.

One metering nuance came up in lane measurement. A 6-token request's cost rounds to zero at the ledger's six-decimal precision. No debit row lands while the usage row still records the request. Sub-resolution requests are effectively free by rounding. The ledger and the usage row never disagree.

The model on paper. Evaluation date early 2024. The sources are the official model card and the technical report. BAAI reports results on multilingual retrieval (MIRACL), cross-lingual retrieval (MKQA), and long-document retrieval (MLDR, NarrativeQA). The comparisons cover dense, sparse, multi-vector, and hybrid combinations, plus BM25. Those results are published as chart images rather than tables, and this page leaves the numbers where they are. The report's own summary claim is scoped to its February 2024 comparisons. It says the multi-output training led the multilingual, cross-lingual, and long-document benchmarks it tested. BAAI also quotes a community multilingual comparison. In it the model placed first against commercial and open peers. That is a 2024 result. This page presents it as history, not as a standing rank. Newer model families benchmark higher today. The FAQ covers that without borrowing their numbers.

Getting started

  1. Sign up on the console. Entry is an email signup plus a single invite code at the door while the beta runs. Once inside, the console is home base for the wallet and for every key you issue.
  2. Issue an API key. Give each project its own key, so when one leaks, the whole blast radius is that single key: switch it off and the other projects never notice.
  3. Check the rate. Because every request bills by its token count, one visit to the pricing page settles the budget question, and the prepaid wallet balance is what enforces it.
  4. Make the first call. Paste your key into the curl tab, embed the two example sentences, and confirm the vector length comes back 1,024. That single check proves the key, the route, and the model; the token count in the usage block is the metering leg. From here the work is your vector store, not the API.

For everything else in the catalog there is the models index. The embeddings page widens out to the whole route family. When you are ready for the second stage of the two-model pipeline this card recommends, the companion is bge-reranker-v2-m3.

What the platform serves

Specialty models — kind and unit price
ModelKindPrice
bge-m3embeddings$0.05 / 1M tokens

The same data GET /v1/models serves. A dash means the value isn't set.

Questions

What is bge-m3?
A multilingual embedding model from BAAI, the Beijing Academy of Artificial Intelligence. Released in early 2024 under the MIT license. It maps text to 1024-dimension dense vectors. It accepts inputs up to 8,192 tokens and works across more than 100 languages. The same model also produces sparse token weights and per-token multi-vectors. Those are the two other retrieval output types it was trained for. It is built on XLM-RoBERTa-large and is about 568M parameters. That is small enough to run on one consumer GPU.
How does BGE M3 work?
One encoder pass produces all three output types at once. The dense vector is the normalized hidden state of the first token, one 1024-dimension vector per text. Similarity is a dot product between two of them. The sparse output pushes each token's hidden state through a small learned layer and keeps only a weight for tokens present in the text. Scoring then works like keyword matching with learned weights, similar to BM25. The multi-vector output keeps one vector per token. It scores pairs by comparing token sets, the ColBERT approach. BAAI trained the three together with self-knowledge distillation. The agreement between the retrieval modes served as the teaching signal. The technical report highlights that as its novel training contribution.
What is the difference between bge-m3 and bge-reranker-v2-m3?
They are two models for two stages of the same retrieval pipeline. Their names collide often enough that the question shows up in Google's related questions for both. bge-m3 is an embedding model, a bi-encoder. Each text turns into its own vector. Search works by comparing those vectors across millions of documents cheaply. bge-reranker-v2-m3 is a cross-encoder reranker. The query and one candidate enter the model as a single pair. What comes out is a relevance judgment on that exact match-up. It judges more sharply than vector comparison. It pays for that with one scoring pass per pair. The standard pattern is to retrieve the top 100 with bge-m3. You then rescore those pairs with the reranker and keep the top few. Both are served on this platform, and the model card for bge-m3 recommends exactly this two-stage arrangement.
What is the bge-m3 embedding dimension, and can it be changed?
The dense vector is 1,024 dimensions, fixed by the model. The sparse output is a different shape entirely. It is one weight per vocabulary token, with most of the roughly 250,000 positions zero. The multi-vector output is one 1,024-dimension vector per input token. The embeddings route on this platform accepts a dimensions field in the request body. But unlike some newer embedding APIs, the model itself has no published dimension-truncation training. Treat 1,024 as the working number for storage planning: 4 KB per float32 vector before index overhead.
Is bge-m3 still a good choice next to newer embedding models?
For multilingual work at its size, yes, and the usage numbers say so. It is one of the most-downloaded embedding models on Hugging Face, with tens of millions of downloads. The Ollama library counts millions of pulls. Newer options exist and benchmark higher. The Qwen3-Embedding series, released in June 2025 under Apache 2.0, ships sizes from 0.6B to 8B. It stretches context to 32k tokens. Its largest model ranked first on the MTEB multilingual leaderboard at release. OpenAI's text-embedding-3 models are the closed-API default. They take 8,192-token inputs and 1,536 or 3,072 dimension outputs. The honest split: if you want the highest leaderboard score and can carry a bigger model, the newer families win. If you want one small open-weights model under an MIT license, bge-m3 remains a standard pick. It covers 100+ languages, long inputs, and three retrieval output types.
Is bge-m3 free?
The model's weights carry the MIT license. Downloading them and running them on hardware you already own costs nothing beyond that hardware. An API route charges for something different. That covers the endpoint that stays up, the ledger that matches every bill, and the spending controls. Here the embeddings route bills per token against a prepaid wallet. The balance itself is the ceiling on spend, and the current unit rate sits on the /pricing page. A compromised key is revoked on the spot, without touching the rest of the account.
Can I run bge-m3 locally?
Yes, and the license encourages it. The official route is BAAI's FlagEmbedding library, whose BGEM3FlagModel class returns all three output types. The card also documents sentence-transformers and plain transformers for dense vectors. Ollama lists bge-m3 in its official library. It is a 1.2 GB download that serves dense embeddings over its embed endpoint. GGUF builds exist for llama.cpp-based servers. One caution from practice. If you serve the same weights through two different local runtimes, verify the vectors agree. Do that before you mix them into one index. Community reports document identical GGUF files producing vectors that differ in scale between runtimes. Re-embed the corpus if you switch.