Gradual Migration to the Responses API
On this page
Updated November 3, 2026: Parameter mapping previously indicated that
max_tokenskeeps its name in the Responses API; xAI’s table changes it tomax_output_tokens, and usage counters also change names. Source: Comparison with Chat Completions.
Why and when to migrate
The Chat Completions endpoint is marked legacy. It will continue to work, but new features will not be added to it. If your application needs server-side tools (web search, code execution), conversation storage, or encrypted reasoning, migrating to the Responses API becomes necessary.
The good news: this migration can happen gradually. You do not need to rewrite everything at once.
Step 1: Identify the features you use
Before migrating, take stock of what your application uses with Chat Completions:
# Audit your existing code
# Look for these patterns:
# 1. Basic calls (simple text)
client.chat.completions.create(model=..., messages=...)
# 2. Streaming
client.chat.completions.create(..., stream=True)
# 3. Function calling
client.chat.completions.create(..., tools=[...])
# 4. Vision (images)
# messages with content as array [text, image_url]
# 5. Generation parameters
# temperature, max_tokens, top_p, stop, seed
Basic calls and streaming are the simplest to migrate. Function calling requires adapting the format. Vision works similarly.
Step 2: Migrate basic calls
Migrating a simple call follows this pattern:
# BEFORE — Chat Completions
response = client.chat.completions.create(
model="grok-4.20-0309-reasoning",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
]
)
texte = response.choices[0].message.content
# AFTER — Responses API
response = client.responses.create(
model="grok-4.20-0309-reasoning",
instructions="You are a helpful assistant.",
input="Hello!"
)
texte = response.output_text
Field mapping
The main changes are:
messageswith rolesystem→instructions(dedicated parameter)messageswith roleuser→input(simple string)response.choices[0].message.content→response.output_textmax_tokens→max_output_tokens, according to xAI’s mapping table;temperaturekeeps its nameresponse.usage.prompt_tokensandcompletion_tokens→input_tokensandoutput_tokens(details:input_tokens_details.cached_tokens,output_tokens_details.reasoning_tokens)
Step 3: Migrate multi-turn conversations
This is where the Responses API simplifies things. Instead of reconstructing history with every request, you chain them by ID:
# BEFORE — Chat Completions (manage history yourself)
historique = [
{"role": "system", "content": "You are a helpful assistant."}
]
def envoyer_message(texte: str) -> str:
historique.append({"role": "user", "content": texte})
response = client.chat.completions.create(
model="grok-4.20-0309-reasoning",
messages=historique
)
reponse = response.choices[0].message.content
historique.append({"role": "assistant", "content": reponse})
return reponse
# AFTER — Responses API (server manages history)
dernier_id = None
def envoyer_message(texte: str) -> str:
response = client.responses.create(
model="grok-4.20-0309-reasoning",
instructions="You are a helpful assistant.",
input=texte,
previous_response_id=dernier_id
)
dernier_id = response.id
return response.output_text
The code is simpler and you no longer have to manage the messages array manually.
Step 4: Take advantage of new features
Once migrated, you can leverage features exclusive to the Responses API:
Built-in web search
response = client.responses.create(
model="grok-4.20-0309-reasoning",
input="What are the latest developments in generative AI?",
tools=[{"type": "web_search"}]
)
# The response includes citations with source URLs
Response storage
response = client.responses.create(
model="grok-4.20-0309-reasoning",
input="Analyze this document...",
store=True # Keeps for 30 days
)
# Retrieve later
stored = client.responses.retrieve(response.id)
Encrypted reasoning
response = client.responses.create(
model="grok-4.20-0309-reasoning",
input="Solve this complex problem...",
include=["reasoning.encrypted_content"]
)
# The encrypted reasoning can be reused in subsequent requests
Recommended migration strategy
Rather than migrating everything at once, proceed in steps:
- Identify Chat Completions calls in your code
- Classify them by complexity (basic → streaming → function calling)
- Migrate first new endpoints or features (greenfield)
- Then migrate the simplest existing calls
- Keep Chat Completions for third-party integrations that require it (LangChain, etc.)
Temporary coexistence
It is perfectly acceptable to run both endpoints side by side during the transition:
# Wrapper that abstracts the endpoint used
class GrokClient:
def __init__(self, use_responses: bool = False):
self.use_responses = use_responses
self.client = OpenAI(
api_key=os.getenv("XAI_API_KEY"),
base_url="https://api.x.ai/v1"
)
def generate(self, system: str, user_message: str) -> str:
if self.use_responses:
response = self.client.responses.create(
model="grok-4.20-0309-reasoning",
instructions=system,
input=user_message
)
return response.output_text
else:
response = self.client.chat.completions.create(
model="grok-4.20-0309-reasoning",
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user_message}
]
)
return response.choices[0].message.content
This approach lets you switch gradually by testing each migration individually.
Key takeaways
- Migration can be gradual: no need to rewrite everything at once
- Main changes:
messages→input/instructions,choices[0].message.content→output_text - Multi-turn conversations move from a messages array to a simple
previous_response_id - The Responses API gives access to exclusive features (web_search, storage, encrypted reasoning)
- Run both endpoints side by side during the transition and migrate by order of complexity
Test your knowledge
The legacy API understood—and migration prepared.
1. What does the legacy and stateless status of Chat Completions mean?
Answer: The endpoint remains supported but no longer evolves: stateless, it requires resending the entire history with every call—the Responses API is the modern path.
2. Which roles structure the messages?
Answer: system (and developer), user, assistant—the framework, the requests, the responses: the full history restarts with every request.
3. How do you use existing OpenAI code with Grok?
Answer: By changing base_url to https://api.x.ai/v1 and the API key—format compatibility handles the rest, including OpenAI SDK and Vercel AI SDK.
4. What do you read in the response and usage?
Answer: choices carries responses with finish_reason; usage details tokens, including reasoning_tokens for reasoning models—the basis for cost tracking.
5. How do you migrate cleanly to the Responses API?
Answer: Gradually: new developments in Responses, existing endpoints converted one by one with prompt re-testing—comparing the two formats guides the conversion.
Understand the legacy, build for the modern: the course’s gradual migration plan is your roadmap—without a big bang.