Beyond the request body, APIs often need to receive information directly through the URL itself. FastAPI provides a clear and efficient way to handle two primary types of URL parameters: path parameters and query parameters, leveraging Python type hints for automatic data validation and conversion, much like it does for request bodies with Pydantic models.
Path parameters are variable segments embedded within the URL path. They are useful for identifying specific resources. For instance, in /models/{model_id}/predict
, model_id
is a path parameter intended to specify which machine learning model to use.
You define path parameters in FastAPI using the same curly brace syntax used in Python f-strings directly within the path string of your route decorator. You then declare the parameter in your path operation function using the same name and adding a type hint.
from fastapi import FastAPI, Path, HTTPException
app = FastAPI()
# A simple dictionary acting as a database for model metadata
model_db = {
"resnet50": {"framework": "PyTorch", "task": "Image Classification"},
"bert-base-uncased": {"framework": "Transformers", "task": "NLP"},
"linear-reg-v1": {"framework": "Scikit-learn", "task": "Regression"}
}
@app.get("/models/{model_id}")
async def get_model_metadata(model_id: str):
"""
Retrieve metadata for a specific model ID.
Path parameter 'model_id' is automatically validated as a string.
"""
if model_id not in model_db:
raise HTTPException(status_code=404, detail=f"Model ID '{model_id}' not found.")
return model_db[model_id]
In this example:
@app.get("/models/{model_id}")
defines model_id
as a path parameter.async def get_model_metadata(model_id: str):
declares model_id
as an argument with a type hint str
.FastAPI automatically uses the type hint str
to validate that the value passed in the URL path segment corresponding to {model_id}
is indeed a string (though path parameters are always strings initially, FastAPI uses the type hint for documentation and potential future features). If you used int
(model_id: int
), FastAPI would automatically try to convert the path segment to an integer and raise a validation error (HTTP 422 Unprocessable Entity) if it failed.
For more complex validation, you can use the Path
function from FastAPI alongside the type annotation. This allows you to specify constraints like minimum/maximum length, numeric constraints (greater than, less than), regular expressions, and provide metadata for documentation.
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(
..., # The '...' indicates the parameter is required
title="The ID of the item to get",
ge=1 # 'ge' means 'greater than or equal to' 1
)
):
"""
Retrieves an item by its ID.
'item_id' must be an integer >= 1.
"""
# Example: Replace with actual item fetching logic
if item_id > 100: # Simulate not found for IDs > 100
raise HTTPException(status_code=404, detail=f"Item ID {item_id} not found.")
return {"item_id": item_id, "name": f"Sample Item {item_id}"}
Here, Path(..., ge=1)
ensures item_id
is required and must be an integer greater than or equal to 1. FastAPI handles the validation before your function code even runs.
Query parameters are optional key-value pairs appended to the URL after a question mark (?
), separated by ampersands (&
). They are typically used for filtering, sorting, or pagination. For example, in /predictions?model_id=bert-base&limit=10
, model_id
and limit
are query parameters.
In FastAPI, any function parameter declared in your path operation function that is not part of the path definition is automatically interpreted as a query parameter.
from fastapi import FastAPI
from typing import Optional, List
app = FastAPI()
# Sample data for demonstration
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
"""
Retrieve items with optional pagination using query parameters.
'skip' defaults to 0, 'limit' defaults to 10.
"""
return fake_items_db[skip : skip + limit]
@app.get("/search/")
async def search_items(query: Optional[str] = None):
"""
Search items based on an optional query string.
'query' is an optional query parameter.
"""
results = fake_items_db
if query:
results = [item for item in fake_items_db if query.lower() in item["item_name"].lower()]
return {"query": query, "results": results}
Key points about query parameters:
skip: int
and limit: int
are not in the path /items/
, so FastAPI treats them as query parameters.int
, str
, bool
, float
, etc.) work just like with path parameters and request bodies, providing automatic data conversion and validation. If a user sends /items/?skip=abc
, FastAPI will return a 422 error because "abc" cannot be converted to an int
.skip: int = 0
) makes the query parameter optional. If the client doesn't provide it, the default value is used.Optional[Type]
or Union[Type, None]
(equivalent in newer Python versions) and set the default value to None
to make a parameter explicitly optional without a specific default other than None
. (query: Optional[str] = None
).true
, True
, 1
, on
, yes
to True
, and false
, False
, 0
, off
, no
to False
for bool
type hints.Similar to Path
, FastAPI provides a Query
function for adding more validation rules and metadata to query parameters.
from fastapi import FastAPI, Query
from typing import Optional, List
app = FastAPI()
@app.get("/models/search/")
async def search_models(
task: Optional[str] = None,
framework: Optional[str] = Query(
None, # Default value is None, making it optional
min_length=3,
max_length=50,
regex="^(PyTorch|TensorFlow|Scikit-learn|Transformers)$", # Example regex
title="ML Framework",
description="Filter models by the ML framework (case-sensitive)."
),
tags: List[str] = Query(
[], # Default is an empty list for multiple values
title="Tags",
description="Provide tags to filter models (e.g., ?tags=nlp&tags=text-generation)"
)
):
"""
Search for models based on task, framework, and tags.
Demonstrates advanced query validation and list parameters.
"""
# In a real app, you'd filter based on these validated parameters
return {
"filters": {
"task": task,
"framework": framework,
"tags": tags
},
"results": ["Model A", "Model B"] # Placeholder results
}
In this example:
framework
uses Query
to add length constraints and a regular expression for allowed values. It also includes title
and description
which enhance the auto-generated documentation.tags
uses List[str]
to accept multiple values for the same query parameter (e.g., /models/search/?tags=cv&tags=classification
). Query([])
sets the default to an empty list if no tags
parameter is provided.By using type hints, default values, Optional
, Path
, and Query
, you can define precise requirements for the data your API expects via the URL, ensuring that the inputs are valid before they reach your core logic, which is particularly important when these parameters dictate how your ML models are queried or used. FastAPI turns these declarations into robust validation and clear API documentation automatically.
© 2025 ApX Machine Learning