# Structured output (JSON mode)

> Get JSON from models on BigModel.io with response_format where the group passes it through, or a tool schema where response_format is dropped, then validate it.

Web version: https://bigmodel.io/docs/structured-output · Updated 2026-10-18

There are two ways to get machine-readable JSON: the OpenAI `response_format` field, or a tool whose parameters are your schema. Which one works depends on your key's group.

| Key's group | `response_format` | Tool schema |
|---|---|---|
| GLM, DeepSeek (Chat Completions only) | Passed to the model unchanged; works if the model supports it | Works if the model supports tools |
| Anthropic-platform groups, on `/v1/chat/completions` | **Dropped** | Works |
| Groups where Chat Completions is converted to Responses | **Dropped**, together with `n`, `seed`, `user` and `logprobs` | Works |

When `response_format` is dropped there is no error. The request succeeds, but the model is not constrained, so you may get prose or JSON in a Markdown fence. Always validate the output.

### Which kind of group do I have?

Send any request to `/v1/messages` with your key. A 404 that says the platform only supports `/v1/chat/completions` means a Chat Completions only group, where `response_format` passes through. Any other answer means it may be dropped, so use the tool schema option.

## Option 1: response_format

On Chat Completions only groups, use `json_schema` if the model supports it, or `json_object`. Also ask for JSON in the prompt; some models require it.

```python [Python]
import json, os
from openai import OpenAI

client = OpenAI(base_url="https://bigmodel.io/v1", api_key=os.environ["BIGMODEL_API_KEY"])

completion = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role": "system", "content": "Reply with a JSON object with keys name and year."},
        {"role": "user", "content": "Which language did Guido van Rossum create?"},
    ],
    response_format={"type": "json_object"},
)
data = json.loads(completion.choices[0].message.content)
print(data["name"], data["year"])
```

```js [Node.js]
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://bigmodel.io/v1", apiKey: process.env.BIGMODEL_API_KEY });

const completion = await client.chat.completions.create({
  model: "deepseek-v4-flash",
  messages: [
    { role: "system", content: "Reply with a JSON object with keys name and year." },
    { role: "user", content: "Which language did Guido van Rossum create?" },
  ],
  response_format: { type: "json_object" },
});
console.log(JSON.parse(completion.choices[0].message.content));
```

```bash [cURL]
curl https://bigmodel.io/v1/chat/completions \
  -H "Authorization: Bearer $BIGMODEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4-flash",
    "messages": [
      {"role": "system", "content": "Reply with a JSON object with keys name and year."},
      {"role": "user", "content": "Which language did Guido van Rossum create?"}
    ],
    "response_format": {"type": "json_object"}
  }'
```

Support differs per model: some accept `json_object` but not `json_schema`, and strict schema enforcement is not universal. Check the model vendor's documentation for the model you choose.

## Option 2: a tool schema (works on every group)

`tools` and `tool_choice` survive every conversion, so a forced tool call is the portable way to get schema-shaped JSON.

```python [Python]
import json, os
from openai import OpenAI

client = OpenAI(base_url="https://bigmodel.io/v1", api_key=os.environ["BIGMODEL_API_KEY"])

schema = {
    "type": "object",
    "properties": {"name": {"type": "string"}, "year": {"type": "integer"}},
    "required": ["name", "year"],
}
completion = client.chat.completions.create(
    model="glm-5.3",
    messages=[{"role": "user", "content": "Which language did Guido van Rossum create?"}],
    tools=[{"type": "function", "function": {"name": "answer", "description": "Return the answer", "parameters": schema}}],
    tool_choice={"type": "function", "function": {"name": "answer"}},
)
data = json.loads(completion.choices[0].message.tool_calls[0].function.arguments)
print(data)
```

```js [Node.js]
const schema = {
  type: "object",
  properties: { name: { type: "string" }, year: { type: "integer" } },
  required: ["name", "year"],
};
const completion = await client.chat.completions.create({
  model: "glm-5.3",
  messages: [{ role: "user", content: "Which language did Guido van Rossum create?" }],
  tools: [{ type: "function", function: { name: "answer", description: "Return the answer", parameters: schema } }],
  tool_choice: { type: "function", function: { name: "answer" } },
});
console.log(JSON.parse(completion.choices[0].message.tool_calls[0].function.arguments));
```

If a model does not accept a forced function, use `"tool_choice": "required"` or `"auto"` with a clear instruction.

## Validate the result

Neither option guarantees valid data from every model. Parse and validate, and retry once on failure:

```python [Python]
from pydantic import BaseModel, ValidationError

class Answer(BaseModel):
    name: str
    year: int

try:
    answer = Answer.model_validate_json(raw_json)
except ValidationError as err:
    # send err back to the model and ask it to fix the JSON, or retry
    raise
```

## Verify the setup

Run the cURL example. `choices[0].message.content` should be a JSON object such as `{"name": "Python", "year": 1991}`, with no surrounding text. If you get prose, `response_format` was dropped or is not supported by the model; switch to the tool schema.

## Troubleshooting

**The reply is prose, or JSON inside a Markdown code fence.** `response_format` was dropped on your group or ignored by the model. Use the tool schema option, or strip the fence before parsing.

**400 from the provider about `response_format`.** The model does not support the format you sent, often `json_schema`. Use `json_object`, or the tool schema option.

**The JSON is cut off.** The reply hit `max_tokens`; `finish_reason` is `length`. Raise `max_tokens` or ask for a smaller object.

**`seed`, `n` or `logprobs` seem to have no effect.** On converted groups these fields are dropped along with `response_format`. Only Chat Completions only groups pass them through, and then only if the model supports them.

**503 `api_error` "No available accounts".** The model is not served to your key's group. See [Model IDs](https://bigmodel.io/docs/model-ids).

## Related

- [Tool calling](https://bigmodel.io/docs/tool-calling) for the full tool loop.
- [Chat Completions API reference](https://bigmodel.io/docs/api-chat-completions).
- [Errors](https://bigmodel.io/docs/errors).
