Structured Outputs
Return structured data from your models.
Siraya Router supports structured outputs for compatible models, ensuring responses follow a specific schema format. This feature is particularly useful when you need consistent, well-formatted responses that can be reliably parsed by your application.
Structured outputs allow you to:
- Enforce specific JSON Schema validation on model responses
- Get consistent, type-safe outputs
- Avoid parsing errors and hallucinated fields
- Simplify response handling in your application
Model Support
| Model | json_object |
json_schema |
|---|---|---|
| gemini-2.5-pro | ✅ | ✅ |
| gemini-2.5-flash | ✅ | ✅ |
| claude-sonnet-4.5 | ✅ | ✅ |
| claude-opus-4.6 | ✅ | ✅ |
| gpt-5.4-pro | ✅ | ✅ |
| gpt-4.1-mini | ✅ | ✅ |
| grok-3 | ✅ | ✅ |
| seed-2-0-lite | ✅ | ✅ (Beta) |
| deepseek-r1 | ✅ | ❌ |
| DeepSeek-V3.2 | ✅ | ❌ |
Note: DeepSeek models only support
json_objectmode. Whenjson_schemais passed, the schema constraint will be ignored and the model will behave asjson_object.
Using Structured Outputs
To use structured outputs, include a response_format parameter in your request.
json_schema
Set type to json_schema and provide your schema definition. The model will respond with a JSON object that strictly follows your schema.
Request:
curl -s -X POST https://llm.siraya.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <API_KEY>" \
-d '{
"model": "gemini-2.5-pro",
"messages": [
{"role": "user", "content": "Extract info: Alice is 30 years old and lives in Tokyo."}
],
"max_tokens": 200,
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person_info",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"],
"additionalProperties": false
}
}
}
}'
Response:
{
"id": "chatcmpl-abc123def456",
"object": "chat.completion",
"created": 1774776734,
"model": "gemini-2.5-pro",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{\"name\":\"Alice\",\"age\":30,\"city\":\"Tokyo\"}"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 29,
"completion_tokens": 124,
"total_tokens": 153
}
}
Complex Nested Schema
Structured outputs support complex schemas with nested objects, arrays, and enums.
Request:
curl -s -X POST https://llm.siraya.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <API_KEY>" \
-d '{
"model": "claude-sonnet-4.5",
"messages": [
{"role": "user", "content": "Parse this invoice: Customer John Smith (john@example.com) ordered 2x Widget A at $9.99 and 1x Widget B at $24.50. Invoice #INV-2026-001, total $44.48 USD, status paid."}
],
"max_tokens": 500,
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "invoice",
"strict": true,
"schema": {
"type": "object",
"properties": {
"invoice_id": {"type": "string"},
"customer": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"],
"additionalProperties": false
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product": {"type": "string"},
"quantity": {"type": "integer"},
"unit_price": {"type": "number"}
},
"required": ["product", "quantity", "unit_price"],
"additionalProperties": false
}
},
"total": {"type": "number"},
"currency": {"type": "string"},
"status": {"type": "string", "enum": ["paid", "pending", "overdue"]}
},
"required": ["invoice_id", "customer", "items", "total", "currency", "status"],
"additionalProperties": false
}
}
}
}'
Response:
{
"id": "chatcmpl-xyz789ghi012",
"object": "chat.completion",
"created": 1774776838,
"model": "claude-sonnet-4.5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{\"currency\":\"USD\",\"customer\":{\"email\":\"john@example.com\",\"name\":\"John Smith\"},\"invoice_id\":\"INV-2026-001\",\"items\":[{\"product\":\"Widget A\",\"quantity\":2,\"unit_price\":9.99},{\"product\":\"Widget B\",\"quantity\":1,\"unit_price\":24.5}],\"status\":\"paid\",\"total\":44.48}"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 843,
"completion_tokens": 364,
"total_tokens": 1207
}
}
json_object
For simpler use cases where you just need valid JSON without strict schema enforcement, use json_object mode.
Request:
curl -s -X POST https://llm.siraya.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <API_KEY>" \
-d '{
"model": "gemini-2.5-pro",
"messages": [
{"role": "user", "content": "Return a JSON object with keys \"greeting\" and \"language\" for: Say hello in French."}
],
"max_tokens": 300,
"response_format": {"type": "json_object"}
}'
Response:
{
"id": "chatcmpl-mno345pqr678",
"object": "chat.completion",
"created": 1774777180,
"model": "gemini-2.5-pro",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{\n \"greeting\": \"Bonjour\",\n \"language\": \"French\"\n}"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 76,
"total_tokens": 96
}
}
Streaming with Structured Outputs
Structured outputs are supported with streaming responses. The model will stream valid partial JSON that, when complete, forms a valid response matching your schema.
To enable streaming with structured outputs, add stream: true to your request:
{
"model": "gemini-2.5-pro",
"messages": [{"role": "user", "content": "Extract person info from the text."}],
"stream": true,
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person_info",
"strict": true,
"schema": { ... }
}
}
}
Note: For Claude models, streaming with
json_schemauses a "fake stream" approach — the full response is generated first, then delivered as SSE chunks. This ensures schema compliance.
json_schema vs json_object
json_object |
json_schema |
|
|---|---|---|
| Guarantees valid JSON | ✅ | ✅ |
| Guarantees correct field names | ❌ | ✅ |
| Guarantees correct field types | ❌ | ✅ |
| Guarantees required fields present | ❌ | ✅ |
| Prevents extra fields | ❌ | ✅ (strict: true) |
| Requires prompt guidance | Yes | No (schema is the constraint) |
Best Practices
- Set sufficient
max_tokens: Some models (e.g., Gemini) use reasoning tokens that count toward the limit. Setmax_tokenshigh enough to accommodate both reasoning and the JSON output. - Use
strict: true: Always setstrict: truein your json_schema to ensure the model follows your schema exactly. - Include
additionalProperties: false: Prevents the model from adding unexpected fields to the output. - Include
descriptions: Add cleardescriptionfields to your schema properties to guide the model.
Error Handling
When using structured outputs, you may encounter these scenarios:
- Model doesn't support
json_schema: DeepSeek models will ignore the schema and behave asjson_object. The request will not fail, but the output may not match your schema. - Truncated JSON: If
max_tokensis too low, the JSON output may be truncated (finish_reason: "length"). Increasemax_tokensto resolve. - Invalid schema: The model will return an error if your JSON Schema is invalid.