Technical Guide

How to Integrate LLMsinto Applications

From the first API call to streaming, function calling, RAG, and cost management in production. A complete technical guide for 2026, with examples in Python and JavaScript.

IntermediatePythonJavaScriptREST APIRAGProduction

Prerequisites

Basic knowledge of Python or JavaScript
Account at OpenAI, Anthropic, or Google (API key)
Familiarity with HTTP and REST APIs
01

Choose Your Model and Provider

The first decision affects cost, quality, and latency. For most cases: start with a mid-tier model (GPT-4o-mini, Claude Haiku, Gemini Flash) and move up to frontier only if quality is insufficient.

CriterionChoice
Maximum qualityGPT-4o, Claude Sonnet 4, Gemini 2.5 Pro
Cost-efficiencyGPT-4o-mini, Claude Haiku, Gemini Flash
Speed (real-time chatbot)Groq (Llama), Claude Haiku, Gemini Flash
Privacy / on-premisesLlama 3.1, Mistral, Qwen 2.5 via Ollama
Long context (200K+)Claude Sonnet, Gemini 2.5 Pro
Code & programmingClaude Sonnet, GPT-4o, DeepSeek Coder

Production tip: use LiteLLM as an abstraction layer. Write the code once and swap models by changing a single environment variable. Saves rewrites when pricing or quality shifts.

02

Basic API Call

Every integration starts with a simple call. The pattern is the same across all major providers: messages with roles (system, user, assistant).

Python
from openai import OpenAI

client = OpenAI(api_key="your-api-key-here")

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "Explain what RAG is in 2 paragraphs."
        }
    ],
    max_tokens=500,
    temperature=0.7,
)

print(response.choices[0].message.content)
print(f"Tokens used: {response.usage.total_tokens}")
JavaScript / Node.js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const response = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [
    {
      role: "system",
      content: "You are a helpful assistant.",
    },
    {
      role: "user",
      content: "Explain what RAG is in 2 paragraphs.",
    },
  ],
  max_tokens: 500,
  temperature: 0.7,
});

console.log(response.choices[0].message.content);
console.log(`Tokens: ${response.usage.total_tokens}`);
03

Streaming for Responsive UX

With streaming, tokens appear as they are generated — just like ChatGPT. Perceived speed improves dramatically without reducing total processing time.

Python — Streaming
with client.chat.completions.stream(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Write a poem about AI"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)  # prints token by token
Next.js — API Route with Streaming (Edge Runtime)
// app/api/chat/route.ts
import { OpenAIStream, StreamingTextResponse } from "ai"; // Vercel AI SDK
import OpenAI from "openai";

export const runtime = "edge";

const openai = new OpenAI();

export async function POST(req: Request) {
  const { messages } = await req.json();

  const response = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    stream: true,
    messages,
  });

  const stream = OpenAIStream(response);
  return new StreamingTextResponse(stream);
}
04

Function Calling

Function calling lets the LLM decide when and how to call functions you define. It is the foundation of agents — the model can query external data, call APIs, and execute actions.

Python — Function Calling
import json

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_model_price",
            "description": "Retrieves the price of an LLM model by name",
            "parameters": {
                "type": "object",
                "properties": {
                    "model_name": {
                        "type": "string",
                        "description": "Model name, e.g. gpt-4o-mini"
                    }
                },
                "required": ["model_name"]
            }
        }
    }
]

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "How much does GPT-4o cost?"}],
    tools=tools,
    tool_choice="auto",
)

if response.choices[0].finish_reason == "tool_calls":
    tool_call = response.choices[0].message.tool_calls[0]
    args = json.loads(tool_call.function.arguments)
    # Execute the real function here
    result = get_model_price(args["model_name"])
    print(f"Model called: {tool_call.function.name}({args})")
05

RAG — Context from Your Own Data

RAG (Retrieval-Augmented Generation) lets the LLM answer based on your data without fine-tuning. You retrieve relevant chunks from a vector store and inject them into the request context.

Python — Simple RAG with OpenAI Embeddings
import numpy as np

# 1. Generate embedding for the user's question
def get_embedding(text: str) -> list[float]:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

# 2. Retrieve relevant chunks (simplified — use pgvector/Pinecone in prod)
def search_context(question: str, documents: list[dict]) -> str:
    q_emb = get_embedding(question)

    # Compute cosine similarity with each document
    scores = []
    for doc in documents:
        score = np.dot(q_emb, doc["embedding"]) / (
            np.linalg.norm(q_emb) * np.linalg.norm(doc["embedding"])
        )
        scores.append((score, doc["text"]))

    # Return top-3 most relevant
    top3 = sorted(scores, reverse=True)[:3]
    return "\n---\n".join([t for _, t in top3])

# 3. Inject context into the prompt
question = "How much does GPT-4o cost?"
context = search_context(question, documents)

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {
            "role": "system",
            "content": f"""Answer ONLY based on the context below.
If the answer is not in the context, say you don't know.

CONTEXT:
{context}"""
        },
        {"role": "user", "content": question}
    ]
)
print(response.choices[0].message.content)
Deep dive: RAG vs Fine-Tuning — when to use each →
06

Caching and Cost Control

In production, caching is the most effective tool for cutting costs. Identical or very similar requests do not need to reach the LLM.

Python — Simple cache with Redis
import hashlib
import redis
import json

r = redis.Redis(host="localhost", port=6379, db=0)

def llm_with_cache(messages: list, model: str = "gpt-4o-mini", ttl: int = 3600) -> str:
    # Generate deterministic cache key
    cache_key = hashlib.sha256(
        json.dumps({"model": model, "messages": messages}, sort_keys=True).encode()
    ).hexdigest()

    # Check cache
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)["content"]

    # Call LLM
    response = client.chat.completions.create(model=model, messages=messages)
    content = response.choices[0].message.content

    # Save to cache
    r.setex(cache_key, ttl, json.dumps({"content": content}))
    return content

Cost Reduction Strategies

  • Cache frequent responses (Redis, Upstash)
  • Native prompt caching (Anthropic, OpenAI) for long system prompts
  • Smaller model for triage, larger only for complex cases
  • Limit max_tokens to the minimum needed
  • Compress long conversation histories

Production Monitoring

  • Log input/output tokens per request
  • Per-user daily budget cap (auto-block when hit)
  • Alerts when daily cost exceeds threshold
  • LangFuse or Helicone for detailed observability
  • Cost dashboard per feature/endpoint

Production Checklist

API key in environment variables (never hardcoded)
Per-user rate limiting implemented
Timeout configured (prevent hanging requests)
Retry with exponential backoff for 429/500 errors
Daily per-user budget cap
Logging of input/output tokens per request
Output validation before displaying to user
Model version pinned (avoid breaking changes)
Fallback to an alternative model if primary is unavailable
Sensitive data never sent to an LLM without a DPA

Frequently Asked Questions

What is the difference between calling the API directly and using a framework like LangChain?
The direct API gives you more control, lower overhead, and fewer dependencies — ideal for simple use cases. LangChain and similar frameworks abstract common patterns (RAG, agents, memory) and speed up development of complex systems. Recommendation: start with the direct API. If you need complex agents or elaborate RAG pipelines, evaluate frameworks. Avoid premature abstraction.
How do I calculate the cost of an LLM in production?
Cost = (input_tokens × input_price/1M) + (output_tokens × output_price/1M). Use tiktoken (Python) or js-tiktoken to estimate tokens before sending. For GPT-4o: ~$2.50/1M input + $10/1M output. For Claude Haiku: ~$0.25/1M input + $1.25/1M output. Multiply by your expected request volume and add a 20% buffer for system tokens and formatting.
How do I prevent users from abusing my LLM integration?
Implement: (1) mandatory authentication before any call; (2) per-user rate limiting (e.g., 10 requests/minute); (3) per-request token limits; (4) daily per-user budget cap — block automatically when reached; (5) real-time cost monitoring with alerts. Never expose your API key in the frontend.
Is it worth fine-tuning or should I use RAG?
RAG first, always. Fine-tuning is expensive (time + compute + labeled data), hard to keep current, and does not solve hallucination. RAG dynamically injects retrieved context — easier to update and debug. Use fine-tuning only for: adapting the model's style/tone, improving performance on a specific output format (structured JSON, niche code), or reducing latency with smaller specialized models.
How do I choose between streaming and a full response?
Use streaming for user interfaces (chatbots, text editors) — perceived speed improves dramatically even without reducing total latency. Use a full response for: batch processing (no user waiting), when you need to parse the entire JSON response before acting, or when total latency is lower than streaming overhead.

Related content: