When you actually need this
You’re calling an undocumented internal API and the response shape is “whatever the senior dev was eating that day”. You want types so the rest of your code can be checked statically. You asked an LLM to return structured JSON for a workflow and you need a Pydantic model to validate the response against. You’re integrating with a Stripe webhook and the OpenAPI spec is 400 pages — you just want types for the three event shapes you actually handle.
Hand-writing types from a JSON sample is busywork and error-prone. Three keys deep into a nested response and you’ve already typoed a field. This generates the boilerplate; you keep the brain cells for the actual logic.
Gotchas we keep hitting
One sample is not a complete schema. If the API returns null for a field 5% of the time and your sample didn’t include that case, the generated type will say the field is required. The first nullable response in production crashes. Strict-mode toggle (when we add it) will infer T | null as soon as it sees one null; for now, manually mark fields you know can be null.
Optional vs required from arrays of objects. When you paste an array, fields present in every element are required, fields missing in some are optional. This is correct and useful — but it does mean if your array has a single weird element, all “required” fields suddenly get optional. Sanity-check the output if the input array is small.
Pydantic v1 vs v2 syntax. We emit v2: class User(BaseModel) with type-only annotations. v1 used Config inner classes and slightly different validators. If you’re stuck on v1 (legacy codebase), the output won’t run as-is — port to v2 first or use a converter.
Go zero values vs nullable. Go has no concept of null. By convention, optional fields use pointer types (*string rather than string) so nil distinguishes “absent” from “empty”. The generator picks pointer types for fields that were null or missing in the sample. If you intended a non-null default, swap to value types manually.
Java requires Jackson runtime. The generated POJOs use Jackson annotations (@JsonProperty, @JsonInclude). They won’t compile without com.fasterxml.jackson.core:jackson-databind on the classpath. If you’re using Gson or a different lib, ignore the annotations and adapt.
JSON Schema output is draft 7. Older validators (draft 4) reject keywords like examples and use slightly different syntax for enum. If your downstream is on draft 4, downgrade manually or use a converter.
With AI in the loop
The day-to-day pattern: ask an LLM to return JSON in a specific shape, paste a sample of the response here, copy the generated Pydantic / Zod model, drop it into your runtime validator. Now the model can return whatever it wants and you’ll catch malformed output at the boundary instead of three calls deep.
Reverse pattern: paste a real API response, generate types, paste the type definitions back into the LLM prompt as system context. The model now writes code that uses the right field names and types — saves a lot of “actually it’s user_id not userId” iterations.
For “the LLM should return one of N variants” prompts, generate types for each variant separately and union them in code. quicktype-core’s union inference is workable but conservative; explicit unions in your runtime types are clearer for both the model and the reader.