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:
| Fact | Value | Where the number comes from |
|---|---|---|
| Parameters | about 568M | BGE documentation (2.27 GB fp32 weights in the repository) |
| Dense dimension | 1,024 | model card and model config |
| Input window | 8,192 tokens | model card (config carries 8,194 position embeddings) |
| Languages | 100+ working | model card |
| License | MIT | model card |
| Base architecture | XLM-RoBERTa-large, extended to 8,192 positions | BGE documentation |
| Released | January 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.
| Field | Required | Meaning |
|---|---|---|
model | yes | bge-m3 |
input | yes | one string or an array of strings; an empty input is turned away with a 400 before any metering runs |
encoding_format | no | response encoding hint, passed through |
dimensions | no | dimension 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:
- 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 tototal_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. - Replays are built in. Send the same
Idempotency-Keytwice on this JSON route and the second attempt gets the stored answer; nothing executes again and nothing bills twice. - A model answers only its own route. A chat alias called at
/v1/embeddingsis a plain 404, never a cross-family proxy, andbge-m3called 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:
| Metric | Value | Basis |
|---|---|---|
| Embed latency p50, c=1 | 1.38 s | E5b grid, single flight |
| Embed latency p50 / p95, c=8 | 1.90 s / 2.43 s | E5b grid |
| Embed latency p50 / p95, c=32 | 4.33 s / 6.10 s | E5b grid |
| Aggregate throughput | about 10x from c=1 to c=32 (36.9 to ~378 tokens/s, estimate) | E5b; token rates are chars-per-four estimates |
| Concurrency ceiling | 8 in-flight requests | E5b seat ceiling, shipped as the monitor default |
| Single-flight first response, p95 | about 1.24 s | task-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
- 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.
- 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.
- 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.
- 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.