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

# Structured Output

> Force models to return JSON matching a specific schema

## Overview

Structured output lets you constrain AI model responses to match a JSON schema you define. Instead of parsing free-form text, you get guaranteed valid JSON that conforms to your schema — ideal for data extraction, API integrations, and any workflow where you need predictable output.

**Use structured output when you need to:**

* Extract structured data from unstructured text (names, dates, entities)
* Generate responses that feed directly into your application logic
* Ensure consistent output format across different models and providers
* Build reliable pipelines without fragile text parsing

## Prerequisites

Before using structured output, ensure you have:

* A Concentrate AI API key ([get one here](https://concentrate.ai))
* A model that supports structured output (most major models do — see [Provider Support](#provider-support))
* A valid JSON Schema defining your desired output format

## Quick Start

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.concentrate.ai/v1/responses \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "model": "gpt-5.2",
      "input": "Extract the name, age, and city from: John Smith is a 34-year-old engineer living in Austin, Texas.",
      "text": {
        "format": {
          "type": "json_schema",
          "name": "person_info",
          "schema": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "age": { "type": "integer" },
              "city": { "type": "string" }
            },
            "required": ["name", "age", "city"],
            "additionalProperties": false
          }
        }
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.concentrate.ai/v1/responses",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "model": "gpt-5.2",
          "input": "Extract the name, age, and city from: John Smith is a 34-year-old engineer living in Austin, Texas.",
          "text": {
              "format": {
                  "type": "json_schema",
                  "name": "person_info",
                  "schema": {
                      "type": "object",
                      "properties": {
                          "name": {"type": "string"},
                          "age": {"type": "integer"},
                          "city": {"type": "string"}
                      },
                      "required": ["name", "age", "city"],
                      "additionalProperties": False
                  }
              }
          }
      }
  )

  data = response.json()
  print(data)
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch("https://api.concentrate.ai/v1/responses", {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "gpt-5.2",
      input: "Extract the name, age, and city from: John Smith is a 34-year-old engineer living in Austin, Texas.",
      text: {
        format: {
          type: "json_schema",
          name: "person_info",
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              age: { type: "integer" },
              city: { type: "string" }
            },
            required: ["name", "age", "city"],
            additionalProperties: false
          }
        }
      }
    })
  });

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

The model returns JSON matching your schema:

```json theme={null}
{
  "name": "John Smith",
  "age": 34,
  "city": "Austin, Texas"
}
```

## Format Types

The `text.format` parameter supports three modes:

### json\_schema

Forces the model to return JSON conforming to a specific JSON Schema. This is the most powerful and recommended option.

```json theme={null}
{
  "text": {
    "format": {
      "type": "json_schema",
      "name": "my_schema",
      "schema": {
        "type": "object",
        "properties": {
          "result": { "type": "string" }
        },
        "required": ["result"],
        "additionalProperties": false
      }
    }
  }
}
```

**Properties:**

| Property      | Type    | Required | Description                                                            |
| ------------- | ------- | -------- | ---------------------------------------------------------------------- |
| `type`        | string  | Yes      | Must be `"json_schema"`                                                |
| `name`        | string  | Yes      | A name for the schema (alphanumeric, underscores, dots, hyphens)       |
| `schema`      | object  | Yes      | A valid JSON Schema object                                             |
| `description` | string  | No       | Description of the expected output                                     |
| `strict`      | boolean | No       | Enable strict schema enforcement (default behavior varies by provider) |

### json\_object

Forces the model to return valid JSON, but without schema enforcement. Useful when you want JSON but don't need a strict structure.

```json theme={null}
{
  "text": {
    "format": {
      "type": "json_object"
    }
  }
}
```

### text

Default mode. The model returns free-form text with no structural constraints.

```json theme={null}
{
  "text": {
    "format": {
      "type": "text"
    }
  }
}
```

## Chat Completions API

If you're using the Chat Completions endpoint (`/v1/chat/completions`), structured output uses the `response_format` parameter instead:

```json theme={null}
{
  "model": "gpt-5.2",
  "messages": [
    { "role": "user", "content": "Extract the person's info..." }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "person_info",
      "schema": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "age": { "type": "integer" }
        },
        "required": ["name", "age"],
        "additionalProperties": false
      }
    }
  }
}
```

<Info>
  The Responses API (`/v1/responses`) uses `text.format`. The Chat Completions API (`/v1/chat/completions`) uses `response_format`. The underlying behavior is the same.
</Info>

## Strict Mode

