Loading
Skip to content
RedRouterRedRouter
Dashboard ↗

Structured output

Request machine-readable output for extraction, classification, and application workflows. Use a model with response-format support in Models, then parse and validate the completed result in your application.

Request a JSON schema

This Chat Completions request asks for one structured finding. Set REDROUTER_API_KEY before running it.

curl --fail-with-body --output completion.json 'https://redrouter.org/v1/chat/completions' \
  -H "Authorization: Bearer $REDROUTER_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "red-cyber-1",
    "messages": [{
      "role": "user",
      "content": "Classify this observation: an internal dashboard allows any signed-in employee to edit billing settings. Return JSON."
    }],
    "max_tokens": 512,
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "finding",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "category": {"type": "string", "enum": ["access_control", "other"]},
            "summary": {"type": "string"},
            "needs_review": {"type": "boolean"}
          },
          "required": ["category", "summary", "needs_review"],
          "additionalProperties": false
        }
      }
    }
  }'

The example saves the complete response to completion.json. The JSON document is returned as a string in choices[0].message.content. Parse that string; do not parse the outer response as though it were the finding. name must contain 1–64 letters, digits, underscores, or hyphens. schema must be an object, with optional description and boolean/null strict alongside it.

RedRouter forwards the format to a capable model. It does not run a local schema validator over the answer or guarantee every JSON Schema keyword. Prefer a small object schema, explicit property types, required fields, and clear instructions. A requested schema cannot establish that an answer is factually correct.

Validate before using the result

Reject output truncated by the token limit, parse the JSON, then apply your application schema. For the request above, save the complete HTTP response to completion.json; this standard-library check verifies the expected shape:

import json

with open("completion.json", encoding="utf-8") as source:
    response = json.load(source)
choice = response["choices"][0]
if choice["finish_reason"] != "stop":
    raise ValueError("Completion did not finish normally")
value = json.loads(choice["message"]["content"])
if not isinstance(value, dict) or set(value) != {"category", "summary", "needs_review"}:
    raise ValueError("Unexpected finding fields")
if value["category"] not in ("access_control", "other"):
    raise ValueError("Invalid category")
if not isinstance(value["summary"], str) or not isinstance(value["needs_review"], bool):
    raise ValueError("Invalid finding types")
print(value)

For larger schemas, use your normal JSON Schema validation library. Treat validation failures as explicit failed jobs or bounded repair attempts. Repeated generations incur normal usage charges. See Dataset generation for handling rows independently.

JSON object mode

Use "response_format": {"type":"json_object"} when you need JSON without a fixed schema. Include a direct instruction to return JSON. This mode still requires response-format support, and you must validate any fields your application depends on. Plain text is also a response-format value; on a model without format support, omit the field entirely.

JSON in a prompt alone is instruction-following. It does not turn on structured-output capability.

Map the format across endpoints

EndpointJSON schema locationUnsupported model behavior
Chat Completionsresponse_format.json_schemaReturns 400 for a supplied response format.
Responsestext.format with type, name, schema, optional strict and descriptionReturns 400 for a supplied response format.
Messagesoutput_config.format with type: "json_schema" and schemaAdds a JSON-schema instruction to the prompt; enforcement is unavailable.

For Responses, the format object is flattened:

{
  "text": {
    "format": {
      "type": "json_schema",
      "name": "classification",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {"needs_review": {"type": "boolean"}},
        "required": ["needs_review"],
        "additionalProperties": false
      }
    }
  }
}

Messages accepts only json_schema in output_config.format, without name or strict there. Its compatibility fallback is useful for clients but is weaker than a model advertising format support. Choose a capable model when your workflow depends on structured output.

Stream and tool boundaries

A JSON prefix is usually invalid until generation completes. Buffer the final text within your application's size limit and validate after a successful terminal event. On Responses, require completed; on Messages, reject max_tokens. See Streaming.

Use Tool calling when the model should request an action. Function arguments and final-answer schemas serve different purposes: both require validation, and a strict hint is not authorization to execute a tool.

↑ ↓ to select · Enter to open · Esc to close