Courses Curriculum Cities Blog Enroll Now
API Development for AI · Day 2 of 5 ~40 minutes

Day 2: Request Validation, Models, and Error Handling

Real APIs fail gracefully. They reject bad input clearly, return meaningful error messages, and never expose internal errors to callers. Build these habits from day one.

1
Day 1
2
Day 2
3
Day 3
4
Day 4
5
Day 5
What You'll Build

A fully validated API with Pydantic models for every endpoint, custom error responses with clear messages, and input sanitization that prevents the most common API failures.

1
Section 1 · 10 min

Pydantic Models: Your First Line of Defense

Pydantic models define what data your API accepts. FastAPI uses them to automatically validate incoming requests and return clear errors when data is wrong — before your code even runs.

pythonmodels.py
from pydantic import BaseModel, Field, validator
from typing import Literal

class TextRequest(BaseModel):
    text: str = Field(
        min_length=1,
        max_length=10000,
        description="The text to process"
    )
    tone: Literal["formal", "casual", "technical"] = "formal"

    @validator("text")
    def strip_whitespace(cls, v):
        stripped = v.strip()
        if not stripped:
            raise ValueError("text cannot be only whitespace")
        return stripped

class APIResponse(BaseModel):
    result: str
    model: str
    tokens_used: int
2
Section 2 · 15 min

HTTP Status Codes and Error Responses

Every HTTP response has a status code. 200 means success. 400 means the caller made a mistake. 500 means something broke on your end. Using the right codes makes your API usable by other developers.

pythonmain.py
from fastapi import FastAPI, HTTPException, status
import anthropic

app = FastAPI()
client = anthropic.Anthropic()

@app.post("/analyze", status_code=status.HTTP_200_OK)
def analyze(req: TextRequest):
    try:
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            messages=[{"role": "user", "content": req.text}]
        )
        return {"result": response.content[0].text}

    except anthropic.AuthenticationError:
        raise HTTPException(
            status_code=503,
            detail="AI service configuration error"
        )
    except anthropic.RateLimitError:
        raise HTTPException(
            status_code=429,
            detail="Rate limit exceeded. Try again in a moment."
        )
3
Section 3 · 15 min

API Keys and Authentication

Your API needs its own authentication — callers need to prove who they are before using it. The simplest approach: API key in the request header.

pythonauth.py
from fastapi import Header, HTTPException
import os

VALID_API_KEYS = {os.getenv("APP_API_KEY")}

def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key not in VALID_API_KEYS:
        raise HTTPException(
            status_code=401,
            detail="Invalid API key"
        )

# Use as dependency on protected routes:
# @app.post("/analyze", dependencies=[Depends(verify_api_key)])

What You Learned Today

  • How Pydantic models validate input before your handler code runs
  • The key HTTP status codes: 200 (ok), 400 (bad request), 401 (unauthorized), 429 (rate limit), 500 (server error)
  • How to catch specific Anthropic API errors and return appropriate status codes
  • How to add API key authentication using FastAPI's dependency injection
Your Challenge

Go Further on Your Own

  • Add a custom exception handler that catches all unhandled exceptions and returns a clean 500 error without leaking stack traces
  • Add request ID tracking: generate a UUID per request and include it in every response. This is essential for debugging production issues.
  • Add rate limiting: allow a maximum of 10 requests per minute per API key using a simple in-memory dictionary (for production you'd use Redis)
Day 2 Complete

Nice work. Keep going.

Day 3 is ready when you are.

Continue to Day 3
Course Progress
40%

Want live instruction and hands-on projects? Join the AI bootcamp — 3 days, 5 cities.

Finished this lesson?