Create REST Endpoints for Predictions

Learn to create REST endpoints for predictions in this hands-on Python for data science lesson. Build a simple API to serve model outputs, handle requests, and test with curl. Perfect for developers progressing step by step.

Focus: create rest endpoints for predictions

Sponsored

You’ve trained a model that predicts house prices with impressive accuracy. But right now, it only works on your laptop. Your boss asks for a live demo, a colleague wants to integrate it into their app, and marketing wants a quick way to test it. If you don't have a way to expose your model to the world, your work stays in a notebook—valuable but isolated. This lesson shows you how to create REST endpoints for predictions using FastAPI, turning your model into a service that anyone can call over HTTP, and you'll test it with curl in under 15 minutes.

The problem this lesson solves

Data science work often ends with a model saved as a .pkl file, a Jupyter notebook full of visualizations, and a report. But a model that isn't deployable isn't useful. Without an API, every consumer of your predictions must run Python, load the model, and manage dependencies themselves. That's slow, error-prone, and blocks collaboration.

REST endpoints solve this by wrapping your model in a standard web interface. Instead of importing your code, users send an HTTP request with input data and receive the prediction as a JSON response. This decouples your model from its consumers—they don't need to know Python, and you don't need to share your training code.

The pain is real: teams waste days on integration because there's no clean boundary between the model and the application. You'll also face issues like versioning (which model version is live?), latency (predictions taking too long), and security (who can call this endpoint?). This lesson gives you the foundational pattern to address these concerns.

Core concept / mental model

Think of a REST endpoint as a waiter in a restaurant. You (the client) sit at the table with a menu (the API documentation). You order a dish (send an HTTP request with parameters). The waiter conveys your order to the kitchen (the model), which prepares the dish (runs the prediction), and the waiter brings it back to you (the response). The waiter doesn't care how the kitchen works—they just need a consistent way to pass orders and return plates.

In technical terms, a REST API defines a set of resources (e.g., /predict) and HTTP methods (GET, POST) that operate on them. For predictions, you'll almost always use POST because you're sending input data in the request body, and the output is not idempotent in practice—each call can have different results.

Here's the flow in a diagram-in-words:

  1. Client (curl, a web app, another service) sends a POST to http://localhost:8000/predict with JSON body containing features.
  2. FastAPI receives the request, validates the data against a Pydantic model, and passes it to your Python function.
  3. Your function loads the trained model, runs a prediction, and returns a JSON response—often with the prediction and maybe a timestamp or model version.
  4. FastAPI sends the response back to the client.

Key definitions you'll need: - Endpoint: A URL path like /predict that exposes a specific function. - Request body: The JSON payload sent in a POST request—for predictions, this is your feature vector. - Response body: The JSON output—the prediction and any metadata. - Pydantic model: A class that defines the schema for request/response data, enabling automatic validation.

Beyond the basics, a good endpoint also includes error handling (what if the client sends bad data?), documentation (FastAPI gives you automatic Swagger docs at /docs), and async support (FastAPI is async by default, so it scales well).

How it works step by step

Let's break down the process of creating a REST endpoint for predictions, from saved model to live API.

Step 1: Save your trained model

After training, you need to persist the model to disk. Use pickle or joblib. For most scikit-learn models, joblib is preferred because it's more efficient on large numpy arrays. Example:

import joblib

# Assume `model` is your trained sklearn model
joblib.dump(model, 'linear_regression.pkl')

Step 2: Set up a FastAPI application

Create a new Python file, say app.py. Install FastAPI and an ASGI server like uvicorn:

pip install fastapi uvicorn[standard] joblib pydantic

Then define the app and a Pydantic model for the request. The request model should match the features your model expects. For a house-price predictor with features like area, bedrooms, age, you'd define:

from fastapi import FastAPI
from pydantic import BaseModel
import joblib
import pandas as pd

app = FastAPI(title="House Price Prediction API")

# Load the model once at startup
model = joblib.load('linear_regression.pkl')

# Define request schema
class HouseFeatures(BaseModel):
    area: float
    bedrooms: int
    age: float

Step 3: Create the prediction endpoint

Add a POST endpoint that receives the features, converts them to the format your model expects (often a DataFrame), and returns the prediction.

@app.post("/predict")
def predict(features: HouseFeatures):
    # Convert to DataFrame (model expects 2D input)
    df = pd.DataFrame([features.model_dump()])
    prediction = model.predict(df)[0]
    return {"prediction": float(prediction)}

Step 4: Run the server and test

Start the server with uvicorn:

uvicorn app:app --reload

The --reload flag lets you auto-reload on code changes, great for development. With the server running, test with curl in a new terminal—or use the interactive docs at http://127.0.0.1:8000/docs.

Hands-on walkthrough

Let's build a complete working example. We'll train a simple linear regression on synthetic data, save it, and serve it via FastAPI. The goal is a prediction endpoint you can call with curl.

1. Train and save a model

Create a file train_model.py:

import numpy as np
import joblib
from sklearn.linear_model import LinearRegression

# Synthesize data: y = 2*area + 3*bedrooms + noise
np.random.seed(42)
X = np.random.rand(100, 2) * 10  # area, bedrooms? actually area in [0,10], bedrooms in [0,10]
# Let's make area 0-200, bedrooms 1-5
area = np.random.uniform(50, 200, 100)
bedrooms = np.random.randint(1, 6, 100)
X = np.column_stack([area, bedrooms])
y = 2 * area + 3 * bedrooms + np.random.normal(0, 5, 100)

model = LinearRegression().fit(X, y)

joblib.dump(model, 'model.pkl')
print("Model saved to model.pkl")

2. Create the FastAPI app

Now write app.py:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import joblib
import numpy as np

app = FastAPI(title="Prediction API")

# Load model once
model = joblib.load('model.pkl')

class HousingData(BaseModel):
    area: float = Field(..., ge=0, description="Area in square feet")
    bedrooms: int = Field(..., ge=1, le=10, description="Number of bedrooms")

@app.post("/predict")
def predict(data: HousingData):
    """Return price prediction for a house."""
    try:
        # Convert to 2D array for sklearn
        X = np.array([[data.area, data.bedrooms]])
        prediction = model.predict(X)[0]
        return {"prediction": round(float(prediction), 2)}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/")
def root():
    return {"message": "Housing Prediction API. Use POST /predict"}

3. Run server and test with curl

In the terminal, start the server:

uvicorn app:app --reload

Then in another terminal, send a test request:

curl -X POST "http://127.0.0.1:8000/predict" -H "Content-Type: application/json" -d '{"area": 1500, "bedrooms": 3}'

Expected output (your numbers will vary based on training data):

{"prediction": 3012.47}

You can also test the interactive Swagger UI at http://127.0.0.1:8000/docs—it lets you try out the API directly from the browser. This is a huge advantage of FastAPI.

Compare options / when to choose what

FastAPI is not the only framework to build REST endpoints for predictions. Let's compare the most common ones:

Framework Best for Strengths Weaknesses
FastAPI Modern data science, rapid prototyping Auto docs, async, Pydantic validation, high performance Requires Python 3.6+; less mature ecosystem than Flask
Flask Simple demos, small apps Lightweight, easy to learn, huge community No built-in validation, synchronous by default
Django REST Framework Large production apps with full-stack Full-featured, ORM, security Heavyweight, steeper learning curve

For most data science use cases, FastAPI is the clear winner. It gives you automatic OpenAPI documentation, data validation with Pydantic, and excellent performance—near Node.js or Go. Flask is fine for a quick internal tool, but you'll soon miss built-in validation and async support. Django REST is better for large teams when you're building a full web application around the model, but it's overkill for a simple prediction endpoint.

When to choose what: - Choose FastAPI when you want a production-ready API fast, with docs and validation built in. - Choose Flask if you're prototyping in a notebook and need minimal dependencies, or if the rest of your stack uses Flask. - Choose Django REST if your project already uses Django and you need database models and auth.

As a rule of thumb, if you're a data scientist, FastAPI is your default starting point.

Troubleshooting & edge cases

When you build your first prediction endpoint, you'll likely hit a few common issues. Here's how to diagnose and fix them:

1. ModuleNotFoundError when starting uvicorn

Symptom: ModuleNotFoundError: No module named 'fastapi' or 'sklearn'.

Cause: You're running uvicorn from a different Python environment than where you installed dependencies.

Solution: Activate the same virtual environment you used for pip install. Use which uvicorn to see if the server is from the right environment.

2. Model expects different feature order

Symptom: Predictions are wrong or you get an error like ValueError: X has 2 features, but LinearRegression is expecting 3 features.

Cause: The feature order in your request schema doesn't match the column order used in training.

Solution: Always save the feature names along with the model. For example, joblib.dump({'model': model, 'feature_names': ['area', 'bedrooms']}, 'model.pkl'). Then in the endpoint, reconstruct the DataFrame in that exact order.

3. Swagger docs not loading

Symptom: The interactive docs at /docs return a 404 or empty page.

Cause: You might be running behind a proxy that blocks the docs assets, or you're using an older version of FastAPI.

Solution: Upgrade to latest FastAPI (pip install --upgrade fastapi). If you’re behind a reverse proxy, you may need to configure the openapi_url to point to the correct path.

4. CORS issues when consuming from a web app

Symptom: Your frontend can’t call the API because the browser blocks the request (No 'Access-Control-Allow-Origin' header).

Cause: Cross-origin resource sharing is not enabled by default.

Solution: Add CORS middleware to your app. Example:

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # restrict this in production
    allow_methods=["*"],
    allow_headers=["*"],
)

5. Model loading every request (slow)

Symptom: Each prediction takes a second—too slow for production.

Cause: You call joblib.load inside the endpoint function, loading the model on every request.

Solution: Load the model once at module level, outside the endpoint. We did that in our examples. For very large models, you might even load it in an app lifespan handler.

What you learned & what's next

You now know how to create REST endpoints for predictions. You can: - Explain the core idea: wrapping a model behind an HTTP endpoint decouples it from consumers. - Complete a practical exercise: train a model, save it, load it in a FastAPI app, and expose a /predict endpoint that returns predictions as JSON. - Test with curl and the auto-generated Swagger UI. - Choose between FastAPI, Flask, and Django REST based on your needs. - Troubleshoot common issues like environment mismatches, feature order, CORS, and performance.

What's next? The next lesson covers deploying your model to the cloud—we'll take this FastAPI app and run it on a platform like Render or Railway. You'll learn about environment variables, persistent storage, and how to keep your API alive 24/7. Get ready to move from localhost to the World Wide Web.

Practice recap

Now extend the API you built: add a second endpoint /predict_batch that accepts a list of feature objects and returns a list of predictions. Test it with a curl request sending two houses. Also, add a model_version field to the response so consumers know which model served the prediction.

Common mistakes

  • Loading the model inside the endpoint function—this slows every request and can cause memory leaks. Load it once at module level.
  • Sending a list of features instead of a named object, causing misalignment between the request fields and the model's feature order. Always use named fields and reconstruct the DataFrame in the exact training order.
  • Forgetting to include CORS middleware when the API is called from a browser-based app—resulting in mysterious blocked requests.

Variations

  1. Use Flask instead of FastAPI if you prefer a minimalist framework and don't need built-in validation or async support.
  2. Use Django REST Framework for large projects that already use Django, where you need auth and database integration.
  3. For non-Python clients, consider providing a batch endpoint /predict/batch that accepts a list of feature dicts to reduce HTTP overhead.

Real-world use cases

  • A real-estate platform exposes a /predict endpoint so its web frontend can show instant house price estimates as users enter details.
  • A fraud detection team builds an internal API for real-time scoring of transactions, called by a transaction processing service.
  • A recommendation engine serves top-N product suggestions via a REST endpoint consumed by both mobile and web frontends.

Key takeaways

  • REST endpoints provide a clean HTTP interface to your model, decoupling it from consumers.
  • Use FastAPI for its automatic docs, Pydantic validation, and async performance.
  • Load the model once at startup, not on every request.
  • Design your request payload with named fields and ensure the feature order matches training.
  • Test your endpoint with curl and interactive /docs; add CORS only when needed.
  • Fix common issues like environment mixing and feature mismatch early to save debugging time.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.