Skip to main content
Lesson 4 of 12 · Structured Output: JSON and Schemas

Enable JSON Mode

Lesson 4 of 12 Updated November 3, 2026 Developer
On this page
  1. How JSON Mode Works
  2. Enabling in Python
  3. Enabling in TypeScript
  4. Explicit Instructions Are Not Optional
  5. Moving Format Instructions to the System Prompt
  6. Parsing the Response
  7. Key Takeaways

Updated November 3, 2026: The TypeScript examples previously imported Mistral as a default export (import Mistral from …), but the SDK only provides a named export: import { Mistral } from "@mistralai/mistralai". They also passed a string | ContentChunk[] typed content directly to JSON.parse and hardcoded the API key in the source code. Verified by compilation with SDK 3.5.0 and TypeScript 7.0.2. Source: Mistral TypeScript SDK.

How JSON Mode Works

JSON Mode is the simplest mechanism for getting structured output from Mistral. By enabling a single parameter, you guarantee that the model’s response will be valid JSON. No schema to define, no data model to write—just syntactically correct JSON on every call.

Enabling in Python

You enable this via the response_format parameter in the client.chat.complete() method. Note that the prompt itself must describe the expected structure—we’ll explain why this isn’t optional shortly.

from mistralai.client import Mistral

client = Mistral(api_key="your-api-key")

response = client.chat.complete(
    model="mistral-large-latest",
    messages=[
        {
            "role": "user",
            "content": "Describe the 3 main advantages of cloud computing. Return the result as JSON with a key 'advantages' containing a list of objects with 'title' and 'description'."
        }
    ],
    response_format={"type": "json_object"}
)

print(response.choices[0].message.content)

The output will be valid JSON, ready to use as-is:

{
  "advantages": [
    {
      "title": "Scalability",
      "description": "Dynamic adjustment of resources based on demand."
    },
    {
      "title": "Cost Reduction",
      "description": "No investment in physical infrastructure required."
    },
    {
      "title": "Accessibility",
      "description": "Access services from anywhere."
    }
  ]
}

Enabling in TypeScript

The TypeScript SDK follows the exact same logic, with camelCase naming: the parameter is responseFormat, and parsing uses JSON.parse(). One extra precaution applies: the SDK types content as string | ContentChunk[], so JSON.parse will only accept the value after you verify it is indeed a string.

import { Mistral } from "@mistralai/mistralai";

const client = new Mistral({ apiKey: process.env.MISTRAL_API_KEY });

const response = await client.chat.complete({
  model: "mistral-large-latest",
  messages: [
    {
      role: "user",
      content: "List 3 popular JavaScript frameworks in JSON with 'name' and 'useCase'."
    }
  ],
  responseFormat: { type: "json_object" },
});

const contenu = response.choices[0]?.message?.content;
if (typeof contenu !== "string") {
  throw new Error("Response does not contain JSON text.");
}
const data = JSON.parse(contenu);
console.log(data);

Explicit Instructions Are Not Optional

Here’s the point many developers miss, and the reason for most disappointment with JSON Mode: enabling the parameter isn’t enough. The mode guarantees syntax, not content. You must explicitly instruct the model, within the prompt, about the format you expect.

Compare the following two calls. In the first, no structural guidance is given: the model will produce JSON, but the keys, their names, and their nesting will vary from call to call, leaving your downstream code with no stable anchor points.

# Bad practice — no JSON instruction in the prompt
response = client.chat.complete(
    model="mistral-large-latest",
    messages=[
        {"role": "user", "content": "What are the advantages of cloud?"}
    ],
    response_format={"type": "json_object"}
)
# The model will produce JSON, but the structure will be unpredictable

In the second, a structural skeleton is inserted directly into the prompt. The model now has a target to mimic, making the output predictable across calls.

# Good practice — clear instruction in the prompt
response = client.chat.complete(
    model="mistral-large-latest",
    messages=[
        {
            "role": "user",
            "content": """What are the advantages of cloud computing?

Return your answer in JSON with the following structure:
{
  "topic": "string",
  "advantages": [
    {"title": "string", "description": "string"}
  ]
}"""
        }
    ],
    response_format={"type": "json_object"}
)

Including a structure example in your prompt guides the model toward the desired format. This isn’t an absolute guarantee like Custom Structured Outputs, but it’s more than sufficient for most use cases.

Moving Format Instructions to the System Prompt

Once the same processing repeats across hundreds of inputs, leaving format instructions in the user message becomes unmanageable: you copy them on every call, and they get mixed with the content to process. Move them to the system prompt instead.

response = client.chat.complete(
    model="mistral-large-latest",
    messages=[
        {
            "role": "system",
            "content": "You are a data extraction assistant. You ALWAYS return your responses in JSON with the keys 'entities', 'confidence', and 'source'."
        },
        {
            "role": "user",
            "content": "Extract information from this text: 'Acme Corp, founded in 2019 in Lyon, employs 250 people.'"
        }
    ],
    response_format={"type": "json_object"}
)

This separation—format instructions carried by the system role, and content to process carried by the user role—makes your code significantly more maintainable: changing the format happens in one place only.

Parsing the Response

One last habit to build: the JSON Mode response is always a JSON string inside message.content, never an object. Parsing is your responsibility, in both languages.

import json

raw = response.choices[0].message.content
data = json.loads(raw)

# Access the data
for advantage in data["advantages"]:
    print(f"- {advantage['title']}: {advantage['description']}")
const contenu = response.choices[0]?.message?.content;
if (typeof contenu !== "string") {
  throw new Error("Response does not contain JSON text.");
}
const data = JSON.parse(contenu);

data.advantages.forEach((adv: any) => {
  console.log(`- ${adv.title}: ${adv.description}`);
});

Key Takeaways

  • Enable JSON Mode with response_format: {"type": "json_object"}
  • Use client.chat.complete() (not parse())
  • Always instruct the model in the prompt about the expected JSON structure
  • The response is a JSON string—parse it with json.loads() or JSON.parse()
  • Place format instructions in the system prompt for repetitive pipelines