Skip to content

Structured outputs

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.

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:

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.

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

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.

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:

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

Structured outputs require a model with JSON output capability. Check the supported_parameters field of the model catalogue (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.

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 for the full envelope and recovery guidance.

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:

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.