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.
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' }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'}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);| Field | Required | Notes |
|---|---|---|
type | Yes | Always json_schema |
name | Yes | A name for the schema, such as person |
schema | Yes | A 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.
- Reasoning - Combine a schema with a reasoning model
- Tool calling - Let the model call your functions
- Advanced - Configure fallbacks and provider-specific settings
Was this helpful?