(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisbon

How to validate API data with Pydantic in Python

João Barros 05 de July de 2026 4 min read

When you consume a REST API in Python, you never have a guarantee that the data arrives in the expected format: a field may come back as null, a number may arrive as text, and suddenly your pipeline breaks halfway through processing. Validating API data with Pydantic solves exactly this — you define a model with the types you expect and Pydantic checks, converts and flags everything for you, with clear error messages when something does not add up. The result is more robust code and fewer hours lost hunting bugs caused by unexpected data.

Prerequisites

  • Python 3.9 or later installed.
  • Basic knowledge of dictionaries and the requests library.
  • Pydantic v2 installed with pip install pydantic requests.
  • A REST API that returns JSON (here we use a public example).

Step 1: Define the data model

The first step to validate API data is to describe the shape you expect to receive. In a Pydantic model, each field has a name and a type; if the data does not match, the library raises an error instead of letting invalid values through. This approach replaces fragile dictionary access (for example, dados['id']), which fails silently or blows up with a KeyError when the structure changes.

from pydantic import BaseModel

class Utilizador(BaseModel):
    id: int
    name: str
    email: str
    ativo: bool = True

The ativo field has a default value (True), so it is optional: if the API does not send it, the model assumes that value automatically. The remaining fields are required. Defining explicit types also acts as living documentation: anyone reading the model immediately understands what the API returns.

Step 2: Validate a single record

With the model ready, validate a dictionary with the model_validate() method. A useful detail: Pydantic automatically converts compatible types, so the string "42" becomes the integer 42 with no effort on your part. If you would rather block these automatic conversions and require exact types, Pydantic also supports strict mode, but for data coming from JSON the flexible conversion is usually what you want.

dados = {"id": "42", "name": "Ana", "email": "ana@exemplo.pt"}

utilizador = Utilizador.model_validate(dados)
print(utilizador.id)      # 42 (int, convertido de texto)
print(utilizador.ativo)   # True (valor por omissão)

Step 3: Catch validation errors

When a required field is missing or has the wrong type, Pydantic raises the ValidationError exception with a detailed description. Wrap the validation in a try/except to handle the problem without stopping the program.

from pydantic import ValidationError

mau = {"id": "abc", "name": "Rui"}  # id não numérico e email em falta

try:
    Utilizador.model_validate(mau)
except ValidationError as erro:
    print(erro)

The message points to exactly which field is problematic and why. This makes debugging far faster than discovering the error three steps later, already inside the pipeline.

Tip: validate the data right at the entry point, as soon as you receive the API response. The earlier you catch an error, the cheaper it is to fix.

Step 4: Validate a list of records

In practice, an API almost always returns a list of objects. To validate them all at once without writing a manual loop, use TypeAdapter with the type list[Utilizador]. That way, with a single line, you guarantee every record respects the contract defined in the model.

import requests
from pydantic import TypeAdapter

resposta = requests.get("https://jsonplaceholder.typicode.com/users")
resposta.raise_for_status()

validador = TypeAdapter(list[Utilizador])
utilizadores = validador.validate_python(resposta.json())

print(f"{len(utilizadores)} utilizadores validados")

The extra fields the API sends that are not in the model (such as phone or address) are simply ignored, leaving your object clean and predictable.

Check the result

Run two quick checks to confirm the validation works. First, with correct data, access an attribute and confirm the type — for example, utilizadores[0].id should return an integer. Second, force an error on purpose: change an id to non-numeric text and confirm the ValidationError is raised. If both behaviours happen, your validation layer is ready to protect the pipeline.

Conclusion

In four steps you have learned to validate API data with Pydantic: define the model, validate a record, catch errors and validate whole lists. From here you can move on to custom validations with field_validator, richer optional fields and automatic JSON Schema generation from the model. Which field of your API most needs a custom validation rule?