Build a FastAPI-Powered API with Python in Minutes
What is FastAPI?
FastAPI is a high-performing web framework for building APIs with Python. Known for its exceptional speed and modern architecture, FastAPI has gained popularity in API development. Built on top of Starlette and Pydantic, it provides asynchronous support and robust data validation while keeping the codebase clean and efficient. Its design promotes best practices, enabling the development of scalable and maintainable web applications.
Read this article to understand how to use APIs in Python.
Key benefits of FastAPI
FastAPI stands out for several compelling features:
- High performance: Supports asynchronous programming and handles large volumes of requests with minimal latency.
- Automatic interactive documentation: Generates Swagger UI and ReDoc interfaces for real-time testing and exploration of endpoints.
- Built-in validation: Utilizes Python type hints and Pydantic models for precise input validation and clear error messages.
- Developer-friendly: Reduces bugs and increases development speed through editor support like autocomplete and type checking.
Now that FastAPI’s core strengths are clear, let’s focus on setting up the project environment and installing the necessary packages.
Using OpenAI APIs: Accessing OpenAI APIs from Python
Excel in OpenAI APIs using Python. Discover API key authentication, access to completions APIs via endpoints, model configurations, and control of creativity and response length.Try it for freeInstalling FastAPI
To start building APIs using FastAPI, the framework, and a server are required to run it. FastAPI works best with Uvicorn, a lightning-fast ASGI server that supports asynchronous programming and ensures high performance in production environments.
Before installation, it’s recommended to use a virtual environment to manage project dependencies efficiently and prevent conflicts across Python projects.
Step 1: Create a project directory
Begin by creating a dedicated folder for the FastAPI project:
mkdir fastapi_app # create the project directorycd fastapi_app # navigate into the project directory
Here, fastapi_app
is the folder name to be used.
Step 2: Set up a virtual environment
Create and activate a virtual environment in the project directory using the following command:
python -m venv venvsource venv/bin/activate # For Windows: venv\Scripts\activate
Step 3: Installing FastAPI and Uvicorn
With the virtual environment active, install FastAPI and Uvicorn using pip
:
pip install fastapi uvicorn
fastapi
: The main framework used to build APIs.uvicorn
: An ASGI server used to serve FastAPI applications.
With the environment set up and essential tools installed, the next step is to create the initial FastAPI application and define API endpoints.
Getting started with API creation
To start building the API, let’s create the main application file and define the initial routes. This section lays the groundwork for developing functional endpoints.
Creating the main file
The first step in API development is setting up the main application file. This file will serve as the entry point to the API. Inside the fastapi_app
folder, create a new Python file to hold the FastAPI application:
touch main.py
The updated project folder should look like this:
fastapi_app/├── venv/ # Virtual environment├── main.py # FastAPI application file
Open main.py and add the following basic code to initialize a basic FastAPI app:
from fastapi import FastAPIapp = FastAPI() # Create an instance of the app.@app.get("/")def read_root():return {"message": "Welcome to FastAPI!"}
This code performs the following:
- Imports the FastAPI class.
- Creates an instance of the app.
- Defines a basic
GET
route for the root path/
that returns a JSON message.
Use Uvicorn to run the application:
uvicorn main:app --reload
Here:
main
refers to the filename main.py.app
is the FastAPI instance created inside the file.--reload
enables automatic reload on code changes.
After running this command, a local development server address will be displayed in the terminal, usually http://127.0.0.1:8000. Visit that address in the browser to see the default JSON response. The output will look like this right now:
Let’s now define additional routes and explore how to handle different requests.
Creating endpoints in FastAPI
In FastAPI, API endpoints, also known as routes - are used to define how an application responds to different HTTP requests. These routes serve as the main entry points for client interactions with the API. Let’s create a basic grocery list endpoint that returns a welcome message:
from fastapi import FastAPIapp = FastAPI()@app.get("/grocery")def read_groceries():return {"message": "This is your grocery list."}
Use the Uvicorn command mentioned earlier to test this endpoint and add /grocery
at the end of the URL. This is how it should look in the output:
This output confirms the API is working. Next, we’ll enhance the endpoint using query and path parameters.
Using query and path parameters to enhance endpoints
FastAPI supports query and path parameters to make APIs flexible and powerful. Query parameters are optional values passed in the URL after a ?
, while path parameters are part of the URL path itself.
FastAPI uses decorators like @app.get()
to define API routes. This tells FastAPI which URL should trigger a specific function when an HTTP GET request is made.
Update the main.py file with the following code:
from fastapi import FastAPIapp = FastAPI()grocery_list = []# Query parameter: Add item to the grocery list@app.get("/grocery")def add_grocery_item(item: str = None):if item:grocery_list.append(item)return {"grocery_list": grocery_list}# Path parameter: Retrieve item by its index@app.get("/grocery/{item_id}")def get_grocery_item(item_id: int):if 0 <= item_id < len(grocery_list):return {"item": grocery_list[item_id]}return {"error": "Item not found"}
For the first function, add_grocery_item()
:
@app.get("/grocery")
: This defines a GET endpoint at/grocery
.- The function handles two actions in one route:
- If an item is passed using a query parameter like
?item=milk
, it adds the item to the list. - If no item is passed, it returns the current grocery list.
- If an item is passed using a query parameter like
- The query parameter
item: str = None
makes theitem
optional. If provided, it’s added to the list.
For the second function, get_grocery_item()
:
@app.get("/grocery/{item_id}")
: This defines a GET endpoint with a path parameter for the item index.- This function fetches a specific item from the list using its index.
- The
item_id
(path parameter) is captured from the URL path. It’s automatically converted to an integer. - The function also checks if the index is within bounds before accessing the list.
To add items using the query parameter, use the following command:
curl "http://127.0.0.1:8000/grocery?item=milk"curl "http://127.0.0.1:8000/grocery?item=bread"
To retrieve an item by index using the path parameter, use the following command:
curl http://127.0.0.1:8000/grocery/1
Note: These requests can also be tested by visiting http://127.0.0.1:8000/grocery or http://127.0.0.1:8000/grocery/0 in the browser.
Smooth requests are outstanding, but what happens when things go wrong? Let’s handle errors next.
Handling HTTP errors
In real-world APIs, it is essential to handle situations when something goes wrong - like when a user requests an item that doesn’t exist. FastAPI provides a way to raise and handle HTTP errors using the built-in HTTPException
class.
The table highlights a few standard HTTP status codes often used in API development:
HTTP Status Code | Description |
---|---|
400 Bad Request | The request is malformed or missing the required data. |
404 Not Found | The requested resource doesn’t exist. |
500 Internal Server Error | Something went wrong with the server. |
Let’s modify the /grocery/{item_id}
endpoint to raise a proper HTTP error instead of returning a custom message:
from fastapi import FastAPI, HTTPExceptionapp = FastAPI()grocery_list = []@app.get("/grocery/{item_id}")def get_grocery_item(item_id: int):if 0 <= item_id < len(grocery_list):return {"item": grocery_list[item_id]}raise HTTPException(status_code=404, detail="Grocery item with the given ID does not exist. ")
In this code:
HTTPException
is imported fromfastapi
.- If the requested index is out of bounds, the function raises a 404 error.
status_code=404
tells the client the item wasn’t found.detail="Grocery item with the given ID does not exist."
gives a clear error message in the response.
If an item that does not exist is accessed, for example, using:
curl http://127.0.0.1:8000/grocery/5
The API will return the customized error message. Once errors are handled, it’s time to make our APIs smarter with structured JSON input.
Handling JSON requests with Pydantic
In FastAPI, data can be sent to the server using query parameters, path parameters, or JSON request bodies. Recall the following code, where a query parameter is used to add an item to a grocery list:
@app.get("/grocery")def add_grocery_item(item: str = None):if item:grocery_list.append(item)return {"grocery_list": grocery_list}
In this setup, the item
is passed as a query parameter in the URL (e.g., ?item=Milk
). However, passing it as a JSON body is more practical and scalable when dealing with more complex or structured data, such as an item with both a name and quantity.
FastAPI integrates with Pydantic to handle and validate JSON data sent in the body of a POST request. This makes it easy to ensure the input is complete and in the correct format.
Here’s how to update the grocery list API to accept JSON data:
from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModel # BaseModel is used to define the structure and validation of request dataapp = FastAPI()grocery_list = []# Define a Pydantic model for the expected JSON structure in POST requestsclass GroceryItem(BaseModel):name: str # 'name' should be a stringquantity: int # 'quantity' should be an integer# This endpoint accepts POST requests with JSON data matching the GroceryItem model@app.post("/grocery")def add_grocery_item(item: GroceryItem):grocery_list.append({"name": item.name, "quantity": item.quantity})return {"grocery_list": grocery_list}@app.get("/grocery")def read_grocery_list():return {"grocery_list": grocery_list}
In this code:
GroceryItem
is a Pydantic model used to define the structure of the incoming JSON object.- The
@app.post("/grocery")
decorator sets up a POST endpoint. - FastAPI automatically:
- Parses the incoming JSON request.
- Validates the structure based on the
GroceryItem
model. - Returns a helpful error if the structure or types don’t match.
- If validation passes, the item is added to
grocery_list
.
To test this, use the following curl command that sends a POST request with a JSON body:
curl -X POST -H "Content-Type: application/json" -d "{\"name\": \"Eggs\", \"quantity\": 12}" http://127.0.0.1:8000/grocery
In this command:
-H
stands for header, and it sets the request header. Here, it tells the server that we’re sending JSON data.-d
stands for data, and it sends the JSON payload (the body of the POST request) to the API.
The response from the API will look like this:
{"grocery_list": [{"name": "Eggs","quantity": 12}]}
Using response models in FastAPI
When our API sends data back to the user—such as after adding an item or fetching a list—the structure of that data must be clear, consistent, and well-defined. This is where response models come in.
They allow us to define what our outgoing data should look like using Pydantic’s BaseModel
. This helps FastAPI validate and format the response before it’s returned to the client.
Response models have the following benefits:
- Standardized Output: This ensures that responses always follow a consistent structure, regardless of the logic inside your function.
- Security: Prevents accidental exposure of sensitive or internal data by explicitly defining what should be included.
- Structured Data: Makes your API predictable and improves the quality of your documentation.
We already use a model to receive data. Now, let’s define a model for the data we send back:
from pydantic import BaseModelclass GroceryItemResponse(BaseModel):name: strquantity: int
This tells FastAPI: “Any response involving a grocery item must include just a name
and quantity
, both with their proper types.”
We’ll modify our endpoints to apply this model using the response_model
parameter. Here’s how the updated code looks:
from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom typing import Listapp = FastAPI()grocery_list = []# Input modelclass GroceryItem(BaseModel):name: strquantity: int# Response modelclass GroceryItemResponse(BaseModel):name: strquantity: int# POST endpoint: Add item to the grocery list@app.post("/grocery", response_model=List[GroceryItemResponse])def add_grocery_item(item: GroceryItem):grocery_list.append({"name": item.name, "quantity": item.quantity})return grocery_list# GET endpoint: Return all grocery items@app.get("/grocery", response_model=List[GroceryItemResponse])def read_grocery_list():return grocery_list# GET endpoint: Return one item by ID@app.get("/grocery/{item_id}", response_model=GroceryItemResponse)def get_grocery_item(item_id: int):if 0 <= item_id < len(grocery_list):return grocery_list[item_id]raise HTTPException(status_code=404, detail="Grocery item with the given ID does not exist.")
In this code:
- A new class
GroceryItemResponse
has been introduced. This acts as a filter and validator for outgoing responses. - Each route that sends grocery item data now uses
response_model=
to enforce this structure./grocery
(POST and GET) returns a list ofGroceryItemResponse
./grocery/{item_id}
returns a singleGroceryItemResponse
.
- FastAPI uses this model to shape and validate what’s returned to the client automatically.
- Even if the underlying data (
grocery_list
) changes internally, the external output remains clean and predictable.
Now that our API returns structured responses, let’s explore how to view and test it easily using FastAPI’s interactive documentation.
Interactive documentation feature in FastAPI
One of FastAPI’s standout features is its automatic generation of interactive documentation. With no extra setup, it provides a built-in UI to view and test your API, making development and debugging incredibly convenient. The table highlights the two types of docs that FastAPI offers:
Feature | Swagger UI | ReDoc |
---|---|---|
Interface | A dynamic, interactive interface where you can explore endpoints, fill in inputs, and click “Try it out” to test your API directly. | A clean, read-only interface that organizes your API schema in a well-structured format. |
Ideal For | Developers during the testing phase | Sharing API references with other teams or external users |
URL | Available at /docs |
Available at /redoc |
To try it, start your FastAPI server:
uvicorn main:app --reload
Then open these in your browser:
- Swagger UI: http://127.0.0.1:8000/docs
- ReDoc: http://127.0.0.1:8000/redoc
You’ll see your grocery store API in action:
- Submit items using
POST /grocery
- View all items via
GET /grocery
- Retrieve a specific item with
GET /grocery/{item_id}
Both interfaces automatically update as your API evolves.
With our basic API up and running, it’s time to step back and see where FastAPI stands in the broader landscape of Python web frameworks.
FastAPI vs. Flask vs. Django
FastAPI, Flask, and Django are widely used Python web frameworks, each suited for different use cases. One may fit better depending on our project’s size, performance needs, and development goals.
The table below highlights how they compare across key features:
Feature | FastAPI | Flask | Django |
---|---|---|---|
Speed | Very fast | Moderate | Moderate |
Asynchronous Support | Built-in async support | Requires extra setup (e.g. Quart) | Limited (available in newer versions) |
Validation | Built-in via Pydantic | Manual or with extensions | Form-based validation |
Automatic Docs | Yes (Swagger UI & ReDoc out of the box) | No (requires extensions) | No (needs third-party tools) |
Use case | APIs and microservices | Lightweight APIs & web apps | Full-scale web applications |
ORM Support | External (SQLModel, Tortoise ORM) | External (SQLAlchemy, etc.) | Built-in ORM (powerful and mature) |
Community & Resources | Growing rapidly | Mature and large | Very large and well-established |
Each framework has its strengths—choosing the right one depends on your project’s complexity, performance needs, and development style.
Conclusion
FastAPI offers a fast and modern way to build APIs with minimal effort. Throughout this article, we explored how to create APIs using FastAPI - creating routes, handling requests, validating inputs, and structuring responses. Interactive docs like Swagger UI and Redoc simplified testing. Finally, we compared FastAPI with Flask and Django to understand their strengths.
To dive deeper into building robust APIs and expand your backend development skills, check out Codecademy’s Create REST APIs with Spring and Java skill path.
Frequently asked questions
1. Why is FastAPI faster than Flask?
FastAPI is built on Starlette and Pydantic, using Python’s asyncio
for asynchronous execution. This makes it highly performant, especially for handling multiple requests concurrently, unlike Flask’s synchronous nature.
2. Is FastAPI better than REST API?
FastAPI is a framework for building REST APIs. It's not a replacement but a modern tool to build APIs quickly, with automatic validation, async support, and interactive docs.
3. Can I use FastAPI with databases like PostgreSQL or MongoDB?
Yes, FastAPI integrates smoothly with ORMs like SQLAlchemy for PostgreSQL and tools like Motor or ODMantic for MongoDB.
4. Does FastAPI support authentication?
Absolutely! FastAPI supports OAuth2, JWT, and API key-based authentication out of the box, with built-in tools to manage security and dependencies.
5. Can FastAPI be used for full-stack applications?
Yes. You can pair FastAPI with frontend frameworks like React or Vue, or use templating engines like Jinja2. However, Django might offer more built-in features for more traditional full-stack needs.
'The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.'
Meet the full teamRelated articles
- Article
How to Use APIs in Python
Learn how to use APIs in Python. Explore HTTP methods, design API endpoints, and return JSON responses using Python and FastAPI. - Article
How to Request Webpages Using Python
Learn how to request webpages and get JSON data using Python's requests library. A step-by-step guide with practical examples for making HTTP GET and POST requests in Python. - Article
HTTP Requests in Velo
Expand your website’s capabilities by using "wix-fetch" to make various HTTP requests.
Learn more on Codecademy
- Free course
Using OpenAI APIs: Accessing OpenAI APIs from Python
Excel in OpenAI APIs using Python. Discover API key authentication, access to completions APIs via endpoints, model configurations, and control of creativity and response length.Beginner Friendly2 hours - Free course
Intro to OpenAI API
Explore OpenAI’s API and learn how to write more effective generative AI prompts that help improve your results.Beginner Friendly< 1 hour - Course
API Development with Swagger and OpenAPI
Learn how to develop APIs using Swagger tooling and the OpenAPI specification.With CertificateIntermediate< 1 hour