OpenAI API Integration: GPT-4o, o1, o3 — Under the Hood
Recently, on a project with tens of thousands of requests per day, the team hit a budget wall due to using a single model for all tasks. After migrating to a combination of GPT-4o, o3-mini, and GPT-4o-mini, we managed to reduce costs by 3x without losing quality. Let's dive into how to choose models, configure a client with retries, and implement structured outputs. Our experience — 5 years in AI integrations and over 50 completed projects — allows us to guarantee 99.9% uptime.
Which Models to Choose and Why Beginners Get It Wrong
OpenAI offers a family of models with different architectures. GPT-4o is a universal multimodal soldier: it accepts text and images, outputs structured responses, and works fast. For deep reasoning (mathematical proofs, algorithmic code), use o1 and o3-mini — they spend more time on chain-of-thought. For high-load scenarios with simple tasks (e.g., classification), use GPT-4o-mini: its p99 latency is 2x lower, and cost per token is almost 10x lower.
| Model | Purpose | Features |
|---|---|---|
| GPT-4o | Universal chat, vision, structured outputs | Best balance quality/cost, supports function calling |
| GPT-4o-mini | High-load, simple tasks, classification | Fast, cheap, but weaker in reasoning |
| o3-mini | Deep reasoning, code, logic | Reasoning effort adjustable, does not support system prompt |
A typical mistake is using a single model for everything. GPT-4o-mini handles 80% of tasks, but many use GPT-4o everywhere, overpaying. Below is a comparison for three typical scenarios.
| Scenario | Recommended Model | Benefit |
|---|---|---|
| Sentiment classification (thousands of requests/min) | GPT-4o-mini | Cost reduction by 8x vs GPT-4o |
| Code generation with verification | o3-mini (reasoning_effort=high) | 2x more accurate than GPT-4o on complex tasks |
| Multimodal document analysis | GPT-4o | Only model with native vision |
How We Configure the Client and Handle Errors
We use the official openai SDK and Pydantic for schemas. We wrap all calls in retry with exponential backoff (tenacity) — on 429 or 5xx we wait with increasing pause. Below is a working example for synchronous and asynchronous modes:
from openai import OpenAI, AsyncOpenAI from pydantic import BaseModel client = OpenAI() # Uses OPENAI_API_KEY from env async_client = AsyncOpenAI() # Synchronous call with retry from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10)) def chat(prompt: str, model: str = "gpt-4o") -> str: response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}], temperature=0.1, ) return response.choices[0].message.content # Structured output class Extraction(BaseModel): name: str amount: float currency: str def extract_structured(text: str) -> Extraction: response = client.beta.chat.completions.parse( model="gpt-4o", messages=[{"role": "user", "content": f"Extract data: {text}"}], response_format=Extraction, ) return response.choices[0].message.parsed # Streaming def stream_response(prompt: str): with client.chat.completions.stream( model="gpt-4o", messages=[{"role": "user", "content": prompt}], ) as stream: for chunk in stream.text_stream: yield chunk # Vision (GPT-4o) def analyze_image(image_url: str, question: str) -> str: response = client.chat.completions.create( model="gpt-4o", messages=[{ "role": "user", "content": [ {"type": "image_url", "image_url": {"url": image_url}}, {"type": "text", "text": question} ] }] ) return response.choices[0].message.content Why It's Important to Structure Responses?
Without a schema, the API returns free text — hard to parse and validate. We use response_format with Pydantic to guarantee format. This cuts backend processing time and eliminates parsing errors. An example for entity extraction is shown above.
How o1/o3 Work for Reasoning Tasks?
These models do not support system prompt, temperature (fixed), or streaming. However, you can adjust reasoning_effort (low/medium/high). We use them for narrow tasks: code verification, proofs, logical chains. Example:
# o1 does not support system prompt, temperature, streaming def reason_with_o1(problem: str) -> str: response = client.chat.completions.create( model="o3-mini", messages=[{"role": "user", "content": problem}], reasoning_effort="high", ) return response.choices[0].message.content Embeddings and Semantic Search
For RAG systems, we use text-embedding-3-small (1536 dimensions). It's cheap and effective. We store vectors in Qdrant or pgvector. Example:
def get_embeddings(texts: list[str]) -> list[list[float]]: response = client.embeddings.create( model="text-embedding-3-small", input=texts, ) return [item.embedding for item in response.data] Typical Mistakes When Integrating OpenAI API
- Incorrect handling of rate limits: without retry exponential backoff, the client crashes on 429.
- Lack of token monitoring — unexpected bills.
- Using system prompt for o1/o3 — the model ignores it.
- Storing embeddings in a suboptimal DB — high search latency.
What's Included in a Turnkey Solution
- Client setup with retries, logging, and monitoring (including alerts on p99 latency).
- Selection of the optimal model for each task (cost/quality).
- Implementation of structured outputs with Pydantic.
- Integration of embeddings and vector DB (RAG).
- API documentation and team training.
- Uptime guarantee of 99.9% (our responsibility). Over 50 projects in 5 years — statistics you can trust.
Timeline and How to Get Started
- Basic chat completions integration: 0.5–1 day.
- Structured outputs + tools: 2–3 days.
- Retry logic + cost management: 1–2 days.
- Full RAG pipeline: up to 5 days.
Contact us — we will assess your project in 1 hour. We guarantee transparent code and full documentation. Get a consultation right now.
— (\text{Learn more about } \text{chain-of-thought} \text{ and } \text{Official OpenAI API docs}.)







