FastAPI’s type annotations, while a boon for developer experience and correctness, can become a surprising bottleneck in your application’s hottest code paths.

Let’s see it in action. Imagine a simple FastAPI endpoint that processes a large list of items, each with a few fields.

from fastapi import FastAPI, Body
from pydantic import BaseModel
from typing import List

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str
    value: float

@app.post("/process_items/")
async def process_items(items: List[Item] = Body(...)):
    # Simulate some processing
    processed_count = 0
    for item in items:
        # In a real app, this might be a database lookup or complex calculation
        processed_count += item.id * item.value
    return {"processed_count": processed_count, "item_count": len(items)}

When a request hits this endpoint, FastAPI and Pydantic perform several steps:

  1. Request Body Parsing: The raw JSON request body is received.
  2. Type Validation: Pydantic iterates through the items list. For each dictionary in the list, it creates an Item model instance. This involves:
    • Checking if id is an integer, name is a string, and value is a float.
    • Performing any custom validation defined on the Item model.
    • Coercing types where possible (e.g., "123" to 123 for an integer field).
  3. Model Instantiation: A List[Item] object is constructed.
  4. Business Logic: Your process_items function then iterates over these validated Item model instances.

The validation and instantiation of Pydantic models, especially within loops for lists, can add significant overhead. For every single item in the request, Pydantic is doing work: checking keys, validating types, and creating Python objects. If you’re processing thousands of items, this adds up.

The core problem is that Pydantic’s default behavior is to be thoroughly validating and always creating model instances. This is fantastic for API correctness and general use, but for a high-throughput endpoint where you know the data is already well-formed (e.g., from a trusted internal service or a well-tested frontend), this validation becomes redundant work.

The primary lever you have to control this is Pydantic’s Config and model_config (for Pydantic v2+). Specifically, you can disable validation for certain fields or even the entire model if you’re confident in the input.

For Pydantic v1.x, you’d add a Config class:

from fastapi import FastAPI, Body
from pydantic import BaseModel
from typing import List

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str
    value: float

    class Config:
        # This setting disables all validation and model instantiation
        # for all fields in this model.
        # Use with extreme caution.
        orm_mode = True # or arbitrary_types_allowed = True if you want to bypass certain checks

For Pydantic v2.x+, you use model_config:

from fastapi import FastAPI, Body
from pydantic import BaseModel, ConfigDict
from typing import List

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str
    value: float

    # For Pydantic v2.x+
    model_config = ConfigDict(
        # This setting disables all validation and model instantiation.
        # Use with extreme caution.
        validate_assignment=False,
        arbitrary_types_allowed=True # Often used in conjunction to bypass certain checks
    )

Setting orm_mode = True (v1) or validate_assignment=False and arbitrary_types_allowed=True (v2) effectively tells Pydantic to trust the input data structure and types, skipping the expensive validation and instantiation steps for each item. FastAPI will still receive the raw data, but Pydantic won’t try to "fix" or validate it when it’s passed to your endpoint function.

The most surprising thing most people miss is that orm_mode = True (v1) or arbitrary_types_allowed=True (v2) doesn’t just allow ORM objects; it significantly bypasses Pydantic’s core data validation and parsing pipeline. If you use this on a BaseModel that represents your request body, Pydantic will simply pass the raw dictionary or list of dictionaries directly to your endpoint function without performing any checks or creating model instances. This means your endpoint function will receive List[dict] instead of List[Item], and you’ll need to handle that difference.

If you’re using Pydantic v2 and need to disable validation for a specific endpoint’s request body without disabling it for the model globally, you can use Depends with a custom callable that bypasses Pydantic validation.

The next problem you’ll encounter is that after bypassing Pydantic validation, you’ve lost the safety net. You’ll need to implement manual checks or ensure your data source is extremely reliable, or risk runtime errors from malformed data.

Want structured learning?

Take the full Fastapi course →