Salidas estructuradas con API compatibles con OpenAI: guía de JSON Schema
Guía práctica para aplicar un JSON Schema estricto en Responses y Chat Completions, validar los resultados y comprobar la compatibilidad de cada modelo y ruta.

Structured Outputs permite pedir JSON que cumpla un esquema definido, en lugar de limitarse a indicar al modelo que «devuelva JSON». En una API compatible con OpenAI, el mismo JSON Schema puede utilizarse con Responses o Chat Completions, pero cambia el envoltorio de la petición y es obligatorio verificar el modelo y la ruta reales.
El patrón seguro de producción tiene tres capas: usar un esquema strict cuando exista soporte, analizar y validar de nuevo el JSON en la aplicación y, por último, aplicar reglas de negocio deterministas. Cumplir el esquema reduce errores de formato; no demuestra que la respuesta sea correcta en términos semánticos o factuales.
Structured Outputs no es lo mismo que el modo JSON
| Método | JSON válido | Obliga a cumplir el esquema | Uso habitual |
|---|---|---|---|
| Solo instrucciones en el prompt | No está garantizado | No | Prototipos que toleran fallos de análisis |
| Modo JSON | Sí, si está soportado y la generación termina | No | JSON flexible validado por la aplicación |
Structured Outputs con strict: true |
Sí, gestionando finalización y negativas | Sí, dentro del subconjunto soportado | Extracción tipada y flujos de aplicación |
La guía de Structured Outputs de OpenAI recomienda este método frente al modo JSON cuando el modelo y el endpoint lo soportan. Un formato de respuesta estructurada sirve para devolver un objeto predecible; Function Calling sirve para solicitar una acción a la aplicación. Pueden compartir el mismo esquema, pero no el contrato de identificadores de llamada y resultados.
Define un esquema antes de elegir endpoint
Para clasificar tickets de soporte se necesitan cuatro campos:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account"]
},
"priority": {
"type": "integer",
"minimum": 1,
"maximum": 3
},
"requires_human": {
"type": "boolean"
},
"summary": {
"type": "string"
}
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
}
Marcar todas las propiedades como obligatorias y usar additionalProperties: false crea un objeto estable. No convierte todas las palabras clave de JSON Schema en portables: OpenAI documenta un subconjunto y cada proveedor compatible con OpenAI puede implementar otro o no disponer de un modo de esquema estricto.
Enviar el esquema con Responses API
Responses coloca el esquema dentro de text.format:
export MODELFLARE_API_KEY="<YOUR_API_KEY>"
export MODEL_ID="<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>"
curl -sS https://modelflare.dev/v1/responses \
-H "Authorization: Bearer $MODELFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"$MODEL_ID\",
\"input\": \"Se ha cobrado dos veces al cliente y solicita un reembolso.\",
\"text\": {
\"format\": {
\"type\": \"json_schema\",
\"name\": \"support_ticket\",
\"schema\": {
\"type\": \"object\",
\"properties\": {
\"category\": {
\"type\": \"string\",
\"enum\": [\"billing\", \"technical\", \"account\"]
},
\"priority\": {
\"type\": \"integer\",
\"minimum\": 1,
\"maximum\": 3
},
\"requires_human\": { \"type\": \"boolean\" },
\"summary\": { \"type\": \"string\" }
},
\"required\": [\"category\", \"priority\", \"requires_human\", \"summary\"],
\"additionalProperties\": false
},
\"strict\": true
}
}
}"
Envía el objeto a POST https://modelflare.dev/v1/responses con Authorization: Bearer <YOUR_API_KEY>. Responses devuelve el contenido en elementos de salida tipados. Un SDK puede ofrecer funciones auxiliares para obtener el texto agregado o el resultado ya analizado, pero un cliente HTTP debe localizar el texto completado y analizarlo como JSON; no debe asumir que existe un objeto tipado en la raíz.
Enviar el mismo esquema con Chat Completions
Chat Completions usa response_format.json_schema:
curl -sS https://modelflare.dev/v1/chat/completions \
-H "Authorization: Bearer $MODELFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"$MODEL_ID\",
\"messages\": [
{
\"role\": \"user\",
\"content\": \"Se ha cobrado dos veces al cliente y solicita un reembolso.\"
}
],
\"response_format\": {
\"type\": \"json_schema\",
\"json_schema\": {
\"name\": \"support_ticket\",
\"schema\": {
\"type\": \"object\",
\"properties\": {
\"category\": {
\"type\": \"string\",
\"enum\": [\"billing\", \"technical\", \"account\"]
},
\"priority\": {
\"type\": \"integer\",
\"minimum\": 1,
\"maximum\": 3
},
\"requires_human\": { \"type\": \"boolean\" },
\"summary\": { \"type\": \"string\" }
},
\"required\": [\"category\", \"priority\", \"requires_human\", \"summary\"],
\"additionalProperties\": false
},
\"strict\": true
}
}
}"
El JSON suele llegar como cadena de texto en choices[0].message.content; todavía hay que analizarlo.
| Objetivo | Responses API | Chat Completions |
|---|---|---|
| Contenedor | text.format |
response_format.json_schema |
| Tipo | text.format.type |
response_format.type |
| Nombre | text.format.name |
response_format.json_schema.name |
| Cuerpo | text.format.schema |
response_format.json_schema.schema |
| Indicador de modo estricto | text.format.strict |
response_format.json_schema.strict |
| Resultado | Elementos de salida / función auxiliar | choices[0].message.content |
La capa de compatibilidad de Modelflare convierte ambos envoltorios en las rutas OpenAI/Codex compatibles, pero no puede añadir Structured Outputs a un modelo de origen que no lo implemente. En otras familias que funcionan como Chat Completions por passthrough, el contrato del proveedor sigue siendo la fuente de verdad.
Validar estructura y significado por separado
{
"category": "billing",
"priority": 2,
"requires_human": true,
"summary": "El cliente informa de un cobro duplicado y solicita un reembolso."
}
Validación estructural
Un validador de JSON Schema debe confirmar las propiedades obligatorias, la ausencia de propiedades inesperadas, los tipos, los valores enumerados y los rangos. Incluso cuando el proveedor promete cumplimiento estricto, la validación en la aplicación protege frente a rutas sin soporte, errores de integración, contenido truncado y cambios futuros.
Validación semántica y de negocio
El esquema no sabe si existió un cobro duplicado, si la prioridad es correcta ni quién puede aprobar un reembolso. En flujos de alto impacto, trata el objeto como una clasificación propuesta, compáralo con registros autoritativos y aplica permisos y límites financieros en código determinista. Un JSON válido nunca debe eludir la autorización, la facturación ni la seguridad.
Gestionar salidas incompletas y excepcionales
Negativas de seguridad
Comprueba el estado y la representación de la negativa antes de buscar JSON. Una negativa de seguridad no es un error de análisis.
Truncamiento y límites
Si la generación se detiene antes del cierre, el esquema no recupera el contenido perdido. Revisa el estado de finalización y el motivo de parada, y asigna un límite de salida suficiente para el mayor objeto válido.
Palabras clave no admitidas
Empieza con objetos, matrices, tipos primitivos, valores enumerados, propiedades obligatorias y additionalProperties. Consulta la documentación antes de usar referencias, recursión, uniones complejas o validadores avanzados.
Latencia del primer esquema
Algunos proveedores preprocesan y almacenan en caché un esquema nuevo. Reutiliza esquemas estables y versionados, y separa la primera llamada de las métricas en régimen estable.
Incompatibilidad de modelo o ruta
Un endpoint puede aceptar Chat Completions normal y rechazar json_schema, ignorar strict o enviarlo a un modelo sin soporte. Un 200 con JSON no demuestra cumplimiento estricto; prueba entradas no válidas y casos límite.
Ejecutar una matriz de compatibilidad
| Prueba | Evidencia |
|---|---|
| Objeto mínimo obligatorio | Estado, modelo, ruta, objeto analizado y resultado de validación |
| Cada valor enumerado | Producción y análisis de todos los valores |
| Información ausente | Valor limitado o solicitud de aclaración |
| Entrada que activa seguridad | Representación de la negativa y manejo |
| Límite de salida bajo | Estado de finalización y truncamiento |
| Palabra clave no admitida | Error explícito o restricción ignorada |
| Envoltorios de Responses y Chat | Objetos equivalentes para la aplicación |
| Esquema repetido | Tiempo inicial frente al régimen estable |
No atribuyas diferencias al modelo si también cambian el esquema, el prompt, la región, el modo de transmisión o el límite de salida. Registra la fecha de la prueba.
Elegir endpoint después de probar el flujo de trabajo
Usa Responses si la aplicación ya trabaja con elementos de salida tipados, eventos de transmisión o un flujo amplio de herramientas. Usa Chat Completions si existe una integración estable basada en mensajes y la ruta admite response_format. Consulta Responses API frente a Chat Completions.
En ambos casos:
- conserva una única fuente versionada para JSON Schema;
- cambia solo el envoltorio, no el significado;
- valida la respuesta completada;
- ejecuta reglas de negocio deterministas después de la estructura;
- monitoriza por separado los fallos de análisis, negativas, truncamiento y compatibilidad.
Para migrar un cliente, empieza por la guía de API compatibles con OpenAI. Para eventos incompletos, consulta la guía de transmisión de AI API. Comprueba Modelos y precios y prueba Structured Outputs en la ruta exacta que utilizará la clave API.
Structured Outputs convierte una respuesta del modelo en una interfaz más predecible, pero no sustituye la verificación de los hechos, la autorización ni la lógica de facturación. La «compatibilidad con OpenAI» sigue siendo una afirmación que debe probarse función por función.