FastAPI Query Parameters
Introduction
When building APIs, you often need to accept additional data from clients to filter, sort, or customize the response. Query parameters provide a way to pass this data through the URL. They appear after a question mark (?
) in the URL and are formatted as key-value pairs, separated by ampersands (&
).
For example, in the URL https://api.example.com/items?skip=0&limit=10
, skip=0
and limit=10
are query parameters.
In this tutorial, you'll learn how to:
- Define query parameters in FastAPI
- Make parameters optional or required
- Add default values and validation
- Work with multiple query parameters
- Use advanced parameter features
Basic Query Parameters
In FastAPI, defining query parameters is remarkably simple. You just need to declare function parameters that aren't part of the path parameters.
Let's create a basic example:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
In this example:
skip
andlimit
are query parameters- Both parameters have default values, making them optional
- FastAPI will automatically convert the string values from the URL to the declared Python types
If you run this and visit http://localhost:8000/items/?skip=20&limit=30
, you'll see:
{
"skip": 20,
"limit": 30
}
Required vs Optional Query Parameters
By default, any parameter with a default value is optional. Parameters without default values are required.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str, skip: int = 0, limit: int = 10):
return {"q": q, "skip": skip, "limit": limit}
In this example:
q
is a required query parameterskip
andlimit
remain optional
If you try to visit http://localhost:8000/items/?skip=20&limit=30
without providing q
, FastAPI will return an error:
{
"detail": [
{
"loc": ["query", "q"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Making a Parameter Optional
What if you want a parameter to be optional but don't want to provide a default value? You can use None
as the default value and make the type "optional" using Python's typing system:
from typing import Optional
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: Optional[str] = None, skip: int = 0, limit: int = 10):
response = {"skip": skip, "limit": limit}
if q:
response.update({"q": q})
return response
In Python 3.10+, you can also use the pipe syntax for union types:
@app.get("/items/")
async def read_items(q: str | None = None, skip: int = 0, limit: int = 10):
# Function body
Query Parameter Type Validation
FastAPI automatically validates the data types of your parameters. For example, if a user tries to pass a string for a parameter declared as int
, FastAPI will return an appropriate error.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(id: int):
return {"id": id}
If someone visits http://localhost:8000/items/?id=abc
, they'll get:
{
"detail": [
{
"loc": ["query", "id"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
Advanced Query Parameter Features
Bool Conversion
FastAPI has smart conversion for boolean values:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(available: bool = False):
return {"available": available}
With this endpoint:
http://localhost:8000/items/?available=true
setsavailable
toTrue
http://localhost:8000/items/?available=yes
setsavailable
toTrue
http://localhost:8000/items/?available=1
setsavailable
toTrue
http://localhost:8000/items/?available=false
setsavailable
toFalse
http://localhost:8000/items/?available=no
setsavailable
toFalse
http://localhost:8000/items/?available=0
setsavailable
toFalse
Multiple Values with List
You can receive multiple values for the same query parameter:
from typing import List
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = None):
query_items = {"q": q}
return query_items
For this endpoint, you can provide multiple q
parameters:
http://localhost:8000/items/?q=foo&q=bar&q=baz
will give you:
{
"q": ["foo", "bar", "baz"]
}
In Python 3.9+, you can use the built-in list
directly with generics:
@app.get("/items/")
async def read_items(q: list[str] = None):
# Function body
Enhanced Validation with Query
For more advanced validation, FastAPI provides the Query
class:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str = Query(
None,
min_length=3,
max_length=50,
title="Query string",
description="Query string for the items to search"
)
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Here, we've defined:
- A minimum length of 3 characters
- A maximum length of 50 characters
- A title and description for documentation
- The parameter remains optional with a default of
None
Required with Query
You can make a parameter required by not providing a default value:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(..., min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
results.update({"q": q})
return results
The ...
ellipsis indicates that the parameter is required.
Real-World Example: Pagination API
Let's create a realistic pagination API for a product catalog:
from typing import List, Optional
from fastapi import FastAPI, Query, HTTPException
app = FastAPI()
# Mock database
products = [
{"id": i, "name": f"Product {i}", "category": "electronics" if i % 2 == 0 else "clothing"}
for i in range(1, 51) # 50 sample products
]
@app.get("/products/")
async def get_products(
page: int = Query(1, ge=1, description="Page number"),
per_page: int = Query(10, ge=1, le=100, description="Items per page"),
category: Optional[str] = Query(None, description="Filter by category"),
sort_by: str = Query("id", description="Field to sort by"),
order: str = Query("asc", description="Sort order (asc or desc)")
):
# Apply category filter if provided
filtered_products = products
if category:
filtered_products = [p for p in products if p["category"] == category]
# Apply sorting
reverse = order.lower() == "desc"
if sort_by in ["id", "name", "category"]:
sorted_products = sorted(filtered_products, key=lambda x: x[sort_by], reverse=reverse)
else:
raise HTTPException(status_code=400, detail=f"Cannot sort by {sort_by}")
# Apply pagination
start = (page - 1) * per_page
end = start + per_page
paginated_products = sorted_products[start:end]
# Return results with metadata
return {
"page": page,
"per_page": per_page,
"total": len(filtered_products),
"total_pages": (len(filtered_products) + per_page - 1) // per_page,
"data": paginated_products
}
This API demonstrates:
- Pagination with
page
andper_page
parameters - Optional filtering by
category
- Sorting options with
sort_by
andorder
parameters - Parameter validation (page ≥ 1, items per page between 1 and 100)
- Helpful API documentation through parameter descriptions
Query Parameter Aliasing
Sometimes, you might need to use a different parameter name in your code than in your API. For example, if you have a parameter with a name like item-query
(which isn't a valid Python variable name):
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Now clients should use item-query
in their requests:
http://localhost:8000/items/?item-query=foobar
Summary
Query parameters in FastAPI offer a powerful way to create flexible, dynamic API endpoints. The key takeaways from this tutorial are:
- Query parameters are defined as function parameters in your path operation function
- Parameters with default values are optional; those without are required
- FastAPI automatically converts and validates parameter types
- You can make parameters optional using
None
as the default value withOptional[Type]
- Advanced validation is available through the
Query
class - You can handle multiple values for the same parameter using Python's typing system
- Real-world APIs often use query parameters for pagination, filtering, and sorting
Additional Resources
- FastAPI official documentation on query parameters
- Pydantic's validation system, which powers FastAPI's parameter validation
- OpenAPI Specification, the standard FastAPI follows for API documentation
Exercises
- Create an API endpoint that accepts a
search
parameter and returns filtered results from a list based on partial matches. - Implement a products API with filtering by price range using min_price and max_price parameters.
- Build a blog post API with parameters for filtering by author, category, and published date range.
- Extend the pagination example to include a "next" and "prev" URL in the response for easier navigation.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)