Query Parameters Basics

Difficulty: Beginner

Query parameters are key-value pairs that appear after the question mark in a URL, like /items?skip=0&limit=10. In FastAPI, any function parameter that is not a path parameter is automatically treated as a query parameter. This makes it intuitive to add filtering, pagination, and other optional parameters to your endpoints.

When you define a function parameter with a default value, FastAPI treats it as an optional query parameter. For example, def list_items(skip: int = 0, limit: int = 10) creates two optional query parameters with defaults. Clients can call /items (uses defaults), /items?skip=20 (overrides skip only), or /items?skip=20&limit=5 (overrides both).

A parameter without a default value is required. If you write def search(q: str), the client must provide the q query parameter. A request to /search without ?q=something will result in a 422 validation error. This is a clean way to enforce that certain parameters are always provided.

FastAPI automatically converts query parameter values from strings to the annotated type. The URL always transmits values as strings, but FastAPI handles the conversion. If you declare limit: int, FastAPI converts '10' to 10. If conversion fails (like passing 'abc' for an int), FastAPI returns a 422 error with a clear message.

Boolean query parameters have special handling. FastAPI accepts various truthy and falsy values: true/false, yes/no, 1/0, on/off (case-insensitive). So /items?active=true, /items?active=yes, /items?active=1 all set active to True.

You can also receive multiple values for the same query parameter using List types. For example, def filter_items(tag: list[str] = Query(default=[])) accepts /items?tag=python&tag=fastapi and provides tag as ['python', 'fastapi']. This is useful for multi-select filters.

Query parameters are ideal for optional filters, pagination, sorting, and search functionality. They keep the URL clean by separating the resource identifier (path) from the modifiers (query). A well-designed API uses path parameters for resource identification and query parameters for everything else.

Code examples

Basic Query Parameters

from fastapi import FastAPI

app = FastAPI()

@app.get("/items")
def list_items(skip: int = 0, limit: int = 10):
    """Both parameters are optional with defaults"""
    return {"skip": skip, "limit": limit}

# GET /items           -> {"skip": 0, "limit": 10}
# GET /items?skip=20   -> {"skip": 20, "limit": 10}
# GET /items?limit=5   -> {"skip": 0, "limit": 5}
# GET /items?skip=20&limit=5 -> {"skip": 20, "limit": 5}

@app.get("/search")
def search(q: str):
    """q is required (no default value)"""
    return {"query": q}

# GET /search?q=fastapi -> {"query": "fastapi"}
# GET /search           -> 422 Error: q is required

Parameters with default values are optional. Parameters without defaults are required. FastAPI distinguishes path parameters (in the URL path) from query parameters (everything else) automatically.

Type Conversion and Booleans

from fastapi import FastAPI

app = FastAPI()

@app.get("/items")
def list_items(
    skip: int = 0,
    limit: int = 10,
    active: bool = True,
):
    return {"skip": skip, "limit": limit, "active": active}

# Boolean values accepted:
# GET /items?active=true    -> active = True
# GET /items?active=false   -> active = False
# GET /items?active=yes     -> active = True
# GET /items?active=no      -> active = False
# GET /items?active=1       -> active = True
# GET /items?active=0       -> active = False
# GET /items?active=on      -> active = True

@app.get("/convert")
def conversion_demo(price: float, count: int, name: str):
    return {"price": price, "count": count, "name": name}

# GET /convert?price=9.99&count=5&name=Widget
# price is float, count is int, name is str

FastAPI converts query strings to the annotated type. Boolean parameters accept multiple truthy/falsy string representations. Type conversion errors automatically return 422 validation errors.

Multiple Values for Same Parameter

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items")
def filter_items(
    tag: list[str] = Query(default=[]),
    category: list[str] = Query(default=[]),
):
    return {"tags": tag, "categories": category}

# GET /items?tag=python&tag=fastapi&category=web
# -> {"tags": ["python", "fastapi"], "categories": ["web"]}

# GET /items
# -> {"tags": [], "categories": []}

@app.get("/filter")
def filter_with_ids(
    ids: list[int] = Query(default=[]),
):
    return {"ids": ids}

# GET /filter?ids=1&ids=2&ids=3
# -> {"ids": [1, 2, 3]}

Use list[str] or list[int] with Query() to accept multiple values for the same parameter. The client repeats the parameter name for each value. This is common for filters like tags or categories.

Key points

Concepts covered

Query Parameters, Default Values, Required Params, Type Conversion, Multiple Values