# Structured outputs

> Return schema-conforming JSON from MirAPI using OpenAI-style response_format with json_schema or json_object, including a worked invoice example and troubleshooting tips.

MirAPI supports OpenAI-style structured outputs: pass a `response_format` parameter and the model returns JSON that matches your schema instead of free-form text. Use it whenever your app needs machine-readable answers without parsing errors or hallucinated fields.

:::note
Structured outputs carry no extra fee — they bill as ordinary text tokens (input and output). Non-streaming responses report token counts in `usage` but no amounts; reconcile amounts in the console or via `GET /api/log/token`.
:::

## `json_schema`

Set `response_format.type` to `json_schema` and provide a `json_schema` object describing the shape the model must return:

| Parameter | Type | Description |
| --- | --- | --- |
| `type` | string | `json_schema` (schema-enforced) or `json_object` (free-form JSON). |
| `json_schema` | object | Required when `type` is `json_schema`. |

The `json_schema` object takes three fields:

| Field | Type | Description |
| --- | --- | --- |
| `name` | string | A label for the schema. |
| `strict` | boolean | When `true`, models with native support enforce the schema exactly. |
| `schema` | object | The JSON Schema the output must satisfy. |

Complete invoice-extraction example:

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

client = OpenAI(base_url="https://api.mirapi.ai/v1", api_key=os.environ["MIRAPI_API_KEY"])

schema = {
    "type": "object",
    "properties": {
        "vendor": {"type": "string", "description": "Company issuing the invoice"},
        "invoice_number": {"type": "string"},
        "date": {"type": "string", "description": "Invoice date in YYYY-MM-DD"},
        "items": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "description": {"type": "string"},
                    "quantity": {"type": "number"},
                    "unit_price": {"type": "number"},
                    "total": {"type": "number"},
                },
                "required": ["description", "quantity", "unit_price", "total"],
                "additionalProperties": False,
            },
        },
        "total_amount": {"type": "number"},
        "currency": {"type": "string"},
    },
    "required": ["vendor", "invoice_number", "date", "items", "total_amount", "currency"],
    "additionalProperties": False,
}

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "system", "content": "Extract the invoice fields from the text."},
        {"role": "user", "content": "Acme Corp, INV-2026-001, 2026-08-01. 12 widgets at $4.50 each ($54.00), 2 crates at $10.00 ($20.00). Total $74.00 USD."},
    ],
    response_format={"type": "json_schema", "json_schema": {"name": "invoice", "strict": True, "schema": schema}},
)

print(json.dumps(json.loads(response.choices[0].message.content), indent=2))
```

With `strict: true`, models that natively support strict mode enforce the schema exactly. Enforcement varies by provider — some translate the schema into their own structured-output format or treat it as a strong hint — so validate the output in your code regardless.

## `json_object`

When you only need "some valid JSON" and don't care about the exact shape, use `type: "json_object"`:

```python
response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[{"role": "user", "content": "Return JSON: name and price of the cheapest phone"}],
    response_format={"type": "json_object"},
)
```

`json_object` guarantees valid JSON, not a specific structure. Pair it with an explicit prompt listing the keys you want.

## Streaming

Structured outputs stream as standard SSE (`data:` lines, ending with `data: [DONE]`). Add `stream: true` and the model emits valid partial JSON — concatenate the deltas and parse once the stream ends:

```json
{
  "model": "deepseek-chat",
  "stream": true,
  "response_format": {
    "type": "json_schema",
    "json_schema": { "name": "invoice", "strict": true, "schema": { "type": "object" } }
  }
}
```

## Model support

Structured outputs require a model with JSON output capability. Check the `supported_parameters` field of the [model catalogue](/docs/models) (via `GET /v1/models` or the console at `https://console.mirapi.ai`). A model without the capability may reject `response_format` or return invalid JSON — see the Error handling section below.

## Error handling

Errors use the OpenAI error envelope: `{"error":{"message","type","param","code"}}`. Every message ends with a request ID — include it in support tickets. Common situations:

- **Invalid JSON Schema** — the request fails with an error describing the problem.
- **Model without JSON output** — the request is rejected; pick a model whose `supported_parameters` includes JSON output.
- **401** — missing, invalid, or unknown key; fix your credentials.
- **403** — the key is valid but rejected: insufficient balance, model whitelist, quota, or IP allowlist.
- **413** — payload too large; shrink the request.
- **429** — rate limited; retry with jittered exponential backoff (1s → 2s → 4s, capped around 30s). There is no `Retry-After` header.
- **500** — gateway or upstream failure; idempotent requests are safe to retry.

See the [errors reference](/docs/api-reference/errors) for the full envelope and recovery guidance.

## Troubleshooting

**The model returns invalid JSON anyway.** Strict enforcement varies by provider. Parse defensively: strip markdown code fences, retry once, or re-ask with the schema embedded in the prompt. As a last resort, fall back to prompting:

```text
Return ONLY a JSON object with exactly these keys:
{"vendor": string, "invoice_number": string, "total_amount": number}
Do not include markdown code fences or commentary.
```

**Extra or unexpected fields.** Add `"additionalProperties": false` to the schema and the top-level object, and give each property a `description` to guide the model.

## Related links

- [Model catalogue](/docs/models) — check `supported_parameters` for JSON output
- [Tool calling](/docs/openai-compat) — function calling for structured actions
- [Reasoning models](/docs/guides/reasoning) — reasoning controls
- [Errors reference](/docs/api-reference/errors) — error envelope and status codes