OpenAI Embeddings RAG 9 min read

OpenAI Embeddings Guide 2025: Semantic Search, RAG & Vector Storage

OpenAI's text-embedding-3 models turn text into semantic vectors, powering semantic search, RAG pipelines, and recommendation systems. This guide covers every model option, the full RAG pipeline from chunk to generation, vector storage integrations, and a pricing comparison against alternatives.

What Are Embeddings?

An embedding model converts text into a fixed-length numeric vector (e.g., 1536 numbers). Semantically similar texts produce vectors that are close together in vector space — measured by cosine similarity (1.0 = identical meaning, 0.0 = unrelated, -1.0 = opposite). This enables search without keyword matching: "car" and "automobile" will be retrieved together even though they share no characters.

Common applications: semantic search, RAG (Retrieval-Augmented Generation), recommendation engines, duplicate detection, clustering, and classification.

OpenAI Embedding Models Compared

Model Dimensions Price (1M tokens) Best for
text-embedding-3-small 1536 $0.02 Default choice — best value
text-embedding-3-large 3072 $0.13 Maximum accuracy (legal/medical/enterprise)
text-embedding-ada-002 1536 $0.10 Legacy only — do not use for new projects

text-embedding-3-small outperforms ada-002 on MTEB benchmarks and costs 5x less. Always use text-embedding-3-small as the default.

Generating Embeddings

Python — single embedding

from openai import OpenAI
import numpy as np

client = OpenAI()  # uses OPENAI_API_KEY env var

response = client.embeddings.create(
    model="text-embedding-3-small",
    input="The quick brown fox jumps over the lazy dog",
)

vector = response.data[0].embedding  # list of 1536 floats
print(f"Dimensions: {len(vector)}")  # 1536

Cosine similarity between two texts

def cosine_similarity(a: list, b: list) -> float:
    a, b = np.array(a), np.array(b)
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

texts = ["I love programming in Python", "Python coding is my passion"]
embeddings = [
    client.embeddings.create(model="text-embedding-3-small", input=t).data[0].embedding
    for t in texts
]
print(cosine_similarity(embeddings[0], embeddings[1]))  # ~0.93

Building a RAG Pipeline

RAG connects an LLM to a vector knowledge base. The four phases:

  1. Chunk — split documents into 300-500 token chunks with ~50 token overlap
  2. Embed — embed each chunk with text-embedding-3-small
  3. Store — upsert vectors into a vector DB (Pinecone / Qdrant / Chroma / pgvector)
  4. Retrieve & Generate — at query time, embed the question, find top-K similar chunks, inject them into the LLM prompt

Minimal RAG with ChromaDB

from openai import OpenAI
import chromadb

client = OpenAI()
chroma = chromadb.EphemeralClient()
collection = chroma.get_or_create_collection("docs")

# --- Indexing ---
docs = [
    "vLLM uses PagedAttention for efficient KV cache management.",
    "OpenAI embeddings are generated via the /v1/embeddings endpoint.",
    "Pinecone is a managed vector database with a generous free tier.",
]

embeddings = client.embeddings.create(
    model="text-embedding-3-small",
    input=docs,
).data

collection.upsert(
    ids=[str(i) for i in range(len(docs))],
    embeddings=[e.embedding for e in embeddings],
    documents=docs,
)

# --- Query ---
query = "How does vLLM manage memory?"
q_embedding = client.embeddings.create(
    model="text-embedding-3-small", input=query
).data[0].embedding

results = collection.query(query_embeddings=[q_embedding], n_results=2)
context = "\n".join(results["documents"][0])

answer = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": f"Answer using only: {context}"},
        {"role": "user", "content": query},
    ],
).choices[0].message.content

print(answer)

Matryoshka Dimensions (Smaller = Cheaper)

The text-embedding-3 models support a dimensions parameter that truncates vectors to a smaller size with only minor quality loss. Use this to reduce storage and query costs:

# Full 1536 dimensions (default)
client.embeddings.create(model="text-embedding-3-small", input="Hello")

# 512 dimensions — 3x less storage, ~2% quality drop
client.embeddings.create(
    model="text-embedding-3-small",
    input="Hello",
    dimensions=512,
)

# 256 dimensions — 6x less storage, ~5% quality drop
client.embeddings.create(
    model="text-embedding-3-small",
    input="Hello",
    dimensions=256,
)

Batch Embedding for Cost Efficiency

Pass a list of strings to embed multiple texts in one API call. Batches of up to 2048 strings are supported. Use the Batch API for large jobs (>50k documents) to get a 50% discount:

texts = ["chunk 1 ...", "chunk 2 ...", "chunk 3 ..."]  # up to 2048

response = client.embeddings.create(
    model="text-embedding-3-small",
    input=texts,  # batch in one call
)

vectors = [item.embedding for item in response.data]
# response.usage.total_tokens shows exact tokens consumed

OpenAI Embeddings vs Alternatives

Provider Best model Price (1M tokens) Notes
OpenAI text-embedding-3-small $0.02 Easiest to start, MRL dimensions
Cohere embed-english-v3.0 $0.10 Requires input_type param, multilingual v3
Voyage AI voyage-3 $0.06 Top MTEB scores, Anthropic-backed
sentence-transformers all-MiniLM-L6-v2 Free (self-hosted) Local only, CPU-friendly, smaller quality

See also: OpenAI API guide · Pinecone guide · Qdrant guide · Chroma guide · Supabase AI guide

Monitor the OpenAI API Your RAG Pipeline Depends On

RAG pipelines fail silently when the embedding API is degraded. Prismix tracks OpenAI API status in real-time with email and webhook alerts so you know before your users do.

Check OpenAI API Status →