Build an AI REST API with FastAPI
Parts 2 through 5 built AI features that render their own real HTML. This part covers a genuinely different, API-first pattern — a real, standalone AI REST API for GreenDesk, the B2B SaaS business referenced throughout this site's marketing and content series, meant to be called by other applications rather than viewed directly in a browser.
Real, practical setup
pip install fastapi uvicorn anthropic python-dotenvFastAPI is a real, modern Python framework built specifically for APIs — genuinely different in purpose from Django (a full web framework) or Flask (a minimal general-purpose one), with built-in real request validation and automatic documentation as core, first-class features.
Real, validated request and response models
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
from shared.ai_client import get_client
app = FastAPI(title="GreenDesk AI API")
class LeadQualificationRequest(BaseModel):
company_size: str
use_case: str
budget_range: str
class LeadQualificationResponse(BaseModel):
qualified: bool
reasoning: strPydantic models like these define the real, exact shape of a valid request and response — FastAPI automatically validates every incoming real request against LeadQualificationRequest, rejecting a malformed one with a clear, real error before it ever reaches your actual AI logic.
The real, complete endpoint
@app.post("/qualify-lead", response_model=LeadQualificationResponse)
def qualify_lead(request: LeadQualificationRequest):
client = get_client()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=512,
system=(
"You qualify B2B leads for GreenDesk, a project-management "
"SaaS for teams of 10-200. A qualified lead has a real "
"budget over $500/mo and a genuine team-coordination use "
"case. Respond with a clear qualified/not-qualified "
"decision and one sentence of real reasoning."
),
messages=[
{
"role": "user",
"content": (
f"Company size: {request.company_size}\n"
f"Use case: {request.use_case}\n"
f"Budget range: {request.budget_range}"
),
}
],
)
# a real, simplified parse — production code would use structured
# output (part 12's approach) rather than parsing free text
text = response.content[0].text
qualified = "qualified" in text.lower() and "not qualified" not in text.lower()
return LeadQualificationResponse(qualified=qualified, reasoning=text)This is a real, concrete B2B use case — GreenDesk's actual sales team calling this API from their CRM to get an immediate, AI-assisted qualification signal on an incoming lead, rather than a person manually reviewing every submission.
response_model=LeadQualificationResponse does real, genuine work beyond documentation — FastAPI validates the actual response against this model too, so a bug that accidentally returns the wrong real shape of data fails loudly during development rather than silently shipping a malformed response to whatever real application is calling this API.
Real, free API documentation
uvicorn main:app --reloadVisiting http://localhost:8000/docs shows a real, interactive,
auto-generated API documentation page — built directly from the
Pydantic models above, with zero separate documentation written
by handThis is a genuine, practical advantage over the Django REST Framework series' own separate OpenAPI documentation step — FastAPI generates this real, interactive documentation automatically from the same type-annotated code already written for validation, rather than requiring an additional, separate documentation-generation step.
Real, async request handling
@app.post("/qualify-lead", response_model=LeadQualificationResponse)
async def qualify_lead(request: LeadQualificationRequest):
client = get_client()
response = await client.messages.create(...)
# ...FastAPI supports real async def endpoints natively — genuinely useful for an AI API specifically, since a real LLM API call can take several real seconds, and an async endpoint lets the server handle other real, concurrent requests while waiting, rather than blocking entirely on one slow request the way a synchronous endpoint would. If what async/await is actually doing while that request waits isn't already familiar, this walkthrough explains the same underlying pause-and-resume model using JavaScript's version of the syntax — the mental model transfers directly to Python's async/await, even though the code examples there aren't Python.
Why FastAPI, specifically, for this real use case
Django (part 5): genuinely well-suited to a full application with
real HTML rendering, sessions, and an admin interface
FastAPI (this part): genuinely well-suited to a focused, real API
with no HTML rendering at all — just structured, validated data in
and out, consumed by another real application (GreenDesk's CRM)This isn't a "FastAPI is better" claim — it's a real, deliberate match between tool and task, the same principle covered when the CSS series discussed combining Grid and Flexbox rather than forcing one tool to handle everything.
Next: adding real authentication and rate limiting to this AI API — genuine production concerns for an endpoint that costs real, per-token money on every call.