ChatGPT API Integration — Complete Developer Guide 2026

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

Introduction

Why This Matters

The ChatGPT API unlocks capabilities the web interface cannot offer: programmatic control over prompts, response streaming, function calling, batch processing, and per-request cost management. For any production application — a code review bot, a documentation generator, a customer support tool — the API is the correct approach. This guide covers everything from your first API call to production-ready patterns.

Account Setup and Authentication

Start at platform.openai.com. Create an account, navigate to API Keys, and generate a key. Store it as an environment variable — never hard-code it in source files.

# .env file (never commit this to git)
OPENAI_API_KEY=sk-proj-...
 
# Python: python-dotenv loads it automatically
# Node.js: dotenv loads it automatically

Install the SDK:

pip install openai           # Python
npm install openai           # Node.js / TypeScript

Your First API Call

from openai import OpenAI
 
# Reads OPENAI_API_KEY from environment automatically
client = OpenAI()
 
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "You are a concise technical assistant."},
        {"role": "user", "content": "Explain what a JWT token is in two sentences."},
    ],
    max_tokens=200,
    temperature=0.3,
)
 
print(response.choices[0].message.content)
print(f"Tokens used: {response.usage.total_tokens}")

The response text lives in choices[0].message.content. Track response.usage.total_tokens — it directly determines your bill.

Multi-Turn Conversations

The OpenAI API is stateless. You maintain conversation history by appending each exchange to the messages list and resending the full array.

from openai import OpenAI
 
client = OpenAI()
 
class CodingAssistant:
    def __init__(self):
        self.messages = [
            {
                "role": "system",
                "content": (
                    "You are a senior Python developer. "
                    "Give concise working code examples. "
                    "Ask for clarification when requirements are ambiguous."
                ),
            }
        ]
 
    def chat(self, user_input: str) -> str:
        self.messages.append({"role": "user", "content": user_input})
 
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=self.messages,
            max_tokens=1024,
            temperature=0.2,
        )
 
        reply = response.choices[0].message.content
        self.messages.append({"role": "assistant", "content": reply})
        return reply
 
assistant = CodingAssistant()
print(assistant.chat("How do I validate an email address in Python?"))
print(assistant.chat("Can you show me how to test that function?"))

Streaming Responses

Streaming returns tokens as they are generated, enabling responsive UIs that do not wait for the full response.

import OpenAI from 'openai';
 
const client = new OpenAI();
 
async function streamCodeExplanation(code: string): Promise<string> {
  const stream = await client.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: 'Explain code clearly, step by step.',
      },
      {
        role: 'user',
        content: `Explain this code:\n\n\`\`\`\n${code}\n\`\`\``,
      },
    ],
    stream: true,
    max_tokens: 800,
  });
 
  let fullResponse = '';
  for await (const chunk of stream) {
    const delta = chunk.choices[0]?.delta?.content ?? '';
    process.stdout.write(delta);
    fullResponse += delta;
  }
 
  return fullResponse;
}

Function Calling

Function calling lets the model invoke actions in your application — querying databases, calling external APIs, or triggering computations — when it decides that data is needed to answer the user.

import json
from openai import OpenAI
 
client = OpenAI()
 
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_package_info",
            "description": "Get version and description of an npm or PyPI package",
            "parameters": {
                "type": "object",
                "properties": {
                    "package_name": {"type": "string"},
                    "registry": {
                        "type": "string",
                        "enum": ["npm", "pypi"],
                    },
                },
                "required": ["package_name", "registry"],
            },
        },
    }
]
 
def get_package_info(package_name: str, registry: str) -> str:
    import urllib.request, json as _json
    if registry == "npm":
        url = f"https://registry.npmjs.org/{package_name}/latest"
    else:
        url = f"https://pypi.org/pypi/{package_name}/json"
    with urllib.request.urlopen(url) as r:
        data = _json.loads(r.read())
    version = data.get("version") or data.get("info", {}).get("version")
    return f"{package_name} @ {version}"
 
def agent_loop(question: str) -> str:
    messages = [{"role": "user", "content": question}]
 
    while True:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto",
        )
 
        choice = response.choices[0]
        if choice.finish_reason != "tool_calls":
            return choice.message.content
 
        messages.append(choice.message)
        for tc in choice.message.tool_calls:
            args = json.loads(tc.function.arguments)
            result = get_package_info(**args)
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": result,
            })
 
print(agent_loop("What is the latest version of the requests library on PyPI?"))

Error Handling and Retries

Build retry logic with exponential backoff for rate-limit and server errors.

import time
from openai import OpenAI, RateLimitError, APIStatusError
 
client = OpenAI()
 
def call_with_retry(messages, model="gpt-4o", max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.chat.completions.create(
                model=model,
                messages=messages,
                max_tokens=1024,
            )
        except RateLimitError:
            wait = 2 ** attempt  # 1s, 2s, 4s
            print(f"Rate limited. Waiting {wait}s (attempt {attempt + 1})...")
            time.sleep(wait)
        except APIStatusError as e:
            if e.status_code >= 500:
                time.sleep(2 ** attempt)
            else:
                raise  # Do not retry client errors (4xx)
    raise RuntimeError(f"API call failed after {max_retries} attempts")

Common Mistakes

  • Hard-coding API keys in source files or committing .env to git
  • Not setting max_tokens — a runaway response can consume expensive tokens
  • Using gpt-4o for tasks where gpt-4o-mini performs equally well (costs ~20x more)
  • Sending the full conversation history without pruning — old messages waste tokens and inflate cost
  • Not handling rate limit errors — applications crash under production load without retry logic

Best Practices

  • Use gpt-4o-mini for high-volume classification, extraction, and summarization tasks
  • Cache responses for repeated identical prompts using a hash of the message array as the cache key
  • Set billing alerts at platform.openai.com before any significant load testing
  • Log token counts and model names per request to identify cost hotspots
  • Limit system prompt size — it is resent on every message in the thread

Key Takeaways

  • The OpenAI API is stateless — you send the full conversation history on each request
  • Streaming (stream=True) returns tokens incrementally for responsive chat UIs
  • Function calling lets the model trigger your application logic when it needs real data
  • gpt-4o-mini costs roughly 20x less than gpt-4o and handles most classification and extraction tasks
  • Rate limit errors (429) require exponential backoff — build retry logic before any production deployment
  • Always set max_tokens to cap response length and control costs
  • Token usage in response.usage should be logged per request for accurate cost attribution
  • Never commit API keys to source control — use environment variables or a secrets manager

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading