---
title: "Use Zod or Pydantic to check every input · Agent Wranglers"
url: https://agentwranglers.se/playbook/stack/web-frameworks/zod-pydantic
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Use Zod or Pydantic to check every input

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/stack/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Web frameworks](https://agentwranglers.se/playbook/stack/web-frameworks)
- Rule: Parse every request body, query string, webhook and file at the boundary with a schema library, Zod in TypeScript or Pydantic in Python.
- Cost: Open source. Both are MIT licensed and free to use. Pydantic's company sells separate paid products that you do not need.
- How: Zod 1.0 came out in March 2020, and version 4.6.7 shipped in October 2026. It has about 207 million weekly downloads. Pydantic 1.0 came out in October 2019, and version 2.14.1 shipped in October 2026, with about 243 million weekly downloads. Both are MIT licensed. One schema gives the check, the type and the error message, as [accept only known fields](https://agentwranglers.se/playbook/api-design/requests-and-responses/strict-input) asks. Choose something else when the framework has its own checks, such as Django forms, or when another schema library is already in use.
- Check: Every route handler reads its input through a schema parse call. A test sends each route one unknown field and one wrong type, and gets 400 or 422.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### TypeScript example

```ts
import { z } from 'zod';

const NewOrder = z.strictObject({ productId: z.string().uuid(), quantity: z.number().int().min(1).max(99) });

app.post('/orders', (req, res) => {
  const result = NewOrder.safeParse(req.body);
  if (!result.success) return res.status(400).json({ errors: result.error.issues });
  res.status(201).json(createOrder(result.data));
});
```

### Python example

```python
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field

class NewOrder(BaseModel):
    model_config = ConfigDict(extra="forbid")
    product_id: UUID
    quantity: int = Field(ge=1, le=99)
```

## Sources

- [Zod](https://zod.dev/)
- [Pydantic](https://pydantic.dev/docs/validation/latest/get-started/)

## Related rules

- [Use Express for a Node.js server](https://agentwranglers.se/playbook/stack/web-frameworks/express) in Software choices
- [Use FastAPI for a Python API](https://agentwranglers.se/playbook/stack/web-frameworks/fastapi) in Software choices
- [Accept only fields you know](https://agentwranglers.se/playbook/api-design/requests-and-responses/strict-input) in API design

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/stack/web-frameworks/zod-pydantic">Agent Wranglers software choices playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Use FastAPI for a Python API](https://agentwranglers.se/playbook/stack/web-frameworks/fastapi)
- Next: [Use plain CSS before adding a CSS framework](https://agentwranglers.se/playbook/stack/web-frameworks/plain-css)
- [Web frameworks](https://agentwranglers.se/playbook/stack/web-frameworks)
