OpenAI SDK: Embeddings & semantic search
An embedding is a list of numbers (a vector) that represents the meaning of a piece of text.
In this post
What this post covers
An embedding is a list of numbers (a vector) that represents the meaning of a piece of text. Texts with similar meaning get vectors that point in similar directions, even with no words in common: “laptop won’t turn on” lands near “notebook computer has no power”. Comparing vectors with cosine similarity gives you semantic search, deduplication, clustering and recommendations, and it is the “retrieval” in RAG (the AI Engineering guide).
A map of meaning
Imagine every sentence placed on a huge map, where nearby places mean similar things. "Refund my order" and "I want my money back" are neighbours; "delivery van" is in another district. An embedding model is the surveyor that gives each sentence its coordinates, with 1,536 of them instead of two.
1. The API
real API
from openai import OpenAI
client = OpenAI()
result = client.embeddings.create(
model="text-embedding-3-small", # 1,536 dimensions by default; -large has 3,072
input=["laptop won't turn on", "notebook computer has no power", "delivery was late"],
)
vectors = [d.embedding for d in result.data]
print(len(vectors), len(vectors[0]), result.usage.total_tokens)
Batch inputs
Send many texts per request (a list). It is much faster than one request per text.
Shorter vectors
dimensions=256 returns smaller vectors that keep most of the quality: less storage, faster search.
Cheap
Embeddings cost a small fraction of text generation. Embed once, store the vectors, and search many times.
2. Cosine similarity
3. A working semantic search
import numpy as np
from fake_openai import fake_client
products = ["Ergonomic office chair with lumbar support", "Standing desk, electric height adjust",
"Noise cancelling wireless headphones", "Bluetooth earbuds with charging case",
"LED desk lamp with USB port", "Mesh back task chair for home office"]
client = fake_client() # fake: toy embeddings (see note)
emb = client.embeddings.create(model="text-embedding-3-small", input=products)
matrix = np.array([d.embedding for d in emb.data]) # one row per product
def search(query, k=3):
q = np.array(client.embeddings.create(model="text-embedding-3-small", input=query).data[0].embedding)
scores = matrix @ q # dot product = cosine for unit vectors
best = np.argsort(-scores)[:k]
return [(products[i], round(float(scores[i]), 2)) for i in best]
for query in ["chair for my back", "wireless audio"]:
print(query, "→", search(query))
Output
chair for my back → [('Mesh back task chair for home office', 0.55), ('Ergonomic office chair with lumbar support', 0.28), ('Bluetooth earbuds with charging case', 0.1)]
wireless audio → [('Noise cancelling wireless headphones', 0.38), ('Mesh back task chair for home office', 0.17), ('Standing desk, electric height adjust', 0.11)]
The fake server's embeddings are a toy: hashed words and word fragments, so they only match on shared
vocabulary. They let the code run offline. Real embeddings capture meaning, so "wireless audio" would
also match "Bluetooth earbuds" and "noise cancelling headphones" with no shared words. Run the same code
with OpenAI() to see the difference.
4. From a NumPy array to a vector database
| Number of vectors | Approach |
|---|---|
| up to ~100k | a NumPy matrix in memory, as above: exact, simple, milliseconds |
| ~100k to many millions | a vector index (FAISS, pgvector, Chroma, Qdrant, Databricks Vector Search) with approximate nearest neighbours |
| documents you never want to manage | OpenAI vector stores + the file_search tool (part 14) |
Store the model name with your vectors. Vectors from different embedding models are not comparable, so switching models means re-embedding everything.
5. Other uses
Deduplication
Pairs with similarity above a threshold (say 0.92) are probably the same ticket, product or question.
Classification without training
Embed one example per category, and assign each new text to the nearest category.
Recap
- Embeddings map text to vectors; similar meaning → similar direction.
- Cosine similarity (a dot product for unit vectors) ranks results.
- Embed in batches, store vectors with the model name, and search many times.
- NumPy for small sets, a vector database for large ones; use
dimensionsto shrink vectors.
Get the free RAG production checklist
25 checks to run before real users see your RAG feature: exact-match misses, invented citations, prompt injection through documents, quality that slips after a prompt change. Confirm your email and the PDF comes with the welcome email, plus each new experiment.