> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/fastapi/fastapi/llms.txt
> Use this file to discover all available pages before exploring further.

# Form

> API reference for the FastAPI Form parameter function

# Form

Declare a form data parameter for a path operation. Form parameters are sent as `application/x-www-form-urlencoded` or `multipart/form-data`.

```python theme={null}
from typing import Annotated
from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/login/")
async def login(
    username: Annotated[str, Form()],
    password: Annotated[str, Form()]
):
    return {"username": username}
```

## Signature

```python theme={null}
def Form(
    default: Any = Undefined,
    *,
    media_type: str = "application/x-www-form-urlencoded",
    alias: str | None = None,
    title: str | None = None,
    description: str | None = None,
    gt: float | None = None,
    ge: float | None = None,
    lt: float | None = None,
    le: float | None = None,
    min_length: int | None = None,
    max_length: int | None = None,
    pattern: str | None = None,
    examples: list[Any] | None = None,
    deprecated: bool | str | None = None,
    include_in_schema: bool = True,
    json_schema_extra: dict[str, Any] | None = None,
) -> Any
```

## Parameters

<ParamField path="default" type="Any" default="Undefined">
  Default value if the parameter field is not set.
</ParamField>

<ParamField path="media_type" type="str" default="application/x-www-form-urlencoded">
  The media type of this parameter field. Changing it would affect the generated OpenAPI, but currently it doesn't affect the parsing of the data.
</ParamField>

<ParamField path="alias" type="str | None" default="None">
  An alternative name for the parameter field. This will be used to extract the data and for the generated OpenAPI.
</ParamField>

<ParamField path="title" type="str | None" default="None">
  Human-readable title for the parameter.
</ParamField>

<ParamField path="description" type="str | None" default="None">
  Human-readable description for the parameter.
</ParamField>

<ParamField path="gt" type="float | None" default="None">
  Greater than validation. If set, the value must be greater than this. Only applicable to numbers.
</ParamField>

<ParamField path="ge" type="float | None" default="None">
  Greater than or equal validation. If set, the value must be greater than or equal to this. Only applicable to numbers.
</ParamField>

<ParamField path="lt" type="float | None" default="None">
  Less than validation. If set, the value must be less than this. Only applicable to numbers.
</ParamField>

<ParamField path="le" type="float | None" default="None">
  Less than or equal validation. If set, the value must be less than or equal to this. Only applicable to numbers.
</ParamField>

<ParamField path="min_length" type="int | None" default="None">
  Minimum length for strings.
</ParamField>

<ParamField path="max_length" type="int | None" default="None">
  Maximum length for strings.
</ParamField>

<ParamField path="pattern" type="str | None" default="None">
  RegEx pattern for strings.
</ParamField>

<ParamField path="discriminator" type="str | None" default="None">
  Parameter field name for discriminating the type in a tagged union.
</ParamField>

<ParamField path="strict" type="bool | None" default="None">
  If True, strict validation is applied to the field.
</ParamField>

<ParamField path="multiple_of" type="float | None" default="None">
  Value must be a multiple of this. Only applicable to numbers.
</ParamField>

<ParamField path="allow_inf_nan" type="bool | None" default="None">
  Allow `inf`, `-inf`, `nan`. Only applicable to numbers.
</ParamField>

<ParamField path="max_digits" type="int | None" default="None">
  Maximum number of allowed digits for numbers.
</ParamField>

<ParamField path="decimal_places" type="int | None" default="None">
  Maximum number of decimal places allowed for numbers.
</ParamField>

<ParamField path="examples" type="list[Any] | None" default="None">
  Example values for this field.
</ParamField>

<ParamField path="deprecated" type="bool | str | None" default="None">
  Mark this parameter field as deprecated. It will affect the generated OpenAPI (visible at `/docs`).
</ParamField>

<ParamField path="include_in_schema" type="bool" default="True">
  Whether to include this parameter field in the generated OpenAPI.
</ParamField>

<ParamField path="json_schema_extra" type="dict[str, Any] | None" default="None">
  Any additional JSON schema data.
</ParamField>

## Examples

### Basic Form

```python theme={null}
from typing import Annotated
from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/login/")
async def login(
    username: Annotated[str, Form()],
    password: Annotated[str, Form()]
):
    return {"username": username}
```

<Note>
  To use form data, you need to install `python-multipart`: `pip install python-multipart`
</Note>

### Form with Validation

```python theme={null}
@app.post("/signup/")
async def signup(
    username: Annotated[str, Form(min_length=3, max_length=20)],
    email: Annotated[str, Form(pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$")],
    password: Annotated[str, Form(min_length=8)]
):
    return {"username": username, "email": email}
```

### Optional Form Fields

```python theme={null}
@app.post("/contact/")
async def contact(
    name: Annotated[str, Form()],
    email: Annotated[str, Form()],
    phone: Annotated[str | None, Form()] = None,
    message: Annotated[str, Form()] = ""
):
    return {"name": name, "email": email, "phone": phone, "message": message}
```

### Form with Default Values

```python theme={null}
@app.post("/subscribe/")
async def subscribe(
    email: Annotated[str, Form()],
    newsletter: Annotated[bool, Form()] = True,
    frequency: Annotated[str, Form()] = "weekly"
):
    return {"email": email, "newsletter": newsletter, "frequency": frequency}
```

### Mixing Form and File

```python theme={null}
from fastapi import File, UploadFile

@app.post("/upload/")
async def upload(
    file: UploadFile,
    description: Annotated[str, Form()],
    public: Annotated[bool, Form()] = False
):
    return {
        "filename": file.filename,
        "description": description,
        "public": public
    }
```

<Note>
  When mixing `Form()` and `File()`, the content type automatically becomes `multipart/form-data`.
</Note>

## Common Use Cases

### Login Form

```python theme={null}
@app.post("/login/")
async def login(
    username: Annotated[str, Form()],
    password: Annotated[str, Form()],
    remember_me: Annotated[bool, Form()] = False
):
    # Authenticate user
    return {"username": username, "remember_me": remember_me}
```

### Registration Form

```python theme={null}
@app.post("/register/")
async def register(
    username: Annotated[str, Form(min_length=3, max_length=20)],
    email: Annotated[str, Form()],
    password: Annotated[str, Form(min_length=8)],
    confirm_password: Annotated[str, Form(min_length=8)],
    terms_accepted: Annotated[bool, Form()]
):
    if password != confirm_password:
        raise ValueError("Passwords do not match")
    return {"username": username, "email": email}
```

### Search Form

```python theme={null}
@app.post("/search/")
async def search(
    query: Annotated[str, Form(min_length=1)],
    category: Annotated[str | None, Form()] = None,
    sort_by: Annotated[str, Form()] = "relevance"
):
    return {
        "query": query,
        "category": category,
        "sort_by": sort_by
    }
```

### Contact Form

```python theme={null}
@app.post("/contact/")
async def contact(
    name: Annotated[str, Form(min_length=1)],
    email: Annotated[str, Form()],
    subject: Annotated[str, Form(min_length=1)],
    message: Annotated[str, Form(min_length=10)]
):
    # Send email
    return {"status": "Message sent"}
```

## Form vs JSON Body

<Warning>
  You cannot use `Form()` and `Body()` in the same path operation. They use different content types (`multipart/form-data` vs `application/json`).
</Warning>

**Form data** is sent as key-value pairs:

```
username=john&password=secret
```

**JSON body** is sent as JSON:

```json theme={null}
{
  "username": "john",
  "password": "secret"
}
```

## Installation Requirement

<Note>
  Form data requires the `python-multipart` package:

  ```bash theme={null}
  pip install python-multipart
  ```
</Note>
