If you want to add AI features to a web application—chat, summarization, classification, or code generation—the OpenAI API is one of the most direct ways to do it. This tutorial walks through the full journey: getting an API key, making your first request, handling responses correctly, streaming output, and avoiding the mistakes that most commonly break production deployments. By the end, you will have a working integration pattern you can adapt to almost any AI feature.
The OpenAI API is a plain HTTPS JSON interface, which means you can call it from any backend language. The examples below use JavaScript with the official OpenAI SDK, but the concepts translate directly to Python, Go, or any HTTP client.
Prerequisites and Setup
Before writing any code, you need three things:
- An OpenAI account with billing enabled or free credits available.
- An API key, generated from the API keys page in your dashboard. Treat this key like a database password.
- A runtime environment—Node.js 18+ is a good choice because it has
fetchbuilt in.
The single most common beginner mistake is embedding the API key in frontend code. Anything shipped to a browser is public. Your key must live on the server, in an environment variable, and every AI request should pass through your own backend first. This is not just a security nicety: it is also the only place you can enforce rate limits, logging, and cost controls.
Create a minimal Node project and install the SDK:
npm init -y
npm install openai dotenv
All the code examples in this tutorial use ES module syntax (import statements) and top-level await, which requires ESM. Open the generated package.json and add a "type": "module" entry (or, alternatively, name your files with a .mjs extension):
{
"type": "module"
}
Then store your key in a .env file (and add .env to .gitignore immediately):
OPENAI_API_KEY=sk-your-key-here
Making Your First API Call
The core concept of the OpenAI API is the chat completion: you send an array of messages, each with a role (system, user, or assistant), and the model returns a continuation. The system message sets behavior and tone; user messages carry the actual input.
Here is a complete, minimal example:
import OpenAI from "openai";
import "dotenv/config";
const client = new OpenAI(); // reads OPENAI_API_KEY from env
const response = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "You are a concise technical assistant." },
{ role: "user", content: "Explain HTTP status codes in three sentences." }
],
max_completion_tokens: 200,
temperature: 0.3
});
console.log(response.choices[0].message.content);
A few parameters deserve attention because they change results dramatically:
temperaturecontrols randomness. Use low values (0–0.3) for factual tasks like extraction or classification, and higher values (0.7+) for creative generation. Note that some newer models only support the default temperature of 1, so check the model documentation if a call is rejected.max_completion_tokenscaps output length. This parameter replaced the oldermax_tokensin the chat completions API for current models. Tokens are not words—a rough rule of thumb is that one token is about three-quarters of an English word. Setting this too low truncates responses mid-sentence.modeldetermines cost, speed, and capability. Smaller models are dramatically cheaper and often sufficient for classification or simple formatting; reserve larger models for complex reasoning.
The response object contains more than the text: it includes a usage field with prompt and completion token counts. Log this from day one, because it is the only reliable way to understand your actual spend per request.
Streaming Responses for Better UX
For chat interfaces, waiting several seconds for a full response feels broken. Streaming sends tokens as they are generated, so users see text appear progressively. The SDK supports this with a stream parameter:
const stream = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Write a short release note." }],
stream: true
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
In a web app, the typical pattern is to stream from your backend to the browser using Server-Sent Events or a readable stream over fetch. One caveat: streaming complicates error handling. If the stream fails midway, you may have already sent partial content to the client, so design your frontend to handle incomplete responses gracefully rather than assuming every stream ends cleanly.
Building a Small API Endpoint
The examples below use Express, so install it first if you have not already:
npm install express
Here is a realistic Express endpoint that wraps the API safely—server-side key, input validation, and error handling:
import express from "express";
import OpenAI from "openai";
const app = express();
app.use(express.json());
const client = new OpenAI();
app.post("/api/summarize", async (req, res) => {
const { text } = req.body;
if (!text || typeof text !== "string" || text.length > 10000) {
return res.status(400).json({ error: "Provide text under 10,000 characters." });
}
try {
const completion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "Summarize the following text in two sentences." },
{ role: "user", content: text }
],
temperature: 0.2
});
res.json({ summary: completion.choices[0].message.content });
} catch (err) {
console.error(err);
res.status(502).json({ error: "AI service unavailable. Try again shortly." });
}
});
app.listen(3000);
Note the error handling. The API can return rate-limit errors (HTTP 429), authentication errors, or transient server errors. In production, implement exponential backoff with retries for 429 and 5xx responses—the SDK has built-in retries you can configure, which is usually enough. Never expose raw API error messages to users; they can leak details about your account or usage.
Common Mistakes and Production Considerations
Several failure patterns account for most broken AI integrations:
Trusting model output blindly. Language models can produce plausible but incorrect information. If your feature feeds a database, sends emails, or executes code, validate and constrain the output. For structured data, use the API's JSON output options or validate responses with a schema library like Zod before acting on them.
Ignoring context limits. Every model has a maximum context window. If you pass entire documents or long conversation histories, you will eventually exceed it. Truncate, summarize older turns, or retrieve only relevant chunks of content rather than stuffing everything into the prompt.
No cost controls. A single runaway loop or abusive user can generate a surprising bill. Set usage limits in your OpenAI dashboard, cap max_completion_tokens, and enforce per-user rate limits in your own backend.
Prompt injection. If user input is combined with instructions in your prompt, users can try to override your system message. For features like document summarization, treat retrieved content as untrusted data and keep behavioral instructions in the system role, clearly separated.
Skipping evaluation. Prompt changes can silently degrade quality. Keep a small set of test inputs and expected behaviors, and run them whenever you modify a prompt. This costs minutes and prevents embarrassing regressions.
Forgetting timeouts. Model calls can occasionally take much longer than expected. Wrap your API calls in a request timeout appropriate to your feature—if the summarization endpoint above should respond within fifteen seconds, enforce that at the HTTP client or server level and return a clear failure to the user instead of hanging. This protects both your users and your server resources under load.
For deeper details on parameters, error codes, and model capabilities, the official API reference is the authoritative source and is updated more frequently than any tutorial.
Frequently Asked Questions
No. Any code running in a browser is visible to users, including your API key. Always call the API from a server or serverless function, and have the frontend talk to your own backend endpoint. This also gives you a place to enforce authentication, rate limits, and logging.
Pricing is per token, with separate rates for input and output, and it varies by model. Smaller models cost a fraction of larger ones. Check the current pricing page on the OpenAI platform, since rates change over time, and monitor the `usage` field in responses to understand your real-world spend.
Start with a small, fast, inexpensive model such as a mini-tier variant. It handles summarization, classification, and simple chat well. Move to a larger model only when you specifically need stronger reasoning or more nuanced generation, because the cost difference is substantial at scale.
Yes. OpenAI provides an official Python SDK with the same core interface: you create a client, call `chat.completions.create`, and pass a list of messages. The concepts in this tutorial—system prompts, temperature, streaming, and error handling—apply identically.
Both control randomness in output. Temperature reshapes the probability distribution across all tokens, while top_p limits sampling to the most probable tokens whose probabilities sum to a threshold. Adjust one or the other, not both simultaneously, and prefer temperature for most everyday tuning.
Conclusion
Integrating the OpenAI API is genuinely simple at the code level—a client, a message array, and a response object—but building something reliable requires attention to the surrounding engineering: keep keys server-side, validate inputs and outputs, stream for responsiveness, control costs, and treat model output as untrusted data. Start by reproducing the summarize endpoint above in your own stack, then add streaming and a small evaluation set as your feature matures. The pattern scales from a weekend prototype to a production service with surprisingly few structural changes.
Transparency Note: Links in this article are not affiliate links. I recommend these tools and platforms based on their relevance to the topic, and no compensation is received for including them.