When `strict` is enabled (or when using providers like OpenAI where it's the default), the model is guaranteed to produce output that exactly matches your schema. The API will:

* Require all properties listed in `required`
* Reject any additional properties (when `additionalProperties: false`)
* Enforce type constraints exactly

<Warning>
  When using strict mode, your schema **must** include `"additionalProperties": false` on all object types. This is required by providers like OpenAI and is a common source of errors.
</Warning>

```json theme={null}
{
  "text": {
    "format": {
      "type": "json_schema",
      "name": "strict_example",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "tags": {
            "type": "array",
            "items": { "type": "string" }
          }
        },
        "required": ["name", "tags"],
        "additionalProperties": false
      }
    }
  }
}
```

## Advanced Schema Examples

### Nested Objects

```json theme={null}
{
  "text": {
    "format": {
      "type": "json_schema",
      "name": "order",
      "schema": {
        "type": "object",
        "properties": {
          "customer": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "email": { "type": "string", "format": "email" }
            },
            "required": ["name", "email"],
            "additionalProperties": false
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "product": { "type": "string" },
                "quantity": { "type": "integer", "minimum": 1 },
                "price": { "type": "number" }
              },
              "required": ["product", "quantity", "price"],
              "additionalProperties": false
            }
          },
          "total": { "type": "number" }
        },
        "required": ["customer", "items", "total"],
        "additionalProperties": false
      }
    }
  }
}
```

### Enums and Fixed Values

```json theme={null}
{
  "text": {
    "format": {
      "type": "json_schema",
      "name": "sentiment_analysis",
      "schema": {
        "type": "object",
        "properties": {
          "sentiment": {
            "type": "string",
            "enum": ["positive", "negative", "neutral"]
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "keywords": {
            "type": "array",
            "items": { "type": "string" }
          }
        },
        "required": ["sentiment", "confidence", "keywords"],
        "additionalProperties": false
      }
    }
  }
}
```

## Streaming

Structured output works with streaming. The JSON is delivered incrementally via `response.output_text.delta` events:

```typescript theme={null}
const response = await fetch("https://api.concentrate.ai/v1/responses", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "gpt-5.2",
    input: "Analyze the sentiment of: I love this product!",
    stream: true,
    text: {
      format: {
        type: "json_schema",
        name: "sentiment",
        schema: {
          type: "object",
          properties: {
            sentiment: { type: "string", enum: ["positive", "negative", "neutral"] },
            score: { type: "number" }
          },
          required: ["sentiment", "score"],
          additionalProperties: false
        }
      }
    }
  })
});
```

<Info>
  Some provider/model combinations may not support `json_schema` in streaming mode. In those cases, the API automatically falls back to `json_object` mode with a system prompt describing your schema.
</Info>

## Provider Support

Concentrate AI normalizes structured output across providers automatically. Here's how each provider handles it:

| Provider        | json\_schema | json\_object | Notes                                           |
| --------------- | :----------: | :----------: | ----------------------------------------------- |
| OpenAI          |    Native    |    Native    | Full support including strict mode              |
| Anthropic       |    Native    |   Fallback   | Falls back to system prompt for `json_object`   |
| Google (Gemini) |    Native    |       -      | Some schema keywords stripped for compatibility |
| AWS Bedrock     |    Native    |       -      | Schema is stringified for the Bedrock API       |
| Mistral         |    Native    |       -      | Uses Conversations API format                   |
| Cohere          |    Adapted   |       -      | Schema embedded in `json_object` format         |
| Cloudflare      |    Adapted   |    Adapted   | Provider-specific schema handling               |
| Z.ai            |   Fallback   |    Native    | Falls back to system prompt + `json_object`     |

**What "Fallback" means:** When a provider doesn't natively support `json_schema`, Concentrate AI automatically injects a system prompt describing your schema and uses `json_object` mode. The model will still return JSON matching your schema, but strict enforcement is best-effort rather than guaranteed.

<Info>
  You don't need to worry about these provider differences. Just set `text.format` and Concentrate AI handles the adaptation automatically.
</Info>

## Best Practices

### Schema Design

* **Keep schemas focused** — only include the fields you need
* **Use descriptions** — add a `description` to help the model understand what each field represents
* **Use enums** — constrain fields to specific allowed values when possible
* **Set `additionalProperties: false`** — required for strict mode and generally a good idea

### Choosing the Right Format

* **`json_schema`** — use when you need guaranteed structure (recommended for most use cases)
* **`json_object`** — use when you want JSON but the structure can vary
* **`text`** — use when structured output isn't needed

### Performance Tips

* Simpler schemas produce faster, more reliable results
* Deeply nested schemas may increase latency
* Combine with `max_output_tokens` to limit response size

## Troubleshooting

### Model returns text instead of JSON

**Solutions:**

* Verify `text.format` is set correctly in your request
* Ensure the model supports structured output
* Check that `type` is `"json_schema"` or `"json_object"`, not `"text"`

### Schema validation errors

**Solutions:**

* Ensure your schema is valid JSON Schema (Draft 7)
* When using strict mode, include `"additionalProperties": false` on all object types
* The `name` field must match the pattern: `^[a-zA-Z0-9_.-]+$`
* The `schema` field must be an object, not a string

### Output doesn't match schema

**Solutions:**

* Enable `strict: true` for providers that support it
* Use a provider with native `json_schema` support (OpenAI, Anthropic, Gemini)
* Simplify your schema — very complex schemas may confuse some models
* Add clear descriptions to schema properties

***

## Related Pages

<CardGroup cols={2}>
  <Card title="Create Response" icon="code" href="/docs/api-reference/endpoint/create-response">
    Main API endpoint
  </Card>

  <Card title="Request Parameters" icon="sliders" href="/docs/api-reference/endpoint/request-parameters">
    Complete parameter reference
  </Card>

  <Card title="Tool Calling" icon="wrench" href="/docs/api-reference/endpoint/tool-calling">
    Function calling with structured parameters
  </Card>

  <Card title="Chat Completions" icon="message" href="/docs/api-reference/endpoint/chat-completions">
    OpenAI-compatible endpoint
  </Card>
</CardGroup>
