OpenAI Embeddings Free code examples in C#, Python, Java and JavaScript
The default embedding models most RAG pipelines start on.
Try it without writing code: Document Summarizer — free in the browser.
WHAT THIS PAGE COVERS
Batching and dimension reduction, which halve the bill without hurting recall.
text-embedding-3-small
openai
Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.
EmbeddingClient is constructed per model, the same pattern as ChatClient and AudioClient.
dotnet add package OpenAI
setx OPENAI_API_KEY "sk-your-key" # Windows, then open a new shell
export OPENAI_API_KEY="sk-your-key" # macOS / Linux
ToFloats() returns a ReadOnlyMemory<float> rather than an array — a view over the buffer, so no copy unless you ask for one.
using OpenAI.Embeddings;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
EmbeddingClient client = new(model: "text-embedding-3-small", apiKey: apiKey);
OpenAIEmbedding embedding =
await client.GenerateEmbeddingAsync("Your text string goes here");
ReadOnlyMemory<float> vector = embedding.ToFloats();
Console.WriteLine(vector.Length); // 1536
GenerateEmbeddingsAsync takes a collection. One call for a few hundred chunks rather than a few hundred calls is where the time goes.
static async Task<List<ReadOnlyMemory<float>>> EmbedAllAsync(
EmbeddingClient client, IReadOnlyList<string> chunks, int batchSize = 256)
{
var vectors = new List<ReadOnlyMemory<float>>(chunks.Count);
for (var start = 0; start < chunks.Count; start += batchSize)
{
var batch = chunks.Skip(start).Take(batchSize).ToList();
OpenAIEmbeddingCollection result =
await client.GenerateEmbeddingsAsync(batch);
// The collection is in request order.
vectors.AddRange(result.Select(item => item.ToFloats()));
}
return vectors;
}
Dimensions truncates at the API. 3-large at 1024 outperforms 3-small at 1536 while taking a third less space in the index — and a smaller index is a faster one.
EmbeddingClient large = new(model: "text-embedding-3-large", apiKey: apiKey);
EmbeddingGenerationOptions options = new() { Dimensions = 1024 };
OpenAIEmbedding compact = await large.GenerateEmbeddingAsync(
"Your text string goes here", options);
Console.WriteLine(compact.ToFloats().Length); // 1024
// ONE MODEL, ONE WIDTH, for the life of an index. Mixing them does
// not throw — it returns neighbours that are simply wrong.
The same `openai` package as chat. Embeddings are a namespace on the same client.
pip install openai
export OPENAI_API_KEY="sk-your-key" # macOS / Linux
setx OPENAI_API_KEY "sk-your-key" # Windows, then open a new shell
The response is a list even for one input, which is the shape that catches people porting from a library that returns a bare vector.
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
input="Your text string goes here",
model="text-embedding-3-small",
)
vector = response.data[0].embedding
print(len(vector)) # 1536
INPUT TAKES A LIST. Five hundred chunks in one request rather than five hundred requests removes five hundred round trips and most of the wall-clock time. Results come back IN ORDER, so zip them to the source.
def embed_all(chunks: list[str], batch_size: int = 256) -> list[list[float]]:
vectors: list[list[float]] = []
for start in range(0, len(chunks), batch_size):
batch = chunks[start:start + batch_size]
response = client.embeddings.create(
input=batch,
model="text-embedding-3-small",
)
# Order is guaranteed, but index is there if you want to assert it.
vectors.extend(item.embedding for item in sorted(
response.data, key=lambda item: item.index))
return vectors
dimensions TRUNCATES AT THE API and renormalises. A 3-large cut to 1024 still outperforms 3-small at its native 1536, at two thirds of the storage. Not supported on ada-002.
# Full size: 3072 floats per chunk.
full = client.embeddings.create(
input="Your text string goes here",
model="text-embedding-3-large",
).data[0].embedding
# Same model, 1024 floats. Storage and index memory fall with it.
compact = client.embeddings.create(
input="Your text string goes here",
model="text-embedding-3-large",
dimensions=1024,
).data[0].embedding
print(len(full), len(compact)) # 3072 1024
# PICK ONE AND STAY ON IT. Vectors of different widths, or from
# different models, cannot be compared — a mixed index returns
# nonsense rather than an error.
Embedding is cheap per call and expensive per corpus. Hashing the chunk and skipping unchanged text is the difference between a nightly job that costs pennies and one that re-bills the whole archive.
import hashlib
def content_hash(text: str) -> str:
return hashlib.sha256(text.encode("utf-8")).hexdigest()
def embed_changed(chunks: list[str], cache: dict[str, list[float]]):
fresh = [chunk for chunk in chunks if content_hash(chunk) not in cache]
if fresh:
response = client.embeddings.create(
input=fresh,
model="text-embedding-3-small",
)
for chunk, item in zip(fresh, response.data):
cache[content_hash(chunk)] = item.embedding
return [cache[content_hash(chunk)] for chunk in chunks]
Embeddings sit under client.embeddings() on the same OpenAIClient as everything else.
# Gradle
implementation("com.openai:openai-java:4.42.0")
export OPENAI_API_KEY="sk-your-key"
The response wraps its data in Optional, as everywhere in this SDK. The embedding itself is a List<Float>, not a float[].
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.embeddings.EmbeddingCreateParams;
import com.openai.models.embeddings.CreateEmbeddingResponse;
import java.util.List;
public class Embed {
public static void main(String[] args) {
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
EmbeddingCreateParams params = EmbeddingCreateParams.builder()
.model("text-embedding-3-small")
.input("Your text string goes here")
.build();
CreateEmbeddingResponse response = client.embeddings().create(params);
List<Float> vector = response.data().get(0).embedding();
System.out.println(vector.size()); // 1536
}
}
inputOfArrayOfStrings takes the whole batch. The results come back in request order, so pair them by position.
import java.util.ArrayList;
static List<List<Float>> embedAll(OpenAIClient client, List<String> chunks) {
List<List<Float>> vectors = new ArrayList<>(chunks.size());
int batchSize = 256;
for (int start = 0; start < chunks.size(); start += batchSize) {
List<String> batch =
chunks.subList(start, Math.min(start + batchSize, chunks.size()));
EmbeddingCreateParams params = EmbeddingCreateParams.builder()
.model("text-embedding-3-small")
.inputOfArrayOfStrings(batch)
.build();
client.embeddings().create(params).data()
.forEach(item -> vectors.add(item.embedding()));
}
return vectors;
}
dimensions truncates server-side and renormalises. Storing 3-large at 1024 rather than 3072 is a two-thirds saving for output that still beats the small model.
EmbeddingCreateParams compact = EmbeddingCreateParams.builder()
.model("text-embedding-3-large")
.input("Your text string goes here")
.dimensions(1024)
.build();
List<Float> vector = client.embeddings()
.create(compact)
.data().get(0).embedding();
System.out.println(vector.size()); // 1024
// Fix the model and the width before the first document is indexed.
// Changing either later means re-embedding the whole corpus.
Server-side. Embedding a corpus from the browser would ship the key with it.
npm install openai
export OPENAI_API_KEY="sk-your-key"
encoding_format defaults to float. Asking for base64 halves the response size on a large batch, at the cost of decoding it yourself.
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: "Your text string goes here",
encoding_format: "float",
});
console.log(response.data[0].embedding.length); // 1536
INPUT TAKES AN ARRAY. Batches of a couple of hundred turn a long serial job into a short one, and the results come back in the order you sent them.
async function embedAll(chunks, batchSize = 256) {
const vectors = [];
for (let start = 0; start < chunks.length; start += batchSize) {
const batch = chunks.slice(start, start + batchSize);
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: batch,
});
// Sort by index rather than trusting order, at no real cost.
response.data
.sort((a, b) => a.index - b.index)
.forEach((item) => vectors.push(item.embedding));
}
return vectors;
}
dimensions truncates and renormalises server-side. 3-large at 1024 beats 3-small at 1536 and costs a third less to store — the cheapest accuracy-per-byte decision available here.
const compact = await openai.embeddings.create({
model: "text-embedding-3-large",
input: "Your text string goes here",
dimensions: 1024,
});
console.log(compact.data[0].embedding.length); // 1024
// PICK ONE WIDTH AND ONE MODEL, then stay on them. Mixing widths or
// models in one index does not error — it quietly returns bad
// neighbours, which is much harder to notice.
One bad chunk fails the whole batch, and re-running all of it doubles the spend. Split on failure instead: the bisect costs a few extra calls and finds the offender.
async function embedResilient(chunks) {
try {
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: chunks,
});
return response.data.map((item) => item.embedding);
} catch (error) {
if (chunks.length === 1) {
// A single chunk that cannot be embedded — usually empty
// after cleaning, or past the context limit.
console.error("unembeddable chunk:", chunks[0].slice(0, 80));
return [null];
}
const middle = Math.floor(chunks.length / 2);
return [
...(await embedResilient(chunks.slice(0, middle))),
...(await embedResilient(chunks.slice(middle))),
];
}
}
QUESTIONS
Which languages are the OpenAI Embeddings examples in?
C#, Python, Java and JavaScript. Every sample runs as pasted and reads its credentials from an environment variable — no key is ever written into the code.
Which package do I install for OpenAI Embeddings?
The samples use openai. The install step on each language tab gives the exact command.
Which OpenAI Embeddings model do these examples use?
text-embedding-3-small, checked against the official documentation on 26 September 2026. Model ids change often; if a call returns a model-not-found error, check the current list in the official docs.
Where is the official OpenAI Embeddings documentation?
https://platform.openai.com/docs/guides/embeddings — it is linked at the bottom of this page, and it is the authority whenever it disagrees with a sample here.
ALSO IN EMBEDDINGS & SEARCH
The authority
Official OpenAI Embeddings documentation
Model names, pricing and rate limits change faster than any page here can. When this page and the official docs disagree, the official docs are right.
https://platform.openai.com/docs/guides/embeddings