> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Structured outputs

> Get JSON that matches your schema from any model on LLM Router with response_format, and know how JSON mode differs.

Structured outputs make the model return JSON that follows a JSON Schema you supply. Set `response_format` on a chat completion, and LLM Router translates it to the structured output feature of the provider that serves the request.

There are two modes:

| Mode | `response_format` | What you get |
|------|-------------------|--------------|
| **Structured outputs** | `{"type": "json_schema", "json_schema": {...}}` | JSON that follows your schema. |
| **JSON mode** | `{"type": "json_object"}` | Valid JSON, with no schema. Not available on every provider. |

Prefer structured outputs. They work on more providers and tell the model exactly which fields you expect.

## Request a schema

<CodeGroup>
```bash cURL
curl --request POST \
  --url https://api.inworld.ai/v1/chat/completions \
  --header "Authorization: Basic $INWORLD_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "model": "openai/gpt-5",
    "messages": [
      {"role": "user", "content": "Extract the event: Alice and Bob meet for lunch on Friday."}
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "calendar_event",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "day": {"type": "string"},
            "participants": {"type": "array", "items": {"type": "string"}}
          },
          "required": ["title", "day", "participants"],
          "additionalProperties": false
        }
      }
    }
  }'
```

```python Python
import json
import os
from openai import OpenAI

api_key = os.environ["INWORLD_API_KEY"]
client = OpenAI(
    base_url="https://api.inworld.ai/v1",
    api_key=api_key,
    default_headers={"Authorization": f"Basic {api_key}"},
)

response = client.chat.completions.create(
    model="openai/gpt-5",
    messages=[
        {"role": "user", "content": "Extract the event: Alice and Bob meet for lunch on Friday."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "calendar_event",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "day": {"type": "string"},
                    "participants": {"type": "array", "items": {"type": "string"}},
                },
                "required": ["title", "day", "participants"],
                "additionalProperties": False,
            },
        },
    },
)

event = json.loads(response.choices[0].message.content)
```
</CodeGroup>

The JSON arrives as a string in `message.content`. Parse it, and handle a response cut short by `finish_reason: "length"`, which can leave the JSON incomplete.

### `json_schema` fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | Yes | A name for the schema. |
| `schema` | `object` | No | The JSON Schema the output must follow. |
| `strict` | `boolean` | No | Ask the provider to enforce the schema exactly. Defaults to `false`. |
| `description` | `string` | No | What the output is for. |

A `json_schema` format without a `json_schema` object, without a `name`, or with a `schema` that is not a JSON object returns `400`.

## Provider behavior

The router sends the format the provider understands. Support differs by provider:

| Provider | `json_schema` | `json_object` |
|----------|---------------|---------------|
| OpenAI | Sent as is. | Sent as is. |
| Other OpenAI-compatible providers | Sent in OpenAI's format. Support depends on the provider and model. | Sent in OpenAI's format. Support depends on the provider and model. |
| Anthropic | Uses Anthropic's structured outputs. Only the schema is sent; `name`, `description` and `strict` are not. | Not supported. The format is dropped and the model answers normally. |
| Google | Uses Gemini's JSON response schema. | Uses Gemini's JSON response type. |

On Gemini models earlier than Gemini 3, the response format is dropped when the request also includes `tools`, because those models do not accept both.

Each provider supports its own subset of JSON Schema. If you route across providers or use [fallbacks](https://docs.inworld.ai/router/core-concepts/overview.md#fallbacks), keep schemas simple (objects, arrays, strings, numbers, booleans and enums) so they work everywhere.

## JSON mode

`{"type": "json_object"}` asks for valid JSON without a schema:

```json
{
  "model": "openai/gpt-5",
  "messages": [
    {"role": "system", "content": "Reply in JSON with the keys \"title\" and \"day\"."},
    {"role": "user", "content": "Alice and Bob meet for lunch on Friday."}
  ],
  "response_format": {"type": "json_object"}
}
```

Describe the JSON you want in the prompt, because the model has no schema to follow. Anthropic models have no JSON mode, so a request routed to one returns ordinary text. Use `json_schema` when a request can reach Anthropic.

An unrecognized `response_format.type` is treated as `text` rather than rejected.

## Next steps

<CardGroup cols={2}>
  <Card title="Tool calling" icon="plug" href="https://docs.inworld.ai/router/capabilities/tool-calling.md">
    Let the model call your functions with structured arguments.
  </Card>

  <Card title="API reference" icon="book" href="https://docs.inworld.ai/api-reference/routerAPI/chat-completions.md">
    Every chat completions parameter.
  </Card>
</CardGroup>
