Sunday, August 23, 2026
HomeData ScienceFastAPI for Data Scientists – Build & Deploy ML APIs in Python

FastAPI for Data Scientists – Build & Deploy ML APIs in Python

Table of Content

FastAPI is the fastest-growing Python web framework for building APIs, and it’s become the standard choice for data scientists who need to expose machine learning models as REST APIs. It’s fast (async by default), self-documenting (automatic Swagger UI), and catches bugs before they hit production (Pydantic validation). This guide shows you how to go from trained model to deployed API.

Why FastAPI for ML Serving?

Flask was the go-to for ML APIs for years, but FastAPI has overtaken it for several reasons. It’s 2-3× faster than Flask for I/O-bound tasks due to async support. Type annotations and Pydantic automatically validate request data and generate interactive documentation. And it’s production-ready out of the box with ASGI servers like Uvicorn. For any new ML API project in 2026, FastAPI is the right choice.

Installation

pip install fastapi uvicorn pydantic scikit-learn pandas

Your First FastAPI ML API

# app.py
from fastapi import FastAPI
from pydantic import BaseModel, Field
import pickle
import numpy as np

app = FastAPI(title="House Price Predictor", version="1.0")

# Load model at startup
with open("model.pkl", "rb") as f:
    model = pickle.load(f)

class HouseFeatures(BaseModel):
    size_sqft: float = Field(..., gt=0, description="Size in square feet")
    bedrooms: int    = Field(..., ge=1, le=10)
    bathrooms: float = Field(..., ge=1.0)
    age_years: int   = Field(..., ge=0)
    location_score: float = Field(..., ge=0, le=10)

class PredictionResponse(BaseModel):
    predicted_price: float
    confidence_interval: dict

@app.get("/health")
def health_check():
    return {"status": "ok", "model": "house-price-v1"}

@app.post("/predict", response_model=PredictionResponse)
def predict(features: HouseFeatures):
    X = np.array([[features.size_sqft, features.bedrooms,
                   features.bathrooms, features.age_years,
                   features.location_score]])
    price = float(model.predict(X)[0])
    return PredictionResponse(
        predicted_price=round(price, 2),
        confidence_interval={"lower": round(price * 0.92, 2),
                              "upper": round(price * 1.08, 2)})

Running the API

uvicorn app:app --reload --host 0.0.0.0 --port 8000

Open http://localhost:8000/docs for the automatic Swagger UI — you can test your endpoint directly in the browser with no extra setup.

Batch Predictions

from typing import List
import pandas as pd

@app.post("/predict/batch")
def predict_batch(features: List[HouseFeatures]):
    df = pd.DataFrame([f.dict() for f in features])
    prices = model.predict(df.values)
    return {"predictions": [round(float(p), 2) for p in prices],
            "count": len(prices)}

Background Tasks and Async

from fastapi import BackgroundTasks
import logging

def log_prediction(features: dict, prediction: float):
    logging.info(f"Prediction: {prediction} | Input: {features}")

@app.post("/predict/logged")
async def predict_logged(features: HouseFeatures, bg: BackgroundTasks):
    X = np.array([[features.size_sqft, features.bedrooms,
                   features.bathrooms, features.age_years,
                   features.location_score]])
    price = float(model.predict(X)[0])
    bg.add_task(log_prediction, features.dict(), price)  # runs after response
    return {"predicted_price": round(price, 2)}

Model Loading with Lifespan Events

from contextlib import asynccontextmanager

ml_models = {}

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: load model
    with open("model.pkl", "rb") as f:
        ml_models["house_price"] = pickle.load(f)
    yield
    # Shutdown: cleanup
    ml_models.clear()

app = FastAPI(lifespan=lifespan)

This pattern is better than global variables — the model loads once at startup and is available to all route handlers via the shared dict.

Dockerizing the FastAPI App

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
docker build -t house-price-api .
docker run -p 8000:8000 house-price-api

Adding Authentication

from fastapi.security import APIKeyHeader
from fastapi import Security, HTTPException

API_KEY = "your-secret-api-key"
api_key_header = APIKeyHeader(name="X-API-Key")

async def verify_api_key(key: str = Security(api_key_header)):
    if key != API_KEY:
        raise HTTPException(status_code=403, detail="Invalid API key")

@app.post("/predict/secure", dependencies=[Depends(verify_api_key)])
def predict_secure(features: HouseFeatures):
    ...

Conclusion

FastAPI makes building production-ready ML APIs genuinely enjoyable. The automatic Swagger docs reduce your need to write API documentation, Pydantic catches bad input before your model sees it, and async support means you can handle many concurrent requests efficiently. From training to a Dockerized, authenticated API with interactive docs — it’s a few dozen lines of Python.

Leave feedback about this

  • Rating

Durgesh Kekare
Durgesh Kekarehttps://www.dataexpertise.in
Durgesh Kekare is a data science educator and founder of DataExpertise.in. With expertise in Python, machine learning, and analytics, he helps 10,000+ learners break into data careers.

Latest Posts

List of Categories