---
title: "Structured Outputs from Agents"
aliases: [structured-output, agent-json-output, validated-json-output]
tags: [agent-sdk, structured-outputs, zod, pydantic, json-schema, typescript, python]
sources: [raw/Get structured output from agents.md]
created: 2026-04-17
updated: 2026-04-17
---

# Structured Outputs from Agents

Return validated, typed JSON from an agent workflow instead of free-form text. The agent still runs its full tool loop; structured output is enforced at the end.

## Why Use It

- Agents return free-form text by default — usable for chat, not for app logic
- Structured outputs give you typed data ready for databases, UIs, APIs
- SDK validates output against your schema; re-prompts on mismatch
- If retries exceed the limit, result is an error (not bad data)

## Quick Start

Pass `outputFormat` (TS) / `output_format` (Python) to `query()`:

```typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

const schema = {
  type: "object",
  properties: {
    company_name: { type: "string" },
    founded_year: { type: "number" },
    headquarters: { type: "string" }
  },
  required: ["company_name"]
};

for await (const message of query({
  prompt: "Research Anthropic and provide key company information",
  options: {
    outputFormat: { type: "json_schema", schema }
  }
})) {
  if (message.type === "result" && message.subtype === "success" && message.structured_output) {
    console.log(message.structured_output);
    // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
  }
}
```

Validated data is on `message.structured_output` in the `result` message.

## Type-Safe Schemas: Zod (TS) and Pydantic (Python)

Instead of hand-writing JSON Schema, use library types — they generate the schema and give you full autocomplete + runtime validation.

**TypeScript with Zod:**

```typescript
import { z } from "zod";

const FeaturePlan = z.object({
  feature_name: z.string(),
  summary: z.string(),
  steps: z.array(z.object({
    step_number: z.number(),
    description: z.string(),
    estimated_complexity: z.enum(["low", "medium", "high"])
  })),
  risks: z.array(z.string())
});

const schema = z.toJSONSchema(FeaturePlan);   // generate JSON Schema
// pass schema to outputFormat, then:
const parsed = FeaturePlan.safeParse(message.structured_output);
if (parsed.success) {
  const plan = parsed.data;  // fully typed FeaturePlan
}
```

**Benefits over raw JSON Schema:**
- Full type inference / type hints
- Runtime validation with `safeParse()` or `model_validate()`
- Better error messages
- Composable, reusable schemas

## outputFormat Config

```typescript
outputFormat: {
  type: "json_schema",  // only supported type
  schema: <JSON Schema object>
}
```

Supported JSON Schema features: `object`, `array`, `string`, `number`, `boolean`, `null`, `enum`, `const`, `required`, nested objects, `$ref`.

## Multi-Step Tool Use + Structured Output

The agent freely uses tools (Grep, Bash, etc.) during execution. Structured output applies only to the **final result**, not individual tool calls.

Example — TODO tracker agent:
- Agent uses `Grep` to find TODO comments, `Bash` for git blame
- Returns a single validated object with `todos[]` and `total_count`
- Optional fields (`author`, `date`) handle cases where git info is unavailable

```typescript
const todoSchema = {
  type: "object",
  properties: {
    todos: {
      type: "array",
      items: {
        type: "object",
        properties: {
          text: { type: "string" },
          file: { type: "string" },
          line: { type: "number" },
          author: { type: "string" },   // optional
          date: { type: "string" }       // optional
        },
        required: ["text", "file", "line"]
      }
    },
    total_count: { type: "number" }
  },
  required: ["todos", "total_count"]
};
```

## Error Handling

Check `message.subtype` on the `result` message:

| Subtype | Meaning |
|---------|---------|
| `success` | Output validated successfully |
| `error_max_structured_output_retries` | Agent failed to produce valid output after retries |

```typescript
if (msg.type === "result") {
  if (msg.subtype === "success" && msg.structured_output) {
    // use data
  } else if (msg.subtype === "error_max_structured_output_retries") {
    // retry with simpler prompt, fall back to unstructured, etc.
  }
}
```

## Tips for Avoiding Errors

- **Keep schemas focused** — deeply nested + many required fields = harder to satisfy
- **Make fields optional** when the task might not surface that data
- **Write clear prompts** — ambiguity makes output unpredictable

## Key Takeaways

- Pass `outputFormat: { type: "json_schema", schema }` to `query()` to get validated JSON back
- Use **Zod** (TS) or **Pydantic** (Python) to generate the schema and get strong types
- The agent runs its full tool loop freely; structured output is enforced only at the end
- Validation failures auto-retry; persistent failure → `error_max_structured_output_retries`
- Optional fields are your friend — don't require data the agent might not find
- For single-turn requests without tool use, the Claude API has its own structured outputs endpoint

## Related

- [[wiki/agent-sdk/typescript-api-reference|TypeScript API Reference]] — full `query()` signature and options
- [[wiki/agent-sdk/python-api-reference|Python API Reference]] — Python `query()` equivalent
- [[wiki/agent-sdk/overview|Agent SDK Overview]] — built-in tools, features, quick orientation
- [[wiki/agent-sdk/mcp-integration|MCP Integration]] — adding custom tools the agent can call during its loop

## Sources

- `raw/Get structured output from agents.md` — official Agent SDK docs on structured outputs
