Skip to main content

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:

python
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 and limit 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:

json
{
"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.

python
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 parameter
  • skip and limit remain optional

If you try to visit http://localhost:8000/items/?skip=20&limit=30 without providing q, FastAPI will return an error:

json
{
"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:

python
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:

python
@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.

python
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:

json
{
"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:

python
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 sets available to True
  • http://localhost:8000/items/?available=yes sets available to True
  • http://localhost:8000/items/?available=1 sets available to True
  • http://localhost:8000/items/?available=false sets available to False
  • http://localhost:8000/items/?available=no sets available to False
  • http://localhost:8000/items/?available=0 sets available to False

Multiple Values with List​

You can receive multiple values for the same query parameter:

python
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:
json
{
"q": ["foo", "bar", "baz"]
}

In Python 3.9+, you can use the built-in list directly with generics:

python
@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:

python
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:

python
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:

python
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 and per_page parameters
  • Optional filtering by category
  • Sorting options with sort_by and order 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):

python
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 with Optional[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​

Exercises​

  1. Create an API endpoint that accepts a search parameter and returns filtered results from a list based on partial matches.
  2. Implement a products API with filtering by price range using min_price and max_price parameters.
  3. Build a blog post API with parameters for filtering by author, category, and published date range.
  4. 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 feedback@compilenrun.com. I’d greatly appreciate your feedback! :)