Skip to main content
Lesson 4 of 12 · Structured JSON Outputs

Automatic parse() Method

Lesson 4 of 12 Updated November 3, 2026 Developer
On this page
  1. The Fast Track: parse()
  2. Complete Example
  3. What parse() Returns
  4. Nested Models
  5. When to Use parse()
  6. Key Takeaways

Updated November 3, 2026. Based on the Structured Outputs documentation from xAI and the openai SDK 3.42.1: with the OpenAI SDK pointed at xAI, the method is client.chat.completions.parse, which returns a completion where the typed object is read from choices[0].message.parsed; client.chat.parse does not exist (the xAI SDK does offer chat.parse(Schema) on a conversation, which returns a response-object pair). Source: Structured Outputs.

The Fast Track: parse()

The parse() method is the simplest way to use structured outputs with the Grok API. It takes a Pydantic model as input and returns a typed object directly—no JSON to parse, no manual validation.

1

Define the Pydantic Model

You create a Python class that describes the expected structure.

2

Call client.chat.completions.parse()

You pass the Pydantic model as response_format.

3

Receive the Tuple (Response, ParsedModel)

The typed object is ready to use in your code.

Complete Example

Here’s how to extract a structured movie review:

from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(
    api_key="your-xai-key",
    base_url="https://api.x.ai/v1"
)

class MovieReview(BaseModel):
    title: str
    rating: float
    summary: str
    recommend: bool

completion = client.chat.completions.parse(
    model="grok-4.20-0309-reasoning",
    messages=[{
        "role": "user",
        "content": "Write a review of the movie Inception"
    }],
    response_format=MovieReview
)
parsed = completion.choices[0].message.parsed

# parsed is a typed MovieReview object
print(parsed.title)      # "Inception"
print(parsed.rating)     # 9.2
print(parsed.recommend)  # True
print(type(parsed))      # <class 'MovieReview'>

What parse() Returns

The parse() method returns a two-element tuple:

  1. response: The full API Response object (with metadata, tokens used, etc.)
  2. parsed: Your Pydantic model instantiated with the extracted data

You can access both as needed:

completion = client.chat.completions.parse(...)
parsed = completion.choices[0].message.parsed

# Access metadata
print(completion.usage.total_tokens)
print(completion.model)

# Access structured data
print(parsed.title)
print(parsed.rating)

Nested Models

parse() handles complex structures effortlessly. You can nest Pydantic models:

class Actor(BaseModel):
    name: str
    role: str

class Review(BaseModel):
    title: str
    director: str
    year: int
    rating: float
    main_actors: list[Actor]
    genres: list[str]
    strengths: list[str]
    weaknesses: list[str]

completion = client.chat.completions.parse(
    model="grok-4.20-0309-reasoning",
    messages=[{
        "role": "user",
        "content": "Complete analysis of the movie Interstellar"
    }],
    response_format=Review
)
parsed = completion.choices[0].message.parsed

for actor in parsed.main_actors:
    print(f"{actor.name} plays {actor.role}")

When to Use parse()

parse() is the shortest path between a model response and a usable Python object: if you work with Pydantic, this is the default choice. The method encapsulates everything—sending the schema, validating the response, instantiating the object—and hands you a typed instance directly, without json.loads or manual checks. It’s the most concise code possible, and for the vast majority of cases, this simplicity comes at no cost: you only give up fine-grained parsing control if you actually needed it, which is rare. The next lesson covers precisely those cases where manual control is justified.

Key Takeaways

  • parse() returns a tuple (Response, ParsedModel)—automatic parsing
  • The returned object is a typed and validated Pydantic instance
  • Nested models and lists of objects work natively
  • This is the recommended method for most use cases in Python
  • No need for json.loads() or manual validation