Structured outputs with open models on an OpenAI-compatible API

Get JSON that matches your JSON schema from open models, through the OpenAI Chat Completions and Responses APIs and the Anthropic Messages API.

The Munito AI API can make an open model answer with JSON that matches a JSON schema, or with any valid JSON object, through its OpenAI-compatible and Anthropic-compatible APIs. Send response_format on Chat Completions, text.format on Responses, or output_config.format on Messages.

APIFieldFormats
Chat Completionsresponse_formattext, json_object, json_schema
Responsestext.formattext, json_object, json_schema
Messagesoutput_config.formatjson_schema

The OpenAI SDK uses the base URL https://api.munito.ai/inference/v1, and the Anthropic SDK uses https://api.munito.ai/inference. Both read the key from MUNITO_API_KEY.

Support for each model

Modeljson_schemajson_object
qwen/qwen3.8-27bYesYes
zai-org/glm-5.3-flashYesYes
deepseek-ai/deepseek-v4-flashYesYes
deepseek-ai/deepseek-v4.1-flash-1017YesYes
aleph-alpha/kolibri-1YesYes
google/gemma-4-12bYesYes

JSON schema on Chat Completions

pip install openai
import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.munito.ai/inference/v1",
    api_key=os.environ["MUNITO_API_KEY"],
)
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "date": {"type": "string"},
        "attendees": {"type": "array", "items": {"type": "string"}},
    },
    "required": ["name", "date", "attendees"],
    "additionalProperties": False,
}
response = client.chat.completions.create(
    model="qwen/qwen3.8-27b",
    messages=[
        {"role": "system", "content": "Extract the event from the text."},
        {"role": "user", "content": "Anna and Tom meet for the budget review on 14 November."},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "event", "schema": schema, "strict": True},
    },
    max_tokens=2048,
)
event = json.loads(response.choices[0].message.content)
print(event)

The model reasons first, and the reasoning goes to the reasoning field. Only the answer in content follows the schema.

JSON mode on Chat Completions

json_object makes the model answer with a valid JSON object, with no schema. Ask for JSON in the prompt and name the keys that you want.

import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.munito.ai/inference/v1",
    api_key=os.environ["MUNITO_API_KEY"],
)
response = client.chat.completions.create(
    model="qwen/qwen3.8-27b",
    messages=[
        {"role": "user", "content": "Give the capital and the currency of Portugal as JSON with the keys capital and currency."}
    ],
    response_format={"type": "json_object"},
    max_tokens=2048,
)
print(json.loads(response.choices[0].message.content))

JSON schema on Responses

import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.munito.ai/inference/v1",
    api_key=os.environ["MUNITO_API_KEY"],
)
response = client.responses.create(
    model="qwen/qwen3.8-27b",
    input="Anna and Tom meet for the budget review on 14 November.",
    instructions="Extract the event from the text.",
    text={
        "format": {
            "type": "json_schema",
            "name": "event",
            "schema": {
                "type": "object",
                "properties": {"name": {"type": "string"}, "date": {"type": "string"}},
                "required": ["name", "date"],
                "additionalProperties": False,
            },
            "strict": True,
        }
    },
    max_output_tokens=2048,
)
print(json.loads(response.output_text))

JSON schema on Messages

pip install anthropic
import json
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.munito.ai/inference",
    api_key=os.environ["MUNITO_API_KEY"],
)
message = client.messages.create(
    model="qwen/qwen3.8-27b",
    max_tokens=2048,
    messages=[{"role": "user", "content": "Where is the Eiffel Tower?"}],
    extra_body={
        "output_config": {
            "format": {
                "type": "json_schema",
                "schema": {
                    "type": "object",
                    "properties": {"city": {"type": "string"}, "country": {"type": "string"}},
                    "required": ["city", "country"],
                    "additionalProperties": False,
                }
            }
        }
    },
)
text = next(block.text for block in message.content if block.type == "text")
print(json.loads(text))

output_config.format takes only type: "json_schema" and a schema object. Another value returns 400:

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "'output_config.format' is not supported: use a supported JSON schema"
  }
}

Errors and limits

  • A format that the API does not know returns 400, for example response_format.type: Input should be 'text'.
  • A format that the model does not support returns 400 with 'response_format' is not supported: not available for this model.
  • The output stops at max_tokens. A cut answer is not valid JSON, and finish_reason is then length. Set max_tokens high enough for the reasoning and the JSON together.

See the chat reference, the Responses reference and the Messages reference.