Serve trained Tensorflow model with REST API using Flask?
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.
Introduction
Serving a trained TensorFlow model through a Flask REST API involves loading the saved model at startup, creating an endpoint that accepts input data (usually JSON), preprocessing the input into the format the model expects, running inference, and returning the prediction as a JSON response. This approach is suitable for prototyping and low-traffic deployments. For production use, consider TensorFlow Serving or FastAPI with async support.
Saving a Trained Model
First, save your trained model in the SavedModel or HDF5 format:
Basic Flask API
Test the endpoint:
Input Preprocessing
Most models require specific input shapes and normalization:
Image Classification Endpoint
For serving an image classification model:
Test with an image file:
Health Check and Model Info
Production Considerations
Use Gunicorn Instead of Flask Dev Server
Thread Safety with TensorFlow
TensorFlow is not fully thread-safe. Use a lock for predictions in multi-threaded environments:
Batch Predictions
Docker Deployment
Common Pitfalls
- Loading the model inside the request handler:
tf.keras.models.load_model()takes seconds to minutes for large models. Load the model once at module level or in anapp.before_first_requesthandler, not inside the endpoint function. - Using Flask's development server in production:
app.run()uses a single-threaded server that cannot handle concurrent requests. Use Gunicorn or uWSGI in production with multiple workers. - NumPy arrays are not JSON serializable:
model.predict()returns a NumPy array. Call.tolist()before passing it tojsonify(), or the response will fail with aTypeError. - Forgetting the batch dimension: Most TensorFlow models expect input shape
(batch_size, ...). A single input of shape(784,)must be reshaped to(1, 784)withnp.expand_dims(input_data, axis=0). - GPU memory with multiple workers: Each Gunicorn worker loads a separate copy of the model into GPU memory. With 4 workers and a large model, you may run out of GPU memory. Use
tf.config.set_memory_growth(gpu, True)or limit workers to 1-2 for GPU inference.
Summary
- Save models with
model.save()and load at Flask app startup withtf.keras.models.load_model() - Create a
/predictendpoint that accepts JSON input, runsmodel.predict(), and returns JSON - Validate input shape and data types before running inference
- Use Gunicorn with multiple workers for production deployment
- Add a threading lock around
model.predict()for thread safety - Use Docker for reproducible deployments with pinned dependency versions
Related reading
- Set half of the filters of a layer as not trainable keras/tensorflow
- Set k-largest elements of a tensor to zero in TensorFlow
- Set static shapes in an existing tensorflow graph where dynamic shapes are used for input
- Set weight and bias tensors of tensorflow conv2d operation
- Setting tensorflow rounding mode
- setting values for ntree and mtry for random forest regression model
- server could not find the requested resource get pods error when deploying Helm chart using Jenkins
- Server Sent Events In a Kubernetes Cluster

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.