---
title: "Structured output"
description: "Request schema-constrained JSON through the Messages API and validate the result."
canonical_url: "https://minirouter.sh/docs/structured-output"
markdown_url: "https://minirouter.sh/docs/structured-output.md"
last_updated: "2026-09-13"
---

# Structured output

Request a JSON object matching a schema through the Messages API.

## Supported models and endpoint

Use `POST /v1/messages` with `output_config.format` and one of these models:

- `openai/gpt-4.1-mini`
- `anthropic/claude-opus-5`

Schema output supports JSON responses and streaming on these models.
Your key must allow the model and an available provider that supports schema
output. No routing override or structured-output beta header is needed.

Chat Completions `response_format` and Responses `text.format` are not
currently supported for schema-constrained output. A model's ability to write
JSON in ordinary chat does not enforce a schema.

## Set your key

Create an [API key](https://minirouter.sh/key), save its recovery link, and
[add credits](https://minirouter.sh/dashboard/billing). Replace the placeholder
below and run it in your terminal before running the examples on this page.

```sh
export MINIROUTER_KEY='paste-your-api-key-here'
```

Examples use paid models. For free requests, follow the
[Auto Free guide](https://minirouter.sh/docs/free-inference).

## Send a schema request

Run this in the same terminal. The schema requires both fields and rejects
extra properties.

```sh
curl https://api.minirouter.sh/v1/messages \
  -H "Authorization: Bearer $MINIROUTER_KEY" \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
  "model": "openai/gpt-4.1-mini",
  "max_tokens": 256,
  "messages": [
    {
      "role": "user",
      "content": "Extract the city and country: I live in Paris, France."
    }
  ],
  "output_config": {
    "format": {
      "type": "json_schema",
      "schema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          }
        },
        "required": [
          "city",
          "country"
        ],
        "additionalProperties": false
      }
    }
  }
}'
```

To extract different data, change the prompt and the `properties` and
`required` entries in the schema. Keep `additionalProperties: false` for an
object with only the named fields.

## Read and validate the result

A successful Messages response contains text blocks in its `content` array.
Join the `text` values from blocks whose `type` is `text`, then parse that
string as JSON. The text for this example should have this shape:

```json
{"city":"Paris","country":"France"}
```

Check the response status and `stop_reason` before using the object. A refusal,
error or `max_tokens` stop is not a completed schema result. Validate the
parsed object against your schema in your application.

## Stream structured output

Add `"stream": true` to the same request and use `curl -N`. Collect text
from `content_block_delta` events with `delta.type: "text_delta"`. Wait for
`message_stop`, check the final stop reason, and then parse the complete text.
Individual chunks may contain only part of a JSON token.

## Troubleshooting

- `400 invalid_request_error`: check the model, endpoint and `output_config.format` shape. Simplify a schema that exceeds request limits.
- A truncated response: increase `max_tokens` or ask for a smaller object.
- A refusal: handle it separately from schema output; do not parse it as your data object.
- No eligible provider: check your key's model and provider restrictions, or try again later.

[Thinking](https://minirouter.sh/docs/reasoning) and tool controls have separate
model requirements. Start with the schema-only request above, then add supported
controls one at a time.
