Integrating OpenAI into Your SaaS Product: A Practical Guide for Founders
Everyone wants AI in their product. Few people ship it well. Here's how we integrate OpenAI APIs into production SaaS applications — with real patterns, not demos.
The OpenAI API is easy to call. Building a production-grade AI feature on top of it is a different story.
After integrating OpenAI, Claude, and other LLM APIs into multiple client products, we've built up a set of patterns that separate "demo AI feature" from "AI feature that actually ships and stays stable."
What We're Covering
- Choosing the right model for your use case
- Streaming responses to the frontend
- Cost control and token budgeting
- Error handling and fallback behaviour
- Prompt engineering fundamentals
- Caching and rate limiting
- User experience patterns
Choosing the Right Model
The GPT-4 family is powerful but expensive. For most product features, you don't need it.
| Use Case | Recommended Model |
|---|---|
| Short classification tasks, tagging, extraction | gpt-4o-mini or gpt-3.5-turbo |
| Complex reasoning, multi-step tasks | gpt-4o |
| Code generation, technical writing | gpt-4o |
| High-volume, cost-sensitive features | gpt-4o-mini |
| Long context (100k+ tokens) | gpt-4o with 128k context |
Start with gpt-4o-mini for every feature. Only upgrade to gpt-4o if the output quality genuinely matters and users notice the difference. The cost difference is 15–20x.
Streaming Responses
Nothing kills the perceived speed of an AI feature like waiting 8 seconds for a response to appear all at once. Stream it.
Backend (Node.js / Express):
app.post('/api/ai/generate', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const stream = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: req.body.messages,
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content || '';
if (delta) res.write(`data: ${JSON.stringify({ text: delta })}\n\n`);
}
res.write('data: [DONE]\n\n');
res.end();
});
Frontend (React):
const streamResponse = async (messages) => {
const response = await fetch('/api/ai/generate', {
method: 'POST',
body: JSON.stringify({ messages }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const lines = decoder.decode(value).split('\n').filter(Boolean);
for (const line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
const { text } = JSON.parse(line.slice(6));
setOutput(prev => prev + text);
}
}
}
};
Cost Control
AI costs surprise founders. A feature that works fine at 100 users can generate a $3,000 bill at 10,000 users if you haven't budgeted for it.
Track token usage per user/request:
const completion = await openai.chat.completions.create({ ... });
const { prompt_tokens, completion_tokens, total_tokens } = completion.usage;
await db.insert('ai_usage_log', {
user_id: req.user.id,
feature: 'blog-generator',
tokens_in: prompt_tokens,
tokens_out: completion_tokens,
cost_usd: (total_tokens / 1000) * 0.00015, // gpt-4o-mini rate
});
Set hard limits per user/plan:
const usage = await getUserMonthlyTokens(req.user.id);
if (usage > PLAN_LIMITS[req.user.plan]) {
return res.status(429).json({ error: 'Monthly AI limit reached. Upgrade your plan.' });
}
Compress your prompts. Every unnecessary word in your system prompt costs money on every request. Audit and trim prompts regularly.
Error Handling
The OpenAI API returns errors. Rate limits, context length exceeded, model overload, network timeouts. Don't let these crash your product.
try {
const result = await openai.chat.completions.create({ ... });
return result;
} catch (err) {
if (err.status === 429) {
// Rate limit — wait and retry
await sleep(2000);
return retryRequest(params, retries - 1);
}
if (err.status === 400 && err.message.includes('context_length')) {
// Trim input and retry
return retryWithTrimmedContext(params);
}
if (err.status >= 500) {
// OpenAI server error — return cached/fallback response
return fallbackResponse;
}
throw err;
}
Always have a fallback. For most AI features, the fallback is simply showing an error message and letting the user retry. For critical features, consider caching recent successful responses as a fallback.
Prompt Engineering Fundamentals
Most bad AI outputs are caused by bad prompts, not bad models.
Be specific about format:
Bad: "Summarise this article"
Good: "Summarise this article in exactly 3 bullet points. Each bullet should be one sentence. Start each bullet with a strong verb."
Include examples (few-shot prompting):
Extract the company name from this text.
Text: "We worked with Acme Corp on their new CRM system."
Company: Acme Corp
Text: "The client was TechFlow Ltd, based in London."
Company: TechFlow Ltd
Text: {user_input}
Company:
Separate system prompt from user content clearly. Never put user input directly into your system prompt — that's a prompt injection risk.
Caching
Not every AI request needs to hit the API. For deterministic or semi-deterministic features, cache aggressively.
const cacheKey = `ai:${feature}:${hashInput(userInput)}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const result = await openai.chat.completions.create({ ... });
await redis.setex(cacheKey, 3600, JSON.stringify(result)); // 1 hour TTL
return result;
Good candidates for caching: content classification, tag extraction, SEO metadata generation, product descriptions.
Bad candidates: conversational AI, personalised responses, anything where the same input should produce varied output.
UX Patterns That Work
- Skeleton loaders while the first token streams — don't show a blank area
- Word-by-word streaming feels faster than character-by-character
- Regenerate button — users always want to try again
- Edit before use — let users edit AI-generated content before it's saved/sent
- Confidence indicators — for extraction tasks, showing "85% confidence" builds trust
- Explain what it's doing — "Analysing your data..." beats a spinner
Conclusion
AI features are not hard to add, but they're hard to do well at scale. The gap between a weekend demo and a production feature that handles errors, controls costs, respects rate limits, and delights users is significant.
Build the boring parts first: logging, cost tracking, error handling. Then focus on the user experience. The model quality is rarely the limiting factor.
Building an AI-powered product? Get in touch — we've integrated LLM APIs into SaaS products across multiple industries.