Deploy Models with Flask APIs
Learn to deploy your machine learning models as Flask APIs — a practical Python for data science lesson with hands-on steps, troubleshooting, and next steps.
Focus: deploy models with flask apis
You’ve spent hours training a model — tuning hyperparameters, squeezing out that last 0.01 accuracy, and validating on a holdout set. But when it’s time to share your work with the world, you hit a wall: how do you let others use your model without handing them the training code and data? The answer is deploying models with Flask APIs — turning your Python model into a live web service that anyone can call with a simple HTTP request. In this lesson, you’ll learn the core concept, build a complete Flask API step by step, and troubleshoot the common pitfalls that trip up even experienced data scientists. By the end, you’ll have a deployable model endpoint you can connect to a frontend, a mobile app, or another microservice.
The problem this lesson solves
Your model is useless if it only lives in a Jupyter notebook. Real-world users — whether they’re analysts, customer-facing apps, or other services — need a way to send input data and receive predictions reliably and securely. Without an API, you’re forced to export predictions manually, share scripts, or let someone dig through your codebase. That’s slow, fragile, and often dangerous (imagine exposing your training data by accident).
Deploying models with Flask APIs solves this by wrapping your model in a lightweight web server. The server listens for requests, runs the model, and returns a JSON response. It’s the same pattern used by production ML systems — think Netflix recommendations or spam filters — but you can start small and scale up.
Pro tip: You don’t need a heavyweight framework like Django or FastAPI to start. Flask’s minimal design makes it perfect for a single-model endpoint — and it’s already familiar if you’ve done any web work in Python.
Core concept / mental model
Think of your model as a black-box function: input goes in, prediction comes out. A Flask API is the delivery mechanism that lets external clients call that function over the network.
Here’s the mental model in three layers:
- Model layer — your trained scikit-learn, TensorFlow, or PyTorch model (often saved to a file like
model.pkl). - API layer — the Flask app that loads the model once, exposes an endpoint (e.g.,
/predict), and handles incoming requests. - Client layer — anything that sends HTTP requests: a web form, a mobile app,
curlin your terminal, or another Python script.
The flow looks like this:
Client --POST JSON--> Flask app --> load model --> preprocess --> predict --> return JSON
Definitions to keep straight:
- Endpoint — the URL path (e.g.,
/predict) that triggers your function. - Request — the HTTP message from the client, usually containing input data (as JSON).
- Response — the HTTP message back, typically including the prediction and a status code.
- Serialization — converting your model to a file (using
pickleorjoblib) so it can be loaded later.
Once your model is behind a Flask API, it’s deployed in the most basic sense: it’s accessible to external callers. The same pattern works whether you run it on your laptop, a cloud VM, or a container.
How it works step by step
Deploying a model with Flask boils down to four steps. Let’s go through each in order.
1. Train and save your model
You can’t deploy a model that isn’t persisted. Use joblib (preferred for scikit-learn) or pickle to save the trained object to disk. This file is your deployable artifact.
# train_and_save.py
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import joblib
# Load data and train
data = load_iris()
X, y = data.data, data.target
model = RandomForestClassifier(n_estimators=100)
model.fit(X, y)
# Save the model
joblib.dump(model, 'iris_model.pkl')
print("Model saved to iris_model.pkl")
Expected output:
Model saved to iris_model.pkl
2. Create the Flask app
Build a minimal Flask app with a single route. The route receives JSON, converts it to the format your model expects, and calls predict().
# app.py
from flask import Flask, request, jsonify
import joblib
import numpy as np
app = Flask(__name__)
# Load the model once at startup
model = joblib.load('iris_model.pkl')
@app.route('/predict', methods=['POST'])
def predict():
data = request.get_json()
features = np.array(data['features']).reshape(1, -1)
prediction = model.predict(features)
return jsonify({'prediction': int(prediction[0])})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
3. Run the server
Start the Flask app with python app.py. By default it runs on port 5000. The host='0.0.0.0' makes it accessible outside your local machine (e.g., from a Docker container or another device on your network).
4. Test the API
Use curl or Python to send a POST request with sample features. You should get back a JSON response.
curl -X POST http://127.0.0.1:5000/predict \
-H "Content-Type: application/json" \
-d '{"features": [5.1, 3.5, 1.4, 0.2]}'
Expected response:
{"prediction":0}
Pro tip: Always load your model at the top level (module scope), not inside the route function. Loading is expensive — you don’t want it to happen on every request.
Hands-on walkthrough
Let’s build a complete, runnable example from scratch. We’ll use a simple linear regression model (your own model can be swapped in).
Step 1: Install dependencies
pip install flask scikit-learn joblib
Step 2: Save the model (run once)
# save_model.py
from sklearn.linear_model import LinearRegression
import joblib, numpy as np
# Dummy data: y = 2*x + 1
X = np.array([[1], [2], [3], [4]])
y = np.array([3, 5, 7, 9])
model = LinearRegression().fit(X, y)
joblib.dump(model, 'linreg.pkl')
print("Saved")
Step 3: Write the Flask API
# app.py
from flask import Flask, request, jsonify
import joblib
import numpy as np
app = Flask(__name__)
model = joblib.load('linreg.pkl')
@app.route('/predict', methods=['POST'])
def predict():
try:
data = request.get_json()
features = np.array(data['features'])
features = features.reshape(1, -1)
pred = model.predict(features)
return jsonify({'prediction': float(pred[0])})
except Exception as e:
return jsonify({'error': str(e)}), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Step 4: Run and test
Start the server in one terminal:
python app.py
In another terminal, send a request:
# test_request.py
import requests
resp = requests.post(
'http://127.0.0.1:5000/predict',
json={'features': [10]}
)
print(resp.json())
Expected output:
{'prediction': 21.0}
Notice the error handling: if the client sends malformed data, you return a 400 status with an error message instead of a confusing crash.
Pro tip: When you’re ready for production, set
debug=False— the debugger can expose sensitive code and is a security risk.
Compare options / when to choose what
Flask isn’t your only choice. Here’s a quick comparison:
| Framework | Best for | Pros | Cons |
|---|---|---|---|
| Flask | Simple single-model APIs | Minimal, familiar, huge ecosystem | Sync by default, not built for heavy async |
| FastAPI | High-performance, async, auto docs | Asynchronous, Pydantic validation, automatic OpenAPI docs | Slightly steeper learning curve |
| Django REST | Full web apps + API | Batteries included (auth, admin) | Heavy for just a model endpoint |
| TensorFlow Serving | TensorFlow models at scale | Optimized for TF, batching | TF-specific, more ops overhead |
For most data science projects, Flask is the right starting point — it’s simple to debug, quick to prototype, and the skills transfer to FastAPI if you outgrow it. Choose FastAPI if you expect high concurrency or need auto-generated docs. Choose TensorFlow Serving only when your model is TensorFlow and performance is critical.
Pro tip: Deployment is a spectrum. Start with Flask on your laptop, then move to a cloud VM (AWS, GCP) or containerize with Docker. Your API code stays the same — only the host changes.
Troubleshooting & edge cases
The most common issues when deploying models with Flask APIs are:
- Model not found (FileNotFoundError) — Make sure the
.pklfile is in the same directory as your Flask app, or use an absolute path. - JSON shape mismatch — Your client sends a different number of features than your model expects. Use
np.array(data['features']).reshape(1, -1)— but verify the feature count matches the training data. - CORS errors — If your frontend is on a different origin, you need to add CORS headers. Use the
flask-corspackage. - Port already in use — If port 5000 is taken, change it with
port=5001or kill the existing process. - Model not serializable — Some custom objects (e.g., custom transformers) fail with
pickle. Usejobliband test the save/load cycle before deployment.
Real-world example of a shape error:
# Wrong: data['features'] = [1, 2, 3] but your model was trained on 4 features
features = np.array([1, 2, 3]).reshape(1, -1) # shape (1,3) -> error
Fix: make sure your request JSON includes all features, and consider adding a validation step:
if len(features[0]) != model.n_features_in_:
return jsonify({'error': f'Expected {model.n_features_in_} features, got {len(features[0])}'}), 400
Pro tip: Always wrap your prediction logic in a
try/exceptand return a meaningful error message with a status code. Your API users will thank you.
What you learned & what's next
In this lesson, you learned how to deploy models with Flask APIs — the essential skill for turning your trained models into live services. You now understand:
- Why deployment matters (models become usable by others)
- The mental model of model → API → client
- How to save a model with
jobliband load it in a Flask app - How to build a
/predictendpoint that accepts JSON and returns JSON - How to test your API with
curland Python - How to compare Flask with other frameworks and choose the right tool
- Common pitfalls like shape mismatches and CORS, and how to fix them
You’ve met both learning objectives: you can explain the core idea behind deploying models with Flask APIs, and you’ve completed a practical exercise that builds and tests a working endpoint.
What’s next? In the next lesson, you’ll take your deployment further by adding input validation, authentication, and versioning — turning your basic API into a production-ready service. You’ll also explore containerization with Docker to make your API portable and scalable.
Keep this lesson fresh: every time you train a new model, practice deploying it as a Flask API before moving on. It’s a skill that will pay off in every project from here on.
Practice recap
As a next step, try deploying a model you’ve already trained in this track. Save it with joblib, build a Flask API with a /predict endpoint, and test it with curl. If you’re feeling ambitious, add a second endpoint like /health that returns a 200 status, and containerize the whole thing with a simple Dockerfile.
Common mistakes
- Loading the model inside the route function — this slows down every request and can cause memory issues.
- Forgetting to reshape the input to the shape the model expects (e.g., using a list instead of a 2D array).
- Not handling exceptions in the
/predictendpoint, so any bad input causes a crash. - Using
pickleinstead ofjoblibfor scikit-learn models, which can fail with certain estimators. - Running Flask with
debug=Truein production, exposing sensitive information.
Variations
- Use FastAPI instead of Flask for asynchronous handling and automatic OpenAPI documentation.
- Containerize your Flask app with Docker to ensure consistent environments across machines.
- Use the
flask-corspackage to enable cross-origin requests from frontend applications.
Real-world use cases
- A data scientist deploys a churn prediction model as a Flask API for the customer service team to query in real time.
- An e-commerce startup exposes a product recommendation model via a Flask endpoint that the website frontend calls on each page load.
- A research lab shares a medical diagnosis model as a Flask API so clinicians can submit patient data and get predictions securely.
Key takeaways
- Deploying models with Flask wraps your model in a web server, making it accessible via HTTP requests.
- Always save your trained model with
joblib(orpickle) and load it once at the top of your Flask app. - A
/predictendpoint with POST method receives JSON, preprocesses it, runs the model, and returns JSON. - Test your API with
curlorrequeststo ensure it works before integrating with clients. - Choose Flask for simplicity, FastAPI for async performance, and Docker for production-grade portability.
- Handle errors and validate input to make your API robust and user-friendly.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.