n4nAI

How to call OpenAI's text-embedding-3-large from Python

Step-by-step guide to calling OpenAI's text-embedding-3-large from Python: setup, batching, dimensions, error handling, and verification with runnable code examples.

n4n Team3 min read619 words

Audio narration

Coming soon — every post will get a voice note here.

Calling text-embedding-3-large from Python is a matter of installing the official SDK and sending a POST to the embeddings endpoint, but real integrations need batching, dimension control, and retry logic. This guide gives you a complete, runnable text-embedding-3-large python workflow from API key to verified vectors, with the edge cases that bite in production.

Step 1: Set up your environment and API key

Start in a clean virtual environment. The OpenAI Python SDK v1.x is the only dependency you need for the standard path.

python -m venv venv
source venv/bin/activate
pip install "openai>=1.12.0"
export OPENAI_API_KEY="sk-your-key-here"

Do not hardcode the key in source. The SDK reads OPENAI_API_KEY from the environment by default, which keeps secrets out of version control. If you run in a container or CI, inject the variable at runtime.

Step 2: Make your first embedding call

The minimal text-embedding-3-large python call uses client.embeddings.create. The model returns a 3072-dimensional vector by default.

from openai import OpenAI

client = OpenAI()  # picks up OPENAI_API_KEY automatically

resp = client.embeddings.create(
    model="text-embedding-3-large",
    input="Legal contracts often contain indemnification clauses.",
)

emb = resp.data[0].embedding
print(len(emb), type(emb[0]))

The output is a list of floats. len(emb) is 3072. The usage field reports prompt_tokens and total_tokens for metering. Treat the embedding list as immutable—never round or truncate without explicit dimension control (covered next).

Step 3: Reduce dimensions to cut storage and latency

text-embedding-3-large supports the dimensions parameter, which projects the vector down via Matryoshka representation. You keep semantic quality at lower dims for many retrieval tasks.

resp = client.embeddings.create(
    model="text-embedding-3-large",
    input="Same text, smaller vector",
    dimensions=1024,
)

print(len(resp.data[0].embedding))  # 1024

Valid values range from 256 to 3072. Pick the smallest dim that holds retrieval recall on your validation set. Storing 1024 floats instead of 3072 cuts vector DB memory by ~67%.

Step 4: Batch multiple texts efficiently

The API accepts a list of strings in input. Order is preserved in the response via the index field. Batching reduces HTTP overhead and is the correct way to embed a corpus.

texts = [
    "First document body",
    "Second document body",
    "Third document body",
]

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

assert len(resp.data) == len(texts)
for original_idx, item in enumerate(resp.data):
    assert item.index == original_idx
    # item.embedding is ready to store

Each individual string is capped at 8191 tokens. If a document exceeds that, chunk it before embedding. The batch itself has a total token limit enforced by the server; split large jobs into chunks of a few hundred items and retry on 429.

Step 5: Verify the embeddings are correct

Never assume the call worked because it returned 200. Verify shape, type, and semantic sanity. A fast check: similar sentences should have high cosine similarity, random pairs near zero.

import math

def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    na = math.sqrt(sum(x * x for x in a))
    nb = math.sqrt(sum(y * y for y in b))
    return dot / (na * nb)

similar = client.embeddings.create(
    model="text-embedding-3-large",
    input=["The cat sat on the mat", "A feline rested on the rug"],
).data
random_pair = client.embeddings.create(
    model="text-embedding-3-large",
    input=["The cat sat on the mat", "Quantum processors use superposition"],
).data

print(cosine(similar[0].embedding, similar[1].embedding))  # ~0.8+
print(cosine(random_pair[0].embedding, random_pair[1].embedding))  # near 0.0

If the first score is not clearly higher than the second, inspect preprocessing (whitespace, encoding) or model name typos.

Step 6: Handle errors and rate limits

Production code must survive 429 and 5xx. The SDK raises RateLimitError and APIError. Retry with exponential backoff, but only on transient failures.

from openai import APIError, RateLimitError
import time

def embed_with_retry(client, texts, attempts=3):
    for i in range(attempts):
        try:
            return client.embeddings.create(
                model="text-embedding-3-large",
                input=texts,
            )
        except RateLimitError as e:
            if i == attempts - 1:
                raise
            time.sleep(2 ** i)
        except APIError as e:
            if e.status_code >= 500 and i < attempts - 1:
                time.sleep(2 ** i)
            else:
                raise

Embedding requests are read-only and safe to retry. Do not retry on 401 or 400—those indicate permanent config errors.

Step 7: Call the API with raw HTTP (no SDK)

In locked-down environments you may avoid the SDK. A requests call mirrors the SDK payload exactly.

import requests, os

resp = requests.post(
    "https://api.openai.com/v1/embeddings",
    headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
    json={
        "model": "text-embedding-3-large",
        "input": ["hello world"],
        "dimensions": 512,
    },
    timeout=30,
).json()

print(len(resp["data"][0]["embedding"]))  # 512

Use this when you need to control the HTTP layer, proxy through a sidecar, or audit the exact wire format. The JSON schema matches the SDK response.

Step 8: Productionize with an OpenAI-compatible gateway

If you serve multiple model providers or want fallback when OpenAI is degraded, point the same SDK at an OpenAI-compatible base URL. n4n.ai exposes one endpoint covering text-embedding-3-large and 240+ other models, with automatic fallback on provider rate limits and per-token metering. It forwards provider cache-control hints and honors client routing directives.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.n4n.ai/v1",
    api_key="your-gateway-key",
)

resp = client.embeddings.create(
    model="text-embedding-3-large",
    input=["route me through the gateway"],
)

The call signature is identical. You gain a single credential surface and provider redundancy without rewriting the integration.

Step 9: Store and use the vectors

Once verified, push the vectors to your store. For Postgres + pgvector:

CREATE TABLE docs (id serial primary key, body text, emb vector(3072));
import psycopg2
conn = psycopg2.connect("postgres://user:pass@localhost/db")
cur = conn.cursor()
cur.execute(
    "INSERT INTO docs (body, emb) VALUES (%s, %s)",
    ("doc text", resp.data[0].embedding),
)
conn.commit()

Normalize vectors if your similarity metric assumes unit length. For text-embedding-3-large python retrieval, cosine distance via inner product on normalized vectors is the standard.

You now have a complete path: environment, single and batched calls, dimension tuning, verification, retries, raw HTTP escape hatch, gateway option, and storage. Swap the model string and dimensions as your corpus evolves, but keep the verification step in your test suite.

Tagsopenaiembeddingspythonapi-integration

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All embeddings api integration across languages posts →