NV ToolkitFormat · Validate · Convert

Code Generators

JSON to Pydantic Model Generator

Paste a JSON sample, get clean Pydantic BaseModel classes — nested models named from your keys, Optional[...] where the sample had null, no positional-argument helper code to fight.

In short

Generate Pydantic BaseModel classes from a JSON sample: nested models, List[...] fields, Optional[...] for nulls — quicktype-powered, client-side, models only by default.

By NaveenKumar T · Updated

  • BaseModel classes, nested models named from keys
  • Optional[...] where the sample had null
  • List[...] for arrays of objects

Runs entirely in your browser — nothing you paste or open here is uploaded, logged, or stored.How we handle data →

Example

JSON to Pydantic: input and output

A FastAPI-style order request: a nested customer object, an array of line-item objects, a null coupon field and an ISO timestamp — the shapes that decide which fields become Optional and which become List[Item].

Input · JSON
{
  "order_id": "ORD-10428",
  "customer": { "name": "Ada Lovelace", "email": "ada@example.com" },
  "items": [
    { "sku": "TS-BLK-M", "qty": 2, "price": 18.0 },
    { "sku": "MUG-01", "qty": 1, "price": 12.5 }
  ],
  "coupon": null,
  "placed_at": "2026-09-18T09:30:00Z"
}
Output · Pydantic models
from pydantic import BaseModel
from typing import List
from datetime import datetime


class Customer(BaseModel):
    name: str
    email: str


class Item(BaseModel):
    sku: str
    qty: int
    price: float


class Root(BaseModel):
    order_id: str
    customer: Customer
    items: List[Item]
    coupon: None
    placed_at: datetime

items became List[Item] with its own nested Item(BaseModel), customer became its own Customer(BaseModel), coupon (sampled only as null) came back typed None, and placed_at — a full ISO date-time string — was typed datetime, matching Pydantic's own built-in datetime-string parsing.

Learn more

From JSON to BaseModel

Inference follows the same shape-from-values approach as every generator on this site: a JSON object becomes a BaseModel subclass, each key becomes a typed field, and a nested object becomes its own BaseModel subclass named from the key it was found under — Customer, Item — referenced by name from the parent model exactly the way you'd nest them by hand. Field NAMES are converted to Python's snake_case convention by default, which is standard Pythonic style but has one honesty caveat covered in the FAQ above: without an explicit alias, the model's field name has to match the incoming JSON key for automatic parsing to actually populate it.

By default this page generates "Models only" — plain BaseModel classes and nothing else — rather than also including the from_dict/to_dict-style helper functions quicktype can optionally add. That default is deliberate: verified against the real generator output, quicktype's helper mode constructs the model with POSITIONAL arguments (Root(order_id, customer, items, ...)), and pydantic.BaseModel's constructor only accepts KEYWORD arguments — so the helper code that quicktype would otherwise generate does not actually run against a real Pydantic model. Models-only avoids shipping code that looks correct and fails the moment you call it; use Pydantic's own Root.model_validate(data) (v2) or Root.parse_obj(data) (v1) instead, which is what the usage example below shows.

JSON value → Python type hint
JSON valueGenerated type hint
ObjectA nested BaseModel class
Array of objectsList[Item]
Array of scalarsList[str] / List[int] / …
Stringstr
Integerint
Decimal numberfloat
Booleanbool
null only (every sample)None
null in one sample, a value in anotherOptional[str] (or the matching type)

Pydantic models vs plain classes vs dataclasses

Three ways to give a JSON payload a Python type, in roughly increasing order of built-in behavior: a plain class with type hints and hand-written (or quicktype-generated, via JSON to Python) from_dict/to_dict methods does exactly what you tell it and nothing more; a @dataclass gets a generated __init__, __repr__ and __eq__ for free from the standard library but still performs zero validation on construction; a pydantic.BaseModel validates every field the moment you construct it, coercing where it reasonably can (a numeric string into an int, for instance) and raising a detailed ValidationError where it can't. That validation-on-construction is the entire reason to reach for Pydantic over a plain class or dataclass: the model itself becomes the place where "is this data actually shaped right" gets answered, instead of that check being scattered across the code that happens to consume the object.

Pydantic BaseModel vs plain class vs dataclass
Plain class (JSON to Python)@dataclassPydantic BaseModel (this page)
Validates on constructionOnly if from_dict asserts typesNoYes
Needs a dependencyNoNo (standard library)Yes — pydantic
Auto __init__/__repr__/__eq__Hand-writtenYes, generatedYes, generated
FastAPI request/response integrationNoNoYes, natively

Using the models with FastAPI and requests

The most common home for a generated Pydantic model is a FastAPI request or response type, where the framework itself calls the validation for you on every request; the second most common is validating a response from an outbound call with the requests or httpx library. Both usages are shown below for Pydantic v2's API; if your project is still on v1, swap model_validate for parse_obj and model_dump for dict.

python
from fastapi import FastAPI
import requests
from models import Root  # the generated file

app = FastAPI()

@app.post("/orders")
def create_order(order: Root):
    # FastAPI already validated + parsed the body into `order` here.
    return {"received": order.model_dump()}

# Validating an outbound API response the same way:
response = requests.get("https://api.example.com/orders/ORD-10428")
order = Root.model_validate(response.json())  # v1: Root.parse_obj(...)
print(order.customer.email)

Help

Frequently asked questions

Everything you need to know about the JSON to Pydantic.

01Pydantic v1 or v2 — which does the output target?

Neither exclusively — the default "Models only" output is plain BaseModel subclasses with ordinary Python type hints (str, int, Optional[str], List[Item], nested classes), and nothing about that syntax is v1- or v2-specific: no model_config, no v2-only ConfigDict, no v1-only class Config. That means the generated file runs unchanged on either major version. Where the versions actually differ is how YOU use the models afterward: Pydantic v2 parses a dict with Root.model_validate(data) and serializes with .model_dump(), while v1 uses Root.parse_obj(data) and .dict() — both shown in the usage example below, since which one applies depends on what's installed in your project, not on anything this generator controls.

02How do nulls become Optional[...] and what about missing keys?

A field whose only sampled value is null is typed as plain None (Python's None type-hint, meaning the field can only ever legally hold None) — technically correct for that one sample but not very useful. Paste a second document where the field carries a real value and it widens to Optional[str] (or whichever type fits), which is what you actually want for a genuinely nullable field. A key that's present in one sample and absent from another gets the same Optional[...] treatment, since Pydantic (like the rest of Python's type-hint ecosystem) doesn't distinguish "missing" from "present but None" at the type-hint level the way TypeScript's optional (?) syntax does — both need Optional to be a correctly typed model.

03Are field names converted to snake_case, and do aliases keep the JSON names?

Field names are Pythonized to snake_case by default (orderId in the JSON sample becomes order_id on the model) — that part matches normal Python style. What the generator does NOT add is a Pydantic Field(alias=...) mapping back to the original JSON key, so if your real payload's key doesn't already match the generated Python name, parsing the raw dict directly (Root(**payload) or Root.model_validate(payload)) will not populate that field — Pydantic looks for the field name (or an explicit alias) in the input, and there isn't one here. For a payload with camelCase or otherwise non-Python key names, add Field(alias="orderId") to the affected fields yourself, and set model_config = ConfigDict(populate_by_name=True) (v2) or class Config: allow_population_by_field_name = True (v1) if you also want to construct instances using the Python attribute name directly.

04Why are there no validators or Field constraints?

Because a single sample value carries no information about a CONSTRAINT — "ada@example.com" tells the generator the field is a string, not that every value in production must be a valid email address, and "36" tells it the field is an int, not that age must be non-negative. Add Field(gt=0), Field(max_length=140), an EmailStr type from pydantic (swapping in for str), or a full @field_validator by hand once you know the real business rules; a sample-driven generator has no way to infer them safely. If your source of truth is a specification that DOES state its constraints — an OpenAPI schema, a hand-written JSON Schema — validate against that directly with [JSON Schema Validator](/json-schema-validator) instead of trying to encode every rule into the Pydantic model by hand.

05How is this different from JSON to Python?

[JSON to Python](/json-to-python) generates plain classes with type hints and explicit from_dict/to_dict staticmethods that manually walk the object and assert each field's type as they go — no third-party dependency, works with the standard library alone. This page generates pydantic.BaseModel subclasses instead: validation happens automatically on construction (Root(**data) raises a detailed ValidationError on a bad shape, rather than your code needing to call a separate from_dict helper), and the models integrate directly with FastAPI, which reads BaseModel type hints to generate request/response validation and OpenAPI docs for free. Pick plain classes when you want zero dependencies; pick Pydantic when your project already depends on it (FastAPI virtually guarantees that) or you want validation built into construction itself.

Keep working

Related tools