BytesAI
ChatGPT

Building With the OpenAI API: What I Learned Shipping Three Products

After shipping three production applications on the OpenAI API, the lessons that stick are not about prompts or models — they are about rate limits, cost control, and the things that break under real traffic.

Abid Zaidi4 min read
#chatgpt#openai-api#production#architecture#developer-tools
Building With the OpenAI API: What I Learned Shipping Three Products

The gap between "works in a notebook" and "runs in production" is larger for LLM applications than for most software. The failure modes are different, the economics are different, and the debugging experience is genuinely strange in ways you do not anticipate until you are in it.

After shipping three products on the OpenAI API — a code review tool, a document summarizer, and a conversational onboarding flow — these are the lessons that stayed.

Model selection is a cost decision first

The instinct when building is to reach for the most capable model. In production, that instinct is expensive and usually wrong. The models differ meaningfully in capability for hard tasks, but for the majority of LLM features — extraction, summarization, classification, straightforward generation — a faster and cheaper model is good enough.

My current default:

  • GPT-4o mini for high-volume, lower-stakes calls: extraction, classification, summarizing short documents, structured output from well-defined inputs.
  • GPT-4o for reasoning-heavy tasks: code generation, complex synthesis, anything where errors are expensive.

The cost difference between these can be 20x. A feature that is trivially affordable at 1,000 requests/day becomes a budget problem at 100,000. Design for the target scale from the start, not from the prototype.

Streaming is not optional for user-facing features

A non-streaming response from a large model takes 5–15 seconds. Users read that as broken. Streaming starts showing content within 1–2 seconds, and the perceived performance is entirely different even if the total time is the same.

typescript
import OpenAI from "openai";
 
const client = new OpenAI();
 
const stream = await client.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: prompt }],
  stream: true,
});
 
for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content ?? "";
  process.stdout.write(delta); // or send to client via SSE
}

For anything a user watches in real time, streaming is table stakes. The engineering cost is low and the UX difference is large.

Rate limits will surprise you

The OpenAI API enforces limits on requests per minute (RPM) and tokens per minute (TPM). These limits vary by tier and model, and they are lower than you expect at the start. A feature that works fine in testing will start returning 429s under real concurrency.

What actually helps:

  • Implement retry with exponential backoff. The official SDKs have this built in (maxRetries option), but check that it is configured correctly for your use case.
  • Use a queue for non-interactive workloads. Batch jobs — processing uploads, running background analysis — should go through a queue with rate-limit-aware workers rather than calling the API in a tight loop.
  • Monitor your usage dashboard. Set up alerts before you hit your rate limit, not after. A 429 storm from a legitimate traffic spike is nearly indistinguishable from a runaway bug.

Costs need active monitoring

LLM API costs are harder to reason about than database or compute costs because they depend on the content of requests — token counts vary per user, per prompt, per document. You cannot budget from first principles alone.

What works: log token counts on every call, aggregate daily by feature, and set a billing alert in the OpenAI dashboard at 80% of your expected monthly budget. The alert will fire once, teach you which feature is expensive, and pay for itself.

The expensive surprises I have seen: system prompts that were much longer than they needed to be (counted on every request), conversation history that grew unbounded over a session, and a batch job that accidentally sent full documents instead of summaries.

Evals are not optional at scale

The thing that makes LLM bugs hard is that they are statistical, not deterministic. A prompt change that improves average output quality can make a specific edge case worse in ways you will not see until a user reports it.

Even a simple eval suite — fifty representative inputs with expected output shapes or criteria — catches regressions before they ship. Build it early, before the codebase is large enough that changing a prompt feels risky.

The thing that does not change

The API itself is stable and well-designed. The client libraries are solid. The fundamentals — completion, structured output, tool use, streaming — are reliable enough to build on. The hard parts of building LLM applications are the same hard parts as building any other software: cost, reliability, and the feedback loop that tells you when something breaks.

Getting More From ChatGPT With Projects
ChatGPT
ChatGPT Projects changed how I use the tool daily — persistent instructions, shared context across conversations, and a much cleaner way to separate different kinds of work.
4 min read
ChatGPT for Developers: Getting Real Work Done
ChatGPT
Beyond autocomplete — how to use ChatGPT as a genuine engineering tool for debugging, design review, and grinding through the tedious parts of a project without shipping subtle bugs.
3 min read
How I Actually Use LLMs Day to Day as a Developer
AI Coding
A grounded look at where large language models earn their keep in a working developer's day — the tasks worth delegating, the ones worth keeping, and the workflow that ties them together.
3 min read