Skip to content
Docs

OpenResponses Structured Outputs with AI Gateway

The OpenResponses API can constrain a response to a JSON schema, so you get parseable data instead of prose you have to extract from. Set text.format to a json_schema object with your schema. Check provider support and handle refusals, incomplete responses, and validation errors before using the result.

structured.ts
const apiKey = process.env.AI_GATEWAY_API_KEY;
 
const response = await fetch('https://ai-gateway.vercel.sh/v1/responses', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${apiKey}`,
  },
  body: JSON.stringify({
    model: 'openai/gpt-6-astra',
    input: [
      {
        type: 'message',
        role: 'user',
        content: 'Extract: John is 30 years old and lives in NYC.',
      },
    ],
    text: {
      format: {
        type: 'json_schema',
        name: 'person',
        schema: {
          type: 'object',
          properties: {
            name: { type: 'string' },
            age: { type: 'integer' },
            city: { type: 'string' },
          },
          required: ['name', 'age', 'city'],
          additionalProperties: false,
        },
      },
    },
  }),
});
 
const result = await response.json();
const message = result.output.find((item: { type: string }) => item.type === 'message');
console.log(JSON.parse(message.content[0].text));
// { name: 'John', age: 30, city: 'NYC' }
structured.py
import json
import os
 
import requests
 
api_key = os.environ["AI_GATEWAY_API_KEY"]
 
response = requests.post(
    "https://ai-gateway.vercel.sh/v1/responses",
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
    },
    json={
        "model": "openai/gpt-6-astra",
        "input": [
            {
                "type": "message",
                "role": "user",
                "content": "Extract: John is 30 years old and lives in NYC.",
            }
        ],
        "text": {
            "format": {
                "type": "json_schema",
                "name": "person",
                "schema": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "age": {"type": "integer"},
                        "city": {"type": "string"},
                    },
                    "required": ["name", "age", "city"],
                    "additionalProperties": False,
                },
            }
        },
    },
)
 
result = response.json()
message = next(item for item in result["output"] if item["type"] == "message")
print(json.loads(message["content"][0]["text"]))
# {'name': 'John', 'age': 30, 'city': 'NYC'}
structured-outputs.sh
curl -X POST "https://ai-gateway.vercel.sh/v1/responses" \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-6-astra",
    "input": [
      {
        "type": "message",
        "role": "user",
        "content": "Extract: John is 30 years old and lives in NYC."
      }
    ],
    "text": {
      "format": {
        "type": "json_schema",
        "name": "person",
        "schema": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "age": {
              "type": "integer"
            },
            "city": {
              "type": "string"
            }
          },
          "required": [
            "name",
            "age",
            "city"
          ],
          "additionalProperties": false
        }
      }
    }
  }'

The JSON arrives as text inside the output message, not as a parsed object. The output array can also carry other item types before the message, such as a reasoning item on a reasoning model, so find the message by type rather than taking output[0]:

const message = result.output.find((item: { type: string }) => item.type === 'message');
const data = JSON.parse(message.content[0].text);
FieldRequiredNotes
typeYesAlways json_schema
nameYesA name for the schema, such as person
schemaYesA JSON Schema object describing the shape you want

Set additionalProperties to false and list every property in required for the strictest results. Models follow a schema more reliably when each property has a clear name, and when nesting stays shallow.

Schema support varies by model. If a model doesn't support structured outputs, the request still succeeds but the response may not match your schema, so parse defensively rather than assuming the shape.

  • Reasoning - Combine a schema with a reasoning model
  • Tool calling - Let the model call your functions
  • Advanced - Configure fallbacks and provider-specific settings
Last updated September 8, 2026

Was this helpful?

supported.