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 [email protected]. I’d greatly appreciate your feedback! :)