Migrating From API v1 to API v2
This guide serves as a reference for developers looking to update their code that uses Cohere API v1 in favor of the new v2 standard. It outlines the key differences and necessary changes when migrating from Cohere API v1 to v2 and the various aspects of the API, including chat functionality, RAG (Retrieval-Augmented Generation), and tool use. Each section provides code examples for both v1 and v2, highlighting the structural changes in request formats, response handling, and new features introduced in v2.
# ! pip install -U cohere
import cohere
# instantiating the old client
co_v1 = cohere.Client(api_key="<YOUR API KEY>")
# instantiating the new client
co_v2 = cohere.ClientV2(api_key="<YOUR API KEY>")General
Section titled “General”- v2:
modelis a required field for Embed, Rerank, Classify, and Chat.
- v2:
embedding_typesis a required field for Embed.
Messages
Section titled “Messages”-
Message structure:
- v1: uses separate
preambleandmessageparameters. - v2: uses a single
messagesparameter consisting of a list of roles (system,user,assistant, ortool). Thesystemrole in v2 replaces thepreambleparameter in v1.
- v1: uses separate
-
Chat history:
- v1: manages the chat history via the
chat_historyparameter. - v2: manages the chat history via the
messageslist.
- v1: manages the chat history via the
v1
res = co_v1.chat(
model="command-a-03-2025",
preamble="You respond in concise sentences.",
chat_history=[
{"role": "user", "message": "Hello"},
{
"role": "chatbot",
"message": "Hi, how can I help you today?",
},
],
message="I'm joining a new startup called Co1t today. Could you help me write a one-sentence introduction message to my teammates?",
)
print(res.text)Excited to join the team at Co1t, where I look forward to contributing my skills and collaborating with everyone to drive innovation and success.v2
res = co_v2.chat(
model="command-a-plus-05-2026",
messages=[
{
"role": "system",
"content": "You respond in concise sentences.",
},
{"role": "user", "content": "Hello"},
{
"role": "assistant",
"content": "Hi, how can I help you today?",
},
{
"role": "user",
"content": "I'm joining a new startup called Co1t today. Could you help me write a one-sentence introduction message to my teammates.",
},
],
)
print(res.message.content[0].text)Excited to join the team at Co1t, bringing my passion for innovation and a background in [your expertise] to contribute to the company's success!Response content
Section titled “Response content”- v1: Accessed via
text - v2: Accessed via
message.content[0].text
v1
res = co_v1.chat(model="command-a-03-2025", message="What is 2 + 2")
print(res.text)The answer is 4.v2
res = co_v2.chat(
model="command-a-plus-05-2026",
messages=[{"role": "user", "content": "What is 2 + 2"}],
)
print(res.message.content[0].text)The answer is 4.Streaming
Section titled “Streaming”-
Events containing content:
- v1:
chunk.event_type == "text-generation" - v2:
chunk.type == "content-delta"
- v1:
-
Accessing response content:
- v1:
chunk.text - v2:
chunk.delta.message.content.text
- v1:
v1
message = "I'm joining a new startup called Co1t today. Could you help me write a one-sentence introduction message to my teammates."
res = co_v1.chat_stream(model="command-a-03-2025", message=message)
for chunk in res:
if chunk.event_type == "text-generation":
print(chunk.text, end="")"Hi, I'm [your name] and I'm thrilled to join the Co1t team today as a [your role], eager to contribute my skills and ideas to help drive innovation and success for our startup!"v2
message = "I'm joining a new startup called Co1t today. Could you help me write a one-sentence introduction message to my teammates."
res = co_v2.chat_stream(
model="command-a-plus-05-2026",
messages=[{"role": "user", "content": message}],
)
for chunk in res:
if chunk:
if chunk.type == "content-delta":
print(chunk.delta.message.content.text, end="")"Hi everyone, I'm thrilled to join the Co1t team today and look forward to contributing my skills and ideas to drive innovation and success!"Documents
Section titled “Documents”- v1: the
documentsparameter supports a list of objects with multiple fields per document. - v2: the
documentsparameter supports a few different options for structuring documents:- List of objects with
dataobject: same as v1 described above, but each document passed as adataobject (with an optionalidfield to be used in citations). - List of objects with
datastring (with an optionalidfield to be used in citations). - List of strings.
- List of objects with
v1
# Define the documents
documents_v1 = [
{
"text": "Reimbursing Travel Expenses: Easily manage your travel expenses by submitting them through our finance tool. Approvals are prompt and straightforward."
},
{
"text": "Health and Wellness Benefits: We care about your well-being and offer gym memberships, on-site yoga classes, and comprehensive health insurance."
},
]
# The user query
message = "Are there fitness-related benefits?"
# Generate the response
res_v1 = co_v1.chat(
model="command-a-03-2025",
message=message,
documents=documents_v1,
)
print(res_v1.text)Yes, there are fitness-related benefits. We offer gym memberships, on-site yoga classes, and comprehensive health insurance.v2
# Define the documents
documents_v2 = [
{
"data": {
"text": "Reimbursing Travel Expenses: Easily manage your travel expenses by submitting them through our finance tool. Approvals are prompt and straightforward."
}
},
{
"data": {
"text": "Health and Wellness Benefits: We care about your well-being and offer gym memberships, on-site yoga classes, and comprehensive health insurance."
}
},
]
# The user query
message = "Are there fitness-related benefits?"
# Generate the response
res_v2 = co_v2.chat(
model="command-a-plus-05-2026",
messages=[{"role": "user", "content": message}],
documents=documents_v2,
)
print(res_v2.message.content[0].text)Yes, we offer gym memberships, on-site yoga classes, and comprehensive health insurance.The following is a list of the the different options for structuring documents for RAG in v2.
documents_v2 = [
# List of objects with data string
{
"id": "123",
"data": "I love penguins. they are fluffy",
},
# List of objects with data object
{
"id": "456",
"data": {
"text": "I love penguins. they are fluffy",
"author": "Abdullah",
"create_date": "09021989",
},
},
# List of strings
"just a string",
]Citations
Section titled “Citations”- Citations access:
- v1:
citations - v2:
message.citations
- v1:
- Cited documents access:
- v1:
documents - v2: as part of
message.citations, in thesourcesfield
- v1:
v1
# Yes, there are fitness-related benefits. We offer gym memberships, on-site yoga classes, and comprehensive health insurance.
print(res_v1.citations)
print(res_v1.documents)[ChatCitation(start=50, end=124, text='gym memberships, on-site yoga classes, and comprehensive health insurance.', document_ids=['doc_1'])]
[{'id': 'doc_1', 'text': 'Health and Wellness Benefits: We care about your well-being and offer gym memberships, on-site yoga classes, and comprehensive health insurance.'}]v2
# Yes, we offer gym memberships, on-site yoga classes, and comprehensive health insurance.
print(res_v2.message.citations)[Citation(start=14, end=88, text='gym memberships, on-site yoga classes, and comprehensive health insurance.', sources=[DocumentSource(type='document', id='doc:1', document={'id': 'doc:1', 'text': 'Health and Wellness Benefits: We care about your well-being and offer gym memberships, on-site yoga classes, and comprehensive health insurance.'})])]Search query generation
Section titled “Search query generation”- v1: Uses
search_queries_onlyparameter - v2: Supported via tools. We recommend using the v1 API for this functionality in order to leverage the
force_single_stepfeature. Support in v2 will be coming soon.
Connectors
Section titled “Connectors”- v1: Supported via the
connectorsparameter - v2: Supported via user-defined tools.
Web search
Section titled “Web search”- v1: Supported via the
web-searchconnector in theconnectorsparameter - v2: Supported via user-defined tools.
v1
Uses the web search connector to search the internet for information relevant to the user's query.
res_v1 = co_v1.chat(
message="who won euro 2024",
connectors=[{"id": "web-search"}],
)
print(res_v1.text)Spain won the UEFA Euro 2024, defeating England 2-1 in the final.v2
Web search functionality is supported via tools.
# Any search engine can be used. This example uses the Tavily API.
from tavily import TavilyClient
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
# Create a web search function
def web_search(queries: list[str]) -> list[dict]:
documents = []
for query in queries:
response = tavily_client.search(query, max_results=2)
results = [
{
"title": r["title"],
"content": r["content"],
"url": r["url"],
}
for r in response["results"]
]
for idx, result in enumerate(results):
document = {"id": str(idx), "data": result}
documents.append(document)
return documents
# Define the web search tool
web_search_tool = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "Returns a list of relevant document snippets for a textual query retrieved from the internet",
"parameters": {
"type": "object",
"properties": {
"queries": {
"type": "array",
"items": {"type": "string"},
"description": "a list of queries to search the internet with.",
}
},
"required": ["queries"],
},
},
}
]
# The user query
query = "who won euro 2024"
# Define a system message to optimize search query generation
instructions = "Write a search query that will find helpful information for answering the user's question accurately. If you need more than one search query, write a list of search queries. If you decide that a search is very unlikely to find information that would be useful in constructing a response to the user, you should instead directly answer."
messages = [
{"role": "system", "content": instructions},
{"role": "user", "content": query},
]
model = "command-a-plus-05-2026"
# Generate search queries (if any)
response = co_v2.chat(
model=model, messages=messages, tools=web_search_tool
)
search_queries = []
while response.message.tool_calls:
print("Tool plan:")
print(response.message.tool_plan, "\n")
print("Tool calls:")
for tc in response.message.tool_calls:
print(
f"Tool name: {tc.function.name} | Parameters: {tc.function.arguments}"
)
print("=" * 50)
messages.append(response.message)
# Step 3: Get tool results
for idx, tc in enumerate(response.message.tool_calls):
tool_result = web_search(**json.loads(tc.function.arguments))
tool_content = []
for data in tool_result:
tool_content.append(
{
"type": "document",
"document": {"data": json.dumps(data)},
}
)
# Optional: add an "id" field in the "document" object, otherwise IDs are auto-generated
messages.append(
{
"role": "tool",
"tool_call_id": tc.id,
"content": tool_content,
}
)
# Step 4: Generate response and citations
response = co_v2.chat(
model=model, messages=messages, tools=web_search_tool
)
print(response.message.content[0].text)Tool plan:
I will search for 'who won euro 2024' to find out who won the competition.
Tool calls:
Tool name: web_search | Parameters: {"queries":["who won euro 2024"]}
==================================================
Spain won the 2024 European Championship. They beat England in the final, with substitute Mikel Oyarzabal scoring the winning goal.Streaming
Section titled “Streaming”-
Event containing content:
- v1:
chunk.event_type == "text-generation" - v2:
chunk.type == "content-delta"
- v1:
-
Accessing response content:
- v1:
chunk.text - v2:
chunk.delta.message.content.text
- v1:
-
Events containing citations:
- v1:
chunk.event_type == "citation-generation" - v2:
chunk.type == "citation-start"
- v1:
-
Accessing citations:
- v1:
chunk.citations - v2:
chunk.delta.message.citations
- v1:
v1
message = "Are there fitness-related benefits?"
res_v1 = co_v1.chat_stream(
model="command-a-03-2025",
message=message,
documents=documents_v1,
)
for chunk in res_v1:
if chunk.event_type == "text-generation":
print(chunk.text, end="")
if chunk.event_type == "citation-generation":
print(f"\n{chunk.citations}")Yes, we offer gym memberships, on-site yoga classes, and comprehensive health insurance as part of our health and wellness benefits.
[ChatCitation(start=14, end=87, text='gym memberships, on-site yoga classes, and comprehensive health insurance', document_ids=['doc_1'])]
[ChatCitation(start=103, end=132, text='health and wellness benefits.', document_ids=['doc_1'])]v2
message = "Are there fitness-related benefits?"
messages = [{"role": "user", "content": message}]
res_v2 = co_v2.chat_stream(
model="command-a-plus-05-2026",
messages=messages,
documents=documents_v2,
)
for chunk in res_v2:
if chunk:
if chunk.type == "content-delta":
print(chunk.delta.message.content.text, end="")
if chunk.type == "citation-start":
print(f"\n{chunk.delta.message.citations}")Yes, we offer gym memberships, on-site yoga classes, and comprehensive health insurance.
start=14 end=88 text='gym memberships, on-site yoga classes, and comprehensive health insurance.' sources=[DocumentSource(type='document', id='doc:1', document={'id': 'doc:1', 'text': 'Health and Wellness Benefits: We care about your well-being and offer gym memberships, on-site yoga classes, and comprehensive health insurance.'})]Tool use
Section titled “Tool use”Tool definition
Section titled “Tool definition”- v1: uses Python types to define tools.
- v2: uses JSON schema to define tools.
v1
def get_weather(location):
return {"temperature": "20C"}
functions_map = {"get_weather": get_weather}
tools_v1 = [
{
"name": "get_weather",
"description": "Gets the weather of a given location",
"parameter_definitions": {
"location": {
"description": "The location to get weather, example: San Francisco, CA",
"type": "str",
"required": True,
}
},
},
]v2
def get_weather(location):
return [{"temperature": "20C"}]
# You can return a list of objects e.g. [{"url": "abc.com", "text": "..."}, {"url": "xyz.com", "text": "..."}]
functions_map = {"get_weather": get_weather}
tools_v2 = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "gets the weather of a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "the location to get weather, example: San Fransisco, CA",
}
},
"required": ["location"],
},
},
},
]Tool calling
Section titled “Tool calling”-
Response handling
- v1: Tool calls accessed through
response.tool_calls - v2: Tool calls accessed through
response.message.tool_calls
- v1: Tool calls accessed through
-
Chat history management
- v1: Tool calls stored in the response's
chat_history - v2: Append the tool call details (
tool_callsandtool_plan) to themessageslist
- v1: Tool calls stored in the response's
v1
message = "What's the weather in Toronto?"
res_v1 = co_v1.chat(
model="command-a-03-2025", message=message, tools=tools_v1
)
print(res_v1.tool_calls)[ToolCall(name='get_weather', parameters={'location': 'Toronto'})]v2
messages = [
{"role": "user", "content": "What's the weather in Toronto?"}
]
res_v2 = co_v2.chat(
model="command-a-plus-05-2026", messages=messages, tools=tools_v2
)
if res_v2.message.tool_calls:
messages.append(res_v2.message)
print(res_v2.message.tool_calls)[ToolCallV2(id='get_weather_k88p0m8504w5', type='function', function=ToolCallV2Function(name='get_weather', arguments='{"location":"Toronto"}'))]Tool call ID
Section titled “Tool call ID”- v1: Tool calls do not emit tool call IDs
- v2: Tool calls emit tool call IDs. This will help the model match tool results to the right tool call.
v1
tool_results = [
{
"call": {
"name": "<tool name>",
"parameters": {"<param name>": "<param value>"},
},
"outputs": [{"<key>": "<value>"}],
},
]v2
messages = [
{
"role": "tool",
"tool_call_id": "123",
"content": [
{
"type": "document",
"document": {
"id": "123",
"data": {"<key>": "<value>"},
},
}
],
}
]Response generation
Section titled “Response generation”-
Tool execution: Chat history management
- v1: Append
callandoutputsto the chat history - v2: Append
tool_call_idandtool_contenttomessagesto the chat history
- v1: Append
-
Tool execution: Tool results
- v1: Passed as
tool_resultsparameter - v2: Incorporated into the
messageslist as tool responses
- v1: Passed as
-
User message
- v1: Set as empty (
"") - v2: No action required
- v1: Set as empty (
v1
tool_content_v1 = []
if res_v1.tool_calls:
for tc in res_v1.tool_calls:
tool_call = {"name": tc.name, "parameters": tc.parameters}
tool_result = functions_map[tc.name](**tc.parameters)
tool_content_v1.append(
{"call": tool_call, "outputs": [tool_result]}
)
res_v1 = co_v1.chat(
model="command-a-03-2025",
message="",
tools=tools_v1,
tool_results=tool_content_v1,
chat_history=res_v1.chat_history,
)
print(res_v1.text)It is currently 20°C in Toronto.v2
if res_v2.message.tool_calls:
for tc in res_v2.message.tool_calls:
tool_result = functions_map[tc.function.name](
**json.loads(tc.function.arguments)
)
tool_content_v2 = []
for data in tool_result:
tool_content_v2.append(
{
"type": "document",
"document": {"data": json.dumps(data)},
}
)
# Optional: add an "id" field in the "document" object, otherwise IDs are auto-generated
messages.append(
{
"role": "tool",
"tool_call_id": tc.id,
"content": tool_content_v2,
}
)
res_v2 = co_v2.chat(
model="command-a-plus-05-2026", messages=messages, tools=tools_v2
)
print(res_v2.message.content[0].text)It's 20°C in Toronto.Citations
Section titled “Citations”- Citations access:
- v1:
citations - v2:
message.citations
- v1:
- Cited tools access:
- v1:
documents - v2: as part of
message.citations, in thesourcesfield
- v1:
v1
print(res_v1.citations)
print(res_v1.documents)[ChatCitation(start=16, end=20, text='20°C', document_ids=['get_weather:0:2:0'])]
[{'id': 'get_weather:0:2:0', 'temperature': '20C', 'tool_name': 'get_weather'}]v2
print(res_v2.message.citations)[Citation(start=5, end=9, text='20°C', sources=[ToolSource(type='tool', id='get_weather_k88p0m8504w5:0', tool_output={'temperature': '20C'})])]Streaming
Section titled “Streaming”-
Event containing content:
- v1:
chunk.event_type == "text-generation" - v2:
chunk.type == "content-delta"
- v1:
-
Accessing response content:
- v1:
chunk.text - v2:
chunk.delta.message.content.text
- v1:
-
Events containing citations:
- v1:
chunk.event_type == "citation-generation" - v2:
chunk.type == "citation-start"
- v1:
-
Accessing citations:
- v1:
chunk.citations - v2:
chunk.delta.message.citations
- v1:
v1
tool_content_v1 = []
if res_v1.tool_calls:
for tc in res_v1.tool_calls:
tool_call = {"name": tc.name, "parameters": tc.parameters}
tool_result = functions_map[tc.name](**tc.parameters)
tool_content_v1.append(
{"call": tool_call, "outputs": [tool_result]}
)
res_v1 = co_v1.chat_stream(
message="",
tools=tools_v1,
tool_results=tool_content_v1,
chat_history=res_v1.chat_history,
)
for chunk in res_v1:
if chunk.event_type == "text-generation":
print(chunk.text, end="")
if chunk.event_type == "citation-generation":
print(f"\n{chunk.citations}")It's 20°C in Toronto.
[ChatCitation(start=5, end=9, text='20°C', document_ids=['get_weather:0:2:0', 'get_weather:0:4:0'])]v2
if res_v2.message.tool_calls:
for tc in res_v2.message.tool_calls:
tool_result = functions_map[tc.function.name](
**json.loads(tc.function.arguments)
)
tool_content_v2 = []
for data in tool_result:
tool_content_v2.append(
{
"type": "document",
"document": {"data": json.dumps(data)},
}
)
# Optional: add an "id" field in the "document" object, otherwise IDs are auto-generated
messages.append(
{
"role": "tool",
"tool_call_id": tc.id,
"content": tool_content_v2,
}
)
res_v2 = co_v2.chat_stream(
model="command-a-plus-05-2026", messages=messages, tools=tools_v2
)
for chunk in res_v2:
if chunk:
if chunk.type == "content-delta":
print(chunk.delta.message.content.text, end="")
elif chunk.type == "citation-start":
print(f"\n{chunk.delta.message.citations}")It's 20°C in Toronto.
start=5 end=9 text='20°C' sources=[ToolSource(type='tool', id='get_weather_k88p0m8504w5:0', tool_output={'temperature': '20C'})]Citation quality (both RAG and tool use)
Section titled “Citation quality (both RAG and tool use)”- v1: controlled via
citation_qualityparameter - v2: controlled via
citation_optionsparameter (withmodeas a key)
Unsupported features in v2
Section titled “Unsupported features in v2”The following v1 features are not supported in v2:
- General chat
conversation_idparameter (chat history is now managed by the developer via themessagesparameter)
- RAG
search_queries_onlyparameterconnectorsparameterprompt_truncationparameter
- Tool use
force_single_stepparameter (all tool calls are now multi-step by default)