Model Library
Browse and deploy state-of-the-art AI models through the DEVUP Gateway.
Browse and deploy state-of-the-art AI models through the DEVUP Gateway.
Text Embedding 3 Large is the higher-quality tier of OpenAI's current embedding generation, producing vectors of up to 3,072 dimensions against the smaller model's 1,536. The extra capacity shows most clearly on multilingual retrieval and on corpora where passages are semantically close and need to be told apart. Like its smaller sibling, the vector length is adjustable: a single parameter shortens the output without retraining, and shortening it is often the right call — a 1,536-dimension vector from this model generally retrieves better than a full-length vector from the smaller one, at identical storage cost. It handles inputs up to 8,191 tokens and produces deterministic, normalised output.

The higher-capacity embedding model in OpenAI's current generation. Vectors run to 3,072 dimensions — twice the smaller tier — and the difference shows where it usually matters: multilingual retrieval, and corpora where the passages are similar enough that telling them apart is the actual problem.
The obvious framing is quality versus storage: the large model gives better vectors, the small model gives cheaper ones. That framing is wrong, because vector length is adjustable on both.
A 1,536-dimension vector produced by this model generally outperforms a 1,536-dimension vector produced by the smaller one. Same storage, same comparison cost, same index size — better retrieval.
{
"model": "openai/text-embedding-3-large",
"input": "How do I speed up a slow query?",
"dimensions": 1536
}So the real trade is not storage. It is the cost of generating the embeddings, which you pay once per document, against retrieval quality, which you get on every query for the life of the index.
For most corpora that arithmetic favours the larger model at a reduced dimension. Embedding is a one-time cost per document; bad retrieval is a permanent one.
The model is trained so that the most significant information concentrates in the earliest dimensions of the vector. Requesting fewer dimensions discards the least informative components first — which is why a shortened vector degrades gracefully instead of collapsing.
| Dimensions | Storage relative to full | Typical use |
|---|---|---|
| 3,072 | 1× | Maximum quality, smaller corpora |
| 1,536 | ½ | The common sweet spot |
| 1,024 | ⅓ | Large indexes where storage is a real constraint |
| 512 | ⅙ | Very large indexes, simpler corpora |
Measure rather than guess. Assemble a few hundred query-and-expected-result pairs drawn from your own corpus, embed at full dimension, record retrieval accuracy, then repeat at each candidate size. Take the smallest that holds your number. The right answer depends on how semantically crowded your data is, and finding it costs an afternoon.
One rule with no exceptions: every vector in an index must share the same dimension count. Mixing lengths produces results that are silently wrong rather than an error.
| Model ID | openai/text-embedding-3-large |
| Max input | 8,191 tokens |
| Output dimensions | 3,072 by default, reducible |
| Output | Normalised float vector |
| Determinism | Identical input yields identical output |
| Endpoint | /v1/embeddings |
Specifications as published by OpenAI. The model's construction is not disclosed — no parameter count, no architecture, no training method, no weights.
| Capability | Value |
|---|---|
input_types | text |
output_types | embedding |
context_window | 8191 |
output_dimensions | 3072 (reducible) |
endpoint | /v1/embeddings |
streaming | Not applicable |
tool_calling | Not applicable |
reasoning | Not applicable |
deterministic | Yes |
requires_prompt | Yes — input text required |
Not every corpus needs it. These are the cases where it consistently does.
Multilingual retrieval. The clearest measured gap between the two tiers. If your corpus spans languages, or your users query in one language against documents written in another, this is the tier to build on.
Semantically crowded corpora. Technical documentation where forty pages all discuss the same subsystem. Legal clauses that differ by a qualifier. Product variants separated by one attribute. When the retrieval problem is discrimination rather than recall, capacity helps.
Long-lived indexes. A corpus you will query for years justifies more care at build time than one rebuilt monthly.
Retrieval feeding an expensive model. If bad context is about to be processed by a frontier model, the retrieval step is the cheapest place to improve the final answer.
Conversely, a few thousand short, distinct FAQ entries in one language will not notice the difference. Use the smaller tier and spend the attention elsewhere.
Endpoint: POST https://api.devupai.com/v1/embeddings
curl https://api.devupai.com/v1/embeddings \
-H "Authorization: Bearer $DEVUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/text-embedding-3-large",
"input": "How do I speed up a slow database query?",
"dimensions": 1536
}'Batching is the largest single performance factor on an embedding endpoint. One request with a hundred texts beats a hundred requests with one each by a wide margin.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEVUP_API_KEY"],
base_url="https://api.devupai.com/v1",
)
DIMENSIONS = 1536 # decided by measurement, fixed for the life of the index
passages = [
"Adding an index on the filtered column usually eliminates a sequential scan.",
"Connection pooling reduces the cost of opening a new database session.",
"Les paiements par carte CIB sont traites en dinar algerien.",
]
response = client.embeddings.create(
model="openai/text-embedding-3-large",
input=passages,
dimensions=DIMENSIONS,
)
vectors = [item.embedding for item in response.data]
print(f"{len(vectors)} vectors of {len(vectors[0])} dimensions")
print(f"Tokens consumed: {response.usage.total_tokens}")Results return in the order sent. Batches in the low hundreds are a sensible size — large enough to amortise request overhead, small enough that one failure does not cost much to retry.
The evaluation worth running before you commit an index.
import numpy as np
def embed(texts: list[str], dimensions: int) -> np.ndarray:
"""Embed texts at a given dimension and return them as a matrix."""
response = client.embeddings.create(
model="openai/text-embedding-3-large",
input=texts,
dimensions=dimensions,
)
return np.array([item.embedding for item in response.data])
def recall_at_k(corpus: list[str], queries: list[str], expected: list[int], dimensions: int, k: int = 5) -> float:
"""Fraction of queries whose expected passage appears in the top k results."""
corpus_vectors = embed(corpus, dimensions)
query_vectors = embed(queries, dimensions)
hits = 0
for query_vector, target in zip(query_vectors, expected):
top_k = np.argsort(corpus_vectors @ query_vector)[::-1][:k]
hits += target in top_k
return hits / len(queries)
for dimensions in (3072, 1536, 1024, 512):
score = recall_at_k(CORPUS, QUERIES, EXPECTED, dimensions)
print(f"{dimensions:>5} dims recall@5 {score:.3f}")Run this once against your real corpus and the dimension question stops being a judgment call.
The capability where this tier separates itself most.
CORPUS = [
"Les remboursements sont effectues sous 14 jours ouvrables.",
"La livraison est gratuite pour les commandes superieures a 5000 DZD.",
"Le service client est disponible du dimanche au jeudi.",
]
corpus_vectors = embed(CORPUS, DIMENSIONS)
query_vector = embed(["When will I be refunded?"], DIMENSIONS)[0]
# Vectors are normalised, so the dot product is the cosine similarity.
scores = corpus_vectors @ query_vector
print(CORPUS[int(np.argmax(scores))])An English query retrieving a French passage with no translation step, because meaning maps to the same space regardless of the language expressing it.
Output is deterministic, so re-embedding text that has not changed produces an identical vector and buys nothing.
import hashlib
import json
from pathlib import Path
CACHE = Path("embedding_cache.json")
def cached_embed(texts: list[str], dimensions: int = DIMENSIONS) -> list[list[float]]:
"""Embed only texts not already stored, keyed by content and dimension."""
store = json.loads(CACHE.read_text()) if CACHE.exists() else {}
keys = [hashlib.sha256(f"{dimensions}:{t}".encode()).hexdigest() for t in texts]
missing = [(k, t) for k, t in zip(keys, texts) if k not in store]
if missing:
response = client.embeddings.create(
model="openai/text-embedding-3-large",
input=[t for _, t in missing],
dimensions=dimensions,
)
for (key, _), item in zip(missing, response.data):
store[key] = item.embedding
CACHE.write_text(json.dumps(store))
return [store[k] for k in keys]Including the dimension in the cache key is not optional. The same text at two lengths produces two different vectors, and treating them as interchangeable corrupts the index quietly.
npm install devupaiimport DevupAI from "devupai";
const client = new DevupAI({
apiKey: process.env.DEVUP_API_KEY,
});
const response = await client.embeddings.create({
model: "openai/text-embedding-3-large",
input: [
"Adding an index usually eliminates a sequential scan.",
"Connection pooling reduces session setup cost.",
],
dimensions: 1536,
});
const vectors = response.data.map((item) => item.embedding);
console.log(`${vectors.length} vectors of ${vectors[0].length} dimensions`);The limit is a ceiling, not an objective.
Every vector is a single point. The longer and more varied the text behind it, the more that point becomes an average of unrelated meanings — near nothing in particular, and retrieved well for nothing.
Split where meaning breaks, not where the token counter runs out. Headings, paragraph boundaries, logical units.
200 to 800 tokens per chunk works for most document types. Long enough to carry its own context, short enough to stay about one thing.
Overlap neighbours by a sentence or two, so a passage crossing a boundary is findable from either side.
Keep headings attached to their content. A chunk reading "must be completed within 14 days" with no indication of what must be completed is retrievable and worthless.
One model, one dimension, one index. Query and corpus must match on both. A mismatch produces meaningless similarity scores rather than an error.
Vectors arrive normalised. The dot product is already the cosine similarity.
Store the model name and dimension count with the index. A year from now, that metadata is the difference between a migration and a rebuild from scratch.
Similarity is not relevance. Two passages can sit close together and still be the wrong answer. Chunking, corpus quality, and ranking each affect retrieval as much as the embedding model does.
Add lexical search alongside. Identifiers, SKUs, version strings, and rare proper nouns are where dense retrieval underperforms and keyword matching excels. Running both and merging beats either alone on almost every real corpus.
Debounce embeddings triggered by typing. A search box that embeds on every keystroke turns a modest per-call cost into a large monthly one.