← Back to FastAPI map
FastAPI · Foundations

Path & query params

Values from the URL path and from the ?query=... part, converted to the type you ask for and checked against limits.

params

Overview

FastAPI reads parameters straight from your function signature. A name that appears in the path template is a path parameter; any other simple-typed parameter is a query parameter. Values arrive as strings and the type hint converts them - to int, bool, an enum and so on. When conversion or a constraint fails, the client gets a 422 response that lists exactly what was wrong.

Key concepts

Required vs optional
A query parameter without a default is required. Give it a default, or None, to make it optional.
Path() and Query()
Add validation the type alone cannot express - gt, le, min_length, pattern - plus descriptions and examples for the docs.
Annotated
page: Annotated[int, Query(ge=1)] = 1 keeps the default as a plain Python default. This is the style the FastAPI docs now recommend.
Enums and Literal
Restrict a parameter to a fixed set of values; the allowed values are listed in the docs automatically.
:path converter
{file_path:path} lets a path parameter contain slashes, for file paths and similar values.

Best practices

  • Use path parameters to identify a resource (/users/42) and query parameters to filter, sort, and paginate a collection.
  • Always cap page sizes (le=100) so a client cannot request a million rows in one call.
  • Prefer the Annotated style in new code.

Path parameters

Path params - type coercion + validation

Path
from fastapi import FastAPI, Path from enum import Enum # Basic - type declared in signature, auto-coerced @app.get("/users/{user_id}") async def get_user(user_id: int): return {"user_id": user_id} # GET /users/42 -> {"user_id": 42} (int, not "42") # GET /users/abc -> 422 Validation Error # Path() with numeric constraints @app.get("/items/{item_id}") async def get_item( item_id: int = Path(..., gt=0, le=1000, description="Item ID (1-1000)") ): return {"item_id": item_id} # String path with regex pattern @app.get("/products/{slug}") async def get_product( slug: str = Path(..., min_length=3, pattern=r"^[a-z0-9\-]+$") ): return {"slug": slug} # Enum path - restrict to specific values class Role(str, Enum): admin = "admin" user = "user" guest = "guest" @app.get("/roles/{role}") async def get_role(role: Role): return {"role": role.value} # GET /roles/admin -> OK # GET /roles/boss -> 422 Validation Error # Catch-all path (file paths etc.) @app.get("/files/{file_path:path}") async def get_file(file_path: str): return {"path": file_path} # GET /files/home/chandu/report.pdf -> OK

Query parameters

Query params - required, optional, defaults

Query
from fastapi import Query from typing import Optional, List, Annotated # Required query param (no default -> required) @app.get("/search") async def search(q: str): # GET /search?q=python return {"q": q} # Optional with defaults @app.get("/items") async def list_items( skip: int = 0, limit: int = 20, active: Optional[bool] = None # None = not filtered ): return {"skip": skip, "limit": limit, "active": active} # Query() with constraints + metadata @app.get("/products") async def list_products( q: Optional[str] = Query(None, min_length=3, max_length=50), page: int = Query(1, ge=1, description="Page number"), per_page: int = Query(20, ge=1, le=100), sort_by: str = Query("created_at", alias="sort") ): # ?sort=price -> sort_by="price" return {"page": page, "sort_by": sort_by} # List query: GET /filter?tags=py&tags=fastapi @app.get("/filter") async def filter_items( tags: List[str] = Query(default=[]) ): return {"tags": tags} # Modern Annotated syntax (preferred in Python 3.10+) @app.get("/v2/items") async def list_v2( q: Annotated[Optional[str], Query(min_length=3)] = None ): return {"q": q}

Tip: Bool parsing: ?active=true, 1, on, yes -> True; false, 0, off, no -> False.

Path + query + body - all together

Combined
# FastAPI figures out source automatically: # • in {path} -> path param # • simple type + default -> query param # • Pydantic model -> request body @app.put("/posts/{post_id}/comments") async def update_comment( post_id: int, # <- path comment: CommentUpdate, # <- body notify: bool = True # <- query ): return {"post_id": post_id, "notify": notify, **comment.model_dump()}

Path() and Query() - all constraints

Reference
Numeric constraints
gt=Ngreater than N (exclusive)
ge=Ngreater than or equal to N
lt=Nless than N (exclusive)
le=Nless than or equal to N
String constraints
min_length=Nminimum string length
max_length=Nmaximum string length
pattern=r"..."regex pattern
Enum / Literal[...]type the param to allow only these values
Metadata
description=shown in OpenAPI docs
alias=URL param name ≠ var name
deprecated=Truemarks in docs
examples=[...]example values in docs

Tip: There is no enum= option on Path() or Query(). To restrict values, annotate the parameter with an Enum subclass or Literal["a", "b"].

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...