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.
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.
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
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.
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." )
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.
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)])
Want live instruction and hands-on projects? Join the AI bootcamp — 3 days, 5 cities.
Before moving on, confirm understanding of these key concepts: