Skip to main content
Lesson 2 of 22 · Embeddings, RAG and Semantic Search

The Embeddings API: Models and Dimensions

Lesson 2 of 22 Updated July 29, 2026 Developer
On this page
  1. Objectives
  2. The Basic Call
  3. The Two Available Models
  4. Batch Embeddings
  5. Dimensionality Reduction
  6. Token Management
  7. Summary

Objectives

  • Master the client.embeddings.create() call
  • Understand the differences between embedding models
  • Use dimensionality reduction

The Basic Call

OpenAI’s entire Embeddings API fits into a single method: client.embeddings.create(). You pass it a text string and a model name:

from openai import OpenAI

client = OpenAI()

response = client.embeddings.create(
    input="Hello, how are you?",
    model="text-embedding-3-large"
)

embedding = response.data[0].embedding
print(f"Type: {type(embedding)}")        # list
print(f"Length: {len(embedding)}")      # 3072
print(f"Snippet: {embedding[:3]}")        # [0.023, -0.041, 0.012]

The returned object isn’t the vector itself but a wrapper containing multiple vectors, because the API is designed to handle batches. Inside, you’ll find data, a list of objects where each has an embedding field (the vector) and an index field (its position in the sent batch); model, the actual model used; and usage.prompt_tokens, the number of billed tokens. You should log this last field from day one: it’s the only reliable measure of what your indexing costs.

The Two Available Models

text-embedding-3-large is the high-performance model. Its 3,072 dimensions capture the finest semantic nuances, making it ideal for searching large corpora, demanding RAG pipelines, and generally any case where precision outweighs cost. If a user phrases questions indirectly or your documents cover a dense technical domain, you’ll see the difference in how results rank.

text-embedding-3-small is more compact with 1,536 dimensions and offers an excellent price-to-performance ratio. It’s the right choice for text classification, recommendations, rapid prototyping, and high-volume applications where the goal isn’t to distinguish between two nearly identical documents but to separate clearly distinct themes. A simple loop verifies what each returns:

# Compare the two models
for model_name in ["text-embedding-3-small", "text-embedding-3-large"]:
    resp = client.embeddings.create(
        input="Artificial intelligence",
        model=model_name
    )
    vec = resp.data[0].embedding
    print(f"{model_name}: {len(vec)} dimensions")

Batch Embeddings

You don’t index a real corpus one text at a time: every HTTP call costs a network round-trip, and processing a thousand documents individually means accumulating a thousand latencies. The API accepts up to 2,048 texts in a single call, and the index field lets you map each vector back to the correct document.

texts = [
    "Machine learning is advancing rapidly",
    "Deep learning is revolutionizing computer vision",
    "Vector databases store embeddings",
    "Python is the preferred language for data scientists"
]

response = client.embeddings.create(
    input=textes,
    model="text-embedding-3-large"
)

for item in response.data:
    print(f"Text {item.index}: vector of {len(item.embedding)} dimensions")

print(f"Tokens consumed: {response.usage.prompt_tokens}")

In practice, group your texts into batches of 100 to 2,048 depending on your volume: smaller batches make recovery after failures easier, while larger ones reduce the number of calls. Monitor usage.prompt_tokens at every iteration to estimate costs as you go rather than discovering them on the bill, and implement exponential backoff retries: a rate limit exceedance is a transient error, not a reason to interrupt a three-hour indexing job.

Dimensionality Reduction

The text-embedding-3-* models accept a dimensions parameter that returns vectors shorter than their native size:

# Full vector: 3072 dimensions
full = client.embeddings.create(
    input="Reduction test",
    model="text-embedding-3-large"
)

# Reduced vector: 256 dimensions
reduced = client.embeddings.create(
    input="Reduction test",
    model="text-embedding-3-large",
    dimensions=256
)

print(f"Full: {len(full.data[0].embedding)} dim")    # 3072
print(f"Reduced: {len(reduced.data[0].embedding)} dim")  # 256

The mechanism is simple: reduction truncates the vector and then re-normalizes it. Since the first dimensions carry the most information, degradation remains very gradual. The table below provides standard benchmarks for trading off quality against storage.

DimensionsRelative QualityStorageUse Case
3,072100%~24 KBHigh-precision search
1,536~98%~12 KBGood general compromise
512~93%~4 KBHigh volume, reduced cost
256~88%~2 KBPrototyping, simple classification

Remember that your dimension choice commits you: all vectors in a given index must have the same size, and changing your mind requires recalculating everything.

Token Management

Embedding models cap at 8,191 tokens per text, and the API returns an error if you exceed this. It’s better to measure before sending than to catch an exception in the middle of a batch:

import tiktoken

encoder = tiktoken.get_encoding("cl100k_base")

text = "A potentially long text..."
tokens = encoder.encode(text)

if len(tokens) > 8191:
    # Truncate
    truncated_text = encoder.decode(tokens[:8191])
    print(f"Text truncated from {len(tokens)} to 8191 tokens")

Truncation is a safety net, not a strategy: a truncated long document loses its end, and the end often contains the answer you’re looking for. Proper handling of large documents involves chunking, which we’ll cover later in the course.

Summary

  • client.embeddings.create() accepts a single text or a list of texts
  • Use text-embedding-3-large (3,072 dim) for precision, text-embedding-3-small (1,536 dim) for cost efficiency
  • The dimensions parameter lets you reduce vector size
  • Batching texts is more efficient than individual calls
  • Limit of 8,191 tokens per text