Skip to main content
You tell Mavera the exact shape you want. It guarantees the response matches. Every field, every type, every time. No validation code. No retries. No “I hope this parses.” You define a schema, and the API returns JSON that conforms to it — or tells you why it can’t.
Structured Outputs work with the same OpenAI SDK you already use. If you’ve used structured outputs with OpenAI, you already know how this works. Just point at Mavera.

Why Use Structured Outputs

  • Guaranteed schema adherence — the response always matches your schema. Not “usually.” Always.
  • Type-safe parsing — use Pydantic (Python) or Zod (JavaScript) to parse directly into typed objects. No json.loads() + pray.
  • Programmatic refusals — when the model can’t comply, you get a structured refusal instead of malformed JSON.
  • Simpler prompts — the schema does the heavy lifting. You don’t need to say “return JSON with these fields” in your prompt anymore.
  • No validation boilerplate — delete your schema validation code. The API handles it.

How It Works

You pass a schema via text.format. Mavera constrains the model’s output to match that schema exactly. The response comes back as valid JSON that you can parse directly into your typed objects. There are two ways to get structured output from the API: This guide covers text.format. For function calling, see Responses API — Tool Calling.

Quick Example

Score a product review. Get back a number, a sentiment label, and an explanation. Every time.
Example output:
The .parse() method automatically converts the JSON response into your Pydantic model (Python) or Zod schema (JavaScript). You get a fully typed object — not a string, not a dict.

Defining Schemas

You can define your output schema three ways. Pick whichever fits your stack.

Pydantic Models (Python)

The most Pythonic approach. Define your schema as a class, and the SDK handles the rest.
Then use it:

Zod Schemas (JavaScript)

Same idea, JavaScript flavor. Zod gives you runtime validation and TypeScript types in one shot.
Then use it:

Raw JSON Schema

For when you need direct control, aren’t using the SDK’s .parse() method, or are working in a language without a schema library.
When using raw JSON Schema with strict: true, you must set "additionalProperties": false on every object in your schema — including nested ones. This is a JSON Schema requirement for strict mode.

Supported JSON Schema Features


Advanced Examples

Make the model show its work. Useful when you need to audit reasoning or debug unexpected results.
Classify content into multiple categories, each with a confidence score. Great for content tagging, support ticket routing, or lead scoring.
Extract a full structured report with sections, each containing multiple findings. Perfect for research analysis, audit reports, or content reviews.
Force the model to pick from a fixed set of options. Eliminates the “creative” responses you didn’t ask for.

Using with Personas

This is where Mavera shines. Structured outputs give you typed data. Personas give you perspective. Combine them and you get quantified audience insights you can pipe directly into your code. Here’s a concrete example: score an ad headline from a Gen Z persona’s perspective.
Example output from a Gen Z persona:
Run the same prompt across multiple personas to compare how different demographics react. Loop through persona IDs and collect the results into a comparison table.

Structured Outputs vs JSON Mode vs Plain Text

When in doubt, use Structured Outputs. JSON mode ({"type": "json_object"}) gives you valid JSON but no schema guarantees — the model might return any shape. Structured Outputs guarantee the shape matches your schema.

How to set each mode


Tips & Best Practices

Deeply nested schemas work, but simpler schemas produce better results. If you can flatten a three-level object into two levels, do it. The model has an easier time conforming to simple structures, and your parsing code stays clean.
This is almost always better than wrapping pros and cons inside a nested analysis object.
The description parameter on your schema fields acts like a mini-prompt for each field. It tells the model what you expect, without cluttering your system prompt.
Without descriptions, the model guesses what “relevance” and “tone_match” mean. With descriptions, it knows.
Sometimes the model can’t — or shouldn’t — produce output matching your schema. Maybe the input is harmful, or the request doesn’t make sense. When this happens, output_parsed will be None and refusal will contain an explanation.Always check for refusals before accessing .output_parsed:
If a field should only have a few possible values, use an enum. This eliminates creative-but-wrong answers like “kinda positive” when you wanted "positive", "negative", or "neutral".
The model will pick from exactly these values. No variations, no surprises.
Your schema will handle the happy path. But what about empty input? Contradictory information? Extremely long text? Test these:
  • Empty or minimal input — does the model still produce valid output?
  • Ambiguous input — does it pick reasonable defaults for enum fields?
  • Long input — does the model still conform to the schema, or does it get confused?
  • Adversarial input — does the model refuse gracefully?
Build a small test suite with 5–10 edge cases and run them before shipping.

Error Handling

Invalid Schema

If your JSON Schema is malformed or uses unsupported features, the API returns a 400 error:
Fix: Check that your schema uses only supported features and includes additionalProperties: false on every object when using strict mode.

Model Can’t Conform

Rarely, the model might not be able to produce output matching your schema — usually because the schema contradicts the prompt, or the input is too ambiguous. When this happens with .parse():

status: "incomplete"

If the response is cut off because it hit max_tokens, the JSON will be incomplete and parsing will fail. Either:
  • Increase max_tokens for complex schemas
  • Simplify your schema to produce shorter output
  • Check response.status before parsing
text.format and analysis_mode cannot be used together. Use text.format for your own custom schemas, or analysis_mode for Mavera’s built-in analysis structure. If you need both, run two separate requests.

Limitations

A few things to keep in mind:
  • No streaming with .parse() — the .parse() convenience method waits for the full response. If you need streaming, use .create() with a raw JSON Schema in text.format and parse the complete response yourself.
  • Schema size — extremely large schemas (50+ fields, deep nesting) may increase latency. Break large schemas into smaller, focused ones when possible.
  • additionalProperties: false — required on every object level in strict mode. This is the most common schema error.
  • No regex patterns — use description to explain expected formats (e.g., “ISO 8601 date string”) instead of JSON Schema pattern.

See Also

Responses API

Full API reference with streaming, analysis mode, and tool calling

Tool Calling

Define functions the model can call with structured arguments

Personas

Explore 50+ pre-built personas and create custom ones

Focus Groups

Run structured outputs at scale with simulated audience panels