Structured Outputs in the Claude Agent SDK

Get typed, schema-validated objects back instead of text. Define a Zod schema, configure outputFormat, and build data extraction pipelines that feed directly into your workflows.

3 min read
structured output agent SDK Zod output schema outputFormat JSON agent data extraction typed response SDK

When text responses are not enough

Most agent responses are text. The agent researches something, writes a summary, formats it for the reader. That works well for deliverables a human reads.

But sometimes you need the data itself — structured, parseable, ready to load into a spreadsheet, database, or downstream workflow. Asking the agent to "respond in JSON" and then parsing the text output is fragile. The agent might add explanation text before or after the JSON. Field names might vary between runs.

Structured output mode solves this. It tells the SDK to enforce a specific schema on the agent's response. You get back a validated object, not a text string.

Defining the schema with Zod

Structured outputs use the same Zod library you used for custom tools. Define the shape of the data you want:

import { z } from "zod";

const CompanyInfo = z.object({
  name: z.string(),
  founded: z.number(),
  keyProducts: z.array(z.string()),
  description: z.string(),
});

type CompanyInfoType = z.infer<typeof CompanyInfo>;

The z.infer utility gives you the TypeScript type automatically. You do not have to define it separately.

Configuring the output format

Pass the schema as an outputFormat option in your query:

import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

for await (const message of query(messages(), {
  model: "claude-sonnet-4-5",
  allowedTools: ["web_search", "web_fetch"],
  outputFormat: {
    type: "json_schema",
    jsonSchema: zodToJsonSchema(CompanyInfo, { target: "draft-07" }),
  },
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "result" && message.subtype === "success") {
    const parsed = CompanyInfo.safeParse(message.structuredOutput);
    if (parsed.success) {
      console.log(parsed.data);
    }
  }
}

Two things to note here:

The { target: "draft-07" } argument is required. Pass zodToJsonSchema(schema, { target: "draft-07" }) — without this second argument, the SDK will error out. This is a known issue in the current SDK version.

Access the structured output via message.structuredOutput. This field contains the raw parsed object. Run it through CompanyInfo.safeParse() to validate it against your schema and get typed access to the data.

A practical research pattern

Research five companies, get structured data for each:

const companies = ["Anthropic", "OpenAI", "Mistral", "Cohere", "AI21 Labs"];

const results: CompanyInfoType[] = [];

for (const company of companies) {
  async function* messages() {
    yield {
      role: "user" as const,
      content: `Research ${company} and extract the key company information.`,
    };
  }

  for await (const message of query(messages(), {
    model: "claude-sonnet-4-5",
    allowedTools: ["web_search", "web_fetch"],
    outputFormat: {
      type: "json_schema",
      jsonSchema: zodToJsonSchema(CompanyInfo, { target: "draft-07" }),
    },
    permissionMode: "bypassPermissions",
    dangerouslyAllowBypassPermissions: true,
  })) {
    if (message.type === "result" && message.subtype === "success") {
      const parsed = CompanyInfo.safeParse(message.structuredOutput);
      if (parsed.success) results.push(parsed.data);
    }
  }
}

// results is now an array of CompanyInfoType objects
// Load into a spreadsheet, database, or reporting pipeline

This pattern turns a research workflow into a data extraction pipeline. The agent searches the web for each company, extracts the information you specified, and returns structured data ready for downstream use.

> Where this fits in operator work: Competitive analysis, client research, market scanning — any workflow where you need to extract the same set of fields from multiple sources. Instead of reading a wall of text and pulling the data manually, the agent returns the fields you care about in the format you specified. The data goes directly into your deliverable.

---

Author: FractionalSkill

Keep Going

Ready to Start Building?

Pick the next step that matches where you are right now.

Tutorial
Claude Code Basics

Start with the terminal basics. A hands-on, step-by-step guide to your first 10 minutes with Claude Code.

Start the Tutorial
Guide
AI-Powered Workflows

Automate your client work. Learn how to connect AI tools into workflows that handle repetitive tasks for you.

Read the Guide
Community
Join the Community

Connect with other fractional leaders building with AI. Share workflows, get feedback, and learn from operators who are ahead of you.

Apply to Join