Skip to main content
An output schema is a JSON Schema you send with a run in the output_schema field. It tells TinyAgent the exact shape the final result has to come back in: which fields, what type each one is, and which ones are required. The goal still says what to find. The schema says what the answer looks like.

Why describing the shape in the goal isn’t enough

Without a schema, TinyAgent decides how to structure the result from the goal’s wording and the page in front of it. One run is usually fine. The problem shows up when you run the same goal on many pages, or on the same page over time, and feed the results to code. Each site lays out its data differently, so each run picks its own field names, types and nesting. Here’s one goal sent to two SaaS pricing pages, linear.app/pricing and notion.com/pricing, with no schema:
Both runs completed, and both returned a paragraph of Markdown inside result rather than JSON fields. Trimmed to the first plan: Linear, no schema
Notion, no schema
The same goal produced two different layouts. Linear’s plan is “Free” and its price line is “Monthly Price:”. Notion’s plan is “Free Plan”, its price line is “Price:”, and its answer ends with an extra note about yearly billing. To use either in code you’d have to parse prose, with a special case for each site. Add this schema to the same goal:
The Linear run now returns JSON in that shape:
monthly_price is a string because pricing pages show values like “$10 per user/month (billed yearly)” and “Custom”. A number field can’t hold those. Parse the string in your own code.

The schema wins over the goal

When the goal and the schema disagree, the result follows the schema. A goal that says “list 10 tools” with a schema that describes a single object returns one object, the first match. To get a list, put an array field in the schema, like plans above. The top level of a schema is always an object, so lists always live inside a field. This is also why a schema helps when you can’t control the goal’s wording, for example when another AI agent writes the goal. However the goal is phrased, the result keeps your shape.
A schema fixes the shape, not the content. A run can match your schema and still hold less than you expected. If a field’s type can’t hold what the page shows, the item is dropped instead of failing the run. A price that renders as £63.00 can’t fit a number field, so the run returns "products": [] rather than an error. The same goes for a goal that can’t be met: asked for a product that doesn’t exist on scrapeme.live/shop, with a schema requiring name and price, the run finished COMPLETED and returned {"name": "", "price": ""}. Use string for any value with symbols or units, and check for empty arrays and empty values before you use a result.

Bad schemas fail before the run starts

TinyFish checks the schema when you send the request, before a browser opens. A schema that uses something unsupported, such as oneOf or additionalProperties, returns a 400 that names the keyword and where it is, and no run is created. You find out in the first second, not after a long run comes back in the wrong shape. The check supports a subset of JSON Schema, not all of it. The Structured-Output Schema reference lists the supported types and keywords, the size and depth limits, and the usual rewrites, for example anyOf instead of oneOf, and nullable: true instead of type: ["string", "null"].

One schema, every run and every surface

The same rules apply wherever you start a run: the REST API, the SDKs, the CLI, the Playground and MCP integrations all pass the schema to the same validator. A schema you test in the Playground works unchanged in code. For many URLs, /run-batch takes output_schema once at the top level and applies it to every run in the batch. Individual items in runs can’t set their own, so one batch can’t mix shapes:
The run keeps the schema you sent. GET /v1/runs/{id} and runs.list() return it as output_schema, so you can see later which shape a stored result was asked for.

A run with a schema

This is the Linear run from above, in code: Python
TypeScript
Result

How it connects

Get a TinyAgent Run as Structured JSON

Build and run a schema in the Playground and the API

Structured-Output Schema

Supported types, keywords, limits and rewrites

Run TinyAgent Across Multiple URLs

Send the same goal to many pages

Runs

Run lifecycle and what COMPLETED means

How TinyAgent works

What happens between sending a goal and getting a result back