Build a REST API with Python Flask: A Practical Guide for 2026
If you want to build a REST API with Python Flask, you are in the right place. In this tutorial, we will walk through everything you need to create a fully working API: setting up Flask, defining routes, handling JSON requests, returning responses, and managing errors properly. All code examples are copy-paste ready and tested on Python 3.12 and Flask 3.1.
Unlike most tutorials that stop at a basic “hello world”, we will build a small but realistic Books API with full CRUD operations, so you finish this article with something you can actually reuse in your projects.

Why Flask for REST APIs?
Flask is a lightweight WSGI web framework that has remained one of the most popular choices for Python developers, even in 2026. It is minimal, flexible, and perfect for small to medium APIs or microservices.
- Minimal setup: install one package and you are ready.
- Flexible: choose your own libraries for database, validation, auth.
- Huge ecosystem: Flask-SQLAlchemy, Flask-Smorest, Flask-JWT-Extended.
- Great documentation and a massive community.
Flask vs FastAPI in 2026
| Criteria | Flask | FastAPI |
|---|---|---|
| Learning curve | Very easy | Easy |
| Performance | Good (sync) | Excellent (async) |
| Ecosystem | Mature, very large | Growing fast |
| Best for | Simple APIs, microservices, prototypes | High-performance async APIs |
If you need raw speed and async out of the box, FastAPI is a great choice. If you want simplicity, stability and full control, Flask is still an excellent pick.
Step 1: Set Up Your Project
Create a new folder and a virtual environment:
mkdir flask-books-api
cd flask-books-api
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install flask
Then create a file named app.py. That is the only file we will need for this tutorial.

Step 2: Your First Flask Route
Let’s start with a minimal Flask app to make sure everything works:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/", methods=["GET"])
def home():
return jsonify({"message": "Welcome to the Books API"})
if __name__ == "__main__":
app.run(debug=True, port=5000)
Run it with:
python app.py
Open http://localhost:5000 and you should see a JSON response. Done, your first Flask endpoint is live.
Step 3: Build the Full CRUD REST API
Now let’s replace the content of app.py with a complete CRUD API for a list of books. We will use an in-memory dictionary as a fake database to keep things simple.
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# Fake in-memory database
books = {
1: {"title": "Clean Code", "author": "Robert C. Martin", "year": 2008},
2: {"title": "The Pragmatic Programmer", "author": "Andy Hunt", "year": 1999},
}
next_id = 3
@app.route("/api/books", methods=["GET"])
def get_books():
return jsonify(books), 200
@app.route("/api/books/<int:book_id>", methods=["GET"])
def get_book(book_id):
book = books.get(book_id)
if book is None:
abort(404, description="Book not found")
return jsonify(book), 200
@app.route("/api/books", methods=["POST"])
def create_book():
global next_id
if not request.is_json:
abort(400, description="Request body must be JSON")
data = request.get_json()
required = ["title", "author", "year"]
if not all(field in data for field in required):
abort(400, description=f"Missing fields. Required: {required}")
new_book = {
"title": data["title"],
"author": data["author"],
"year": data["year"],
}
books[next_id] = new_book
created_id = next_id
next_id += 1
return jsonify({"id": created_id, "book": new_book}), 201
@app.route("/api/books/<int:book_id>", methods=["PUT"])
def update_book(book_id):
if book_id not in books:
abort(404, description="Book not found")
if not request.is_json:
abort(400, description="Request body must be JSON")
data = request.get_json()
book = books[book_id]
book["title"] = data.get("title", book["title"])
book["author"] = data.get("author", book["author"])
book["year"] = data.get("year", book["year"])
return jsonify(book), 200
@app.route("/api/books/<int:book_id>", methods=["DELETE"])
def delete_book(book_id):
if book_id not in books:
abort(404, description="Book not found")
deleted = books.pop(book_id)
return jsonify({"deleted": deleted}), 200
# Error handlers
@app.errorhandler(400)
def bad_request(error):
return jsonify({"error": "Bad Request", "message": str(error.description)}), 400
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "Not Found", "message": str(error.description)}), 404
@app.errorhandler(405)
def method_not_allowed(error):
return jsonify({"error": "Method Not Allowed"}), 405
@app.errorhandler(500)
def server_error(error):
return jsonify({"error": "Internal Server Error"}), 500
if __name__ == "__main__":
app.run(debug=True, port=5000)
Restart the app and you now have a fully working REST API with five endpoints.
Endpoint Summary
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/books | List all books |
| GET | /api/books/<id> | Get one book |
| POST | /api/books | Create a new book |
| PUT | /api/books/<id> | Update an existing book |
| DELETE | /api/books/<id> | Delete a book |
Step 4: Test Your API
You can test the API using curl, Postman, HTTPie, or your favorite REST client.
Create a new book (POST)
curl -X POST http://localhost:5000/api/books \
-H "Content-Type: application/json" \
-d '{"title":"Designing Data-Intensive Applications","author":"Martin Kleppmann","year":2017}'
Get all books (GET)
curl http://localhost:5000/api/books
Update a book (PUT)
curl -X PUT http://localhost:5000/api/books/1 \
-H "Content-Type: application/json" \
-d '{"year":2009}'
Delete a book (DELETE)
curl -X DELETE http://localhost:5000/api/books/2

Step 5: Best Practices to Take It Further
Now that you have a working API, here are the next steps to make it production-ready:
- Use a real database with Flask-SQLAlchemy (PostgreSQL, MySQL, or SQLite).
- Validate input using Pydantic, Marshmallow, or Flask-Smorest.
- Add authentication with Flask-JWT-Extended.
- Document the API automatically with APIFlask or Flask-Smorest (OpenAPI/Swagger).
- Use Blueprints to organize routes when the project grows.
- Deploy with Gunicorn behind Nginx, or use a PaaS like Fly.io or Render.
- Add tests with pytest and Flask’s test client.
Recommended Project Structure
flask-books-api/
├── app/
│ ├── __init__.py
│ ├── routes/
│ │ └── books.py
│ ├── models.py
│ └── schemas.py
├── tests/
│ └── test_books.py
├── requirements.txt
└── run.py
Common Pitfalls to Avoid
- Forgetting jsonify(): returning a Python dict works in modern Flask, but jsonify gives you full control of headers.
- Running debug mode in production: never do this, it exposes a Python shell.
- Skipping input validation: always validate JSON payloads before using them.
- No error handlers: without them, your users get HTML error pages instead of JSON.
FAQ
Is Flask still used in 2026?
Yes. Flask remains one of the most popular Python web frameworks in 2026, especially for microservices, internal tools and APIs where simplicity matters more than async performance.
Is Python Flask a REST API framework?
Flask itself is a general-purpose web framework, but it is widely used to build REST APIs thanks to its routing system and JSON helpers. Extensions like Flask-Smorest or APIFlask add REST-specific features.
Can I build a REST API in Python without Flask?
Yes. Alternatives include FastAPI, Django REST Framework, Starlette, Falcon, and even the standard library. Flask is just one of the easiest to start with.
Should I choose Flask or FastAPI?
Choose Flask for simplicity, stability and a huge ecosystem. Choose FastAPI if you need async performance and automatic OpenAPI documentation out of the box.
How do I deploy a Flask REST API?
Use a production WSGI server like Gunicorn or uWSGI behind Nginx, or deploy to platforms like Render, Fly.io, Railway, AWS, or Google Cloud Run.
Conclusion
You just learned how to build a REST API with Python Flask from scratch, including routing, JSON handling, CRUD operations, and proper error management. The code in this tutorial works out of the box and is a solid foundation for any real-world project.
From here, plug in a database, add validation and authentication, and you have a production-grade API. Happy coding from the Pixelseed team!