Generative AI & LLM

Module 3 of 16

Module 03: Working with LLM APIs

4 min read741 words
What you'll learn
Integrate OpenAI, Anthropic Claude, and Google Gemini APIs into Python applicationsImplement streaming responses for real-time user experiencesUse function calling / tool use to extend LLM capabilitiesHandle errors, rate limits, and retries gracefullyEstimate and optimize API costs across providers

Duration: 4 hours | Difficulty: Intermediate | Prerequisites: Modules 01–02

Learning Objectives

By the end of this module, you will be able to:

  • Integrate OpenAI, Anthropic Claude, and Google Gemini APIs into Python applications
  • Implement streaming responses for real-time user experiences
  • Use function calling / tool use to extend LLM capabilities
  • Handle errors, rate limits, and retries gracefully
  • Estimate and optimize API costs across providers

1. The LLM API Landscape

Why Multiple Providers?

Nobody wins every benchmark forever. In production you might pick by latency, price, context, compliance, or modalities (vision, audio).

ProviderOften strongest forCost vibe
OpenAI (GPT family)Tooling, dev experience, codingMid (varies by model)
Anthropic (Claude)Long docs, nuanced writingMid
Google (Gemini)Multimodal + Google stackLow–mid on some SKUs
Open-weight / self-hostPrivacy, controlInfra + people time

Try This! Make a one-page “model card” for your app: allowed models, max tokens, PII rules, fallback provider.

2. OpenAI API Deep Dive

Basic Chat Completion

Most tutorials start here: messages is an ordered transcript; temperature and max_tokens shape behavior.

FieldRole
modelWhich weights + tokenizer policy
messagesSystem / user / assistant (+ tool) turns
stream=TrueSSE-style token stream for UX
toolsJSON schemas the model may call

Key Example: Non-streaming chat with usage fields you’ll log for cost dashboards.

python
[object Object], openai ,[object Object], OpenAI

client = OpenAI(api_key=,[object Object],)

response = client.chat.completions.create(
    model=,[object Object],,
    messages=[
        {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
        {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
    ],
    temperature=,[object Object],,
    max_tokens=,[object Object],,
    top_p=,[object Object],,
)

,[object Object],(response.choices[,[object Object],].message.content)
,[object Object],(,[object Object],, response.usage.total_tokens)
,[object Object],(,[object Object],, response.choices[,[object Object],].finish_reason)

Streaming (Conceptual)

With stream=True, you iterate chunks and print partial text—chat UIs feel alive. You still concatenate chunks server-side if you need the final string for storage.

Streaming winStreaming watch-out
Perceived speedYou must handle partial JSON carefully
Early cancelMore client logic

3. Anthropic Claude API

Claude’s Python SDK uses messages.create with system as a top-level string (not always a message with role system in the older style).

PieceNotes
max_tokensRequired cap on assistant output
messagesAlternating user/assistant content blocks
StreamingIterator over events; same mental model as OpenAI

Fun Fact: Providers disagree on tiny details (content blocks vs plain strings)—wrappers like LiteLLM exist because of that friction.

4. Google Gemini API

Gemini often starts with GenerativeModel(model_name) then generate_content or a start_chat session object for multi-turn state.

ModeWhen
Single-shot generate_contentOne question, one answer
Chat sessionConversation with history managed for you
MultimodalPass image/audio parts alongside text (check current API)

5. Universal LLM Client

Why teams build a thin wrapper

BenefitWhat you hide
Swap models in configAuth, retries, logging
Uniform return typeToken counts + text + raw
Cost estimationMultiply usage × price table

You don’t need abstraction on day one—add it when you have two providers or two environments.

6. Error Handling and Retry Logic

Typical production errors:

ErrorFirst move
429 rate limitExponential backoff + jitter
5xx / timeoutRetry with cap
400 bad requestLog body; fix prompt/schema
Broken tool JSONRepair loop or re-ask model

Key Example: Exponential backoff on rate limits—pattern is the same across SDKs; swap the client call for Anthropic/Gemini.

python
[object Object], time
,[object Object], openai ,[object Object], OpenAI, RateLimitError, APIConnectionError, APITimeoutError

client = OpenAI(api_key=,[object Object],)

,[object Object], ,[object Object],(,[object Object],):
    ,[object Object], attempt ,[object Object], ,[object Object],(max_retries):
        ,[object Object],:
            ,[object Object], client.chat.completions.create(
                model=model,
                messages=messages,
                timeout=,[object Object],,
            )
        ,[object Object], (RateLimitError, APIConnectionError, APITimeoutError):
            ,[object Object], attempt == max_retries - ,[object Object],:
                ,[object Object],
            time.sleep(base_delay * (,[object Object],**attempt))


resp = chat_with_retry(
    [{,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}]
)
,[object Object],(resp.choices[,[object Object],].message.content)

7. Cost Optimization Strategies

LeverIdea
Model routingCheap model for triage; expensive for hard cases
Prompt compressionDrop decorative prose in system prompts
CacheHash(prompt+model) for idempotent answers (careful with PII)
Batch where supportedFewer HTTP envelopes
Anti-patternWhy it hurts
Giant pasted context “just in case”Linear $ and latency
Max tokens sky-high alwaysPay for completion headroom you don’t use

Key Takeaway

Treat tokens like cloud spend: log per route, alert on spikes, cap per user.

Practice Exercises

LevelTask
BeginnerSame 3 prompts to 2 providers; compare latency + vibe
IntermediateTerminal chat with streaming + /model switch
IntermediateOne tool (get_time) wired through function calling
AdvancedAsync parallel calls; pick fastest answer above quality bar

Mini-Project: LLM API Gateway

FastAPI service: /chat accepts {model, messages} → routes to provider, caches deterministic prompts, /stats returns spend estimates. Start with OpenAI only; add a second provider behind the same response schema.

Key Takeaways

Key Takeaway

  • Chat APIs are mostly the same story: messages + sampling knobs + usage metadata.
  • Streaming and tools are product features, not academic extras.
  • Retries belong in your client layer, not copy-pasted per call site.
  • Abstract only after pain is real (2+ providers or environments).
  • Observability (tokens, latency, errors) is how you keep bills honest.

Resources for Further Learning

← Previous: Prompt Engineering | Next: Prompt Chaining →