~/TechPurAI
~/tutorials/ai-projects-with-python/build-an-ai-rest-api-with-fastapi
intermediate·part 6 of 22·4 min read

Build an AI REST API with FastAPI

Updated Aug 16, 2026Python · AI

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

bash
pip install fastapi uvicorn anthropic python-dotenv

FastAPI 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

python
# 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: str

Pydantic 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

python
@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.

Why it matters

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

bash
uvicorn main:app --reload
text
Visiting 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 hand

This 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

python
@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

text
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.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous5. Build an AI Chatbot with Djangonext →7. Adding Authentication and Rate Limiting to Your AI API