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:
result rather than JSON fields. Trimmed to the first plan:
Linear, no schema
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, likeplans 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.
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 asoneOf 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:
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: PythonHow it connects
Related
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