> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modelstudio.console.alibabacloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deep research (Qwen-Deep-Research)

> Automates complex research through planning, multiple rounds of web searches, and structured report generation. Gathers and synthesizes information without manual effort.

<Note>
  This document applies only to the Chinese mainland (Beijing) region. To use the model, use an [API key](https://modelstudio.console.alibabacloud.com/model/settings/api-key) from the Chinese mainland (Beijing) region.
</Note>

## Getting started <span id="e3fc617de8ilo" /> <span id="7991a2ea8f0ty" />

[Obtain an API key](/en/model-studio/get-api-key) and [export the API key as an environment variable](/en/model-studio/get-api-key). If you use an SDK to make calls, [install the DashScope SDK](/en/model-studio/install-sdk).

The model uses a two-step workflow: a follow-up question step (the model clarifies your research scope) and a deep research step (the model searches, analyzes, and generates a report). The follow-up question lets the model understand exactly what to investigate before starting a lengthy research run.

> Currently, the model does not support DashScope SDK for Java or OpenAI-compatible API calls.

<CodeGroup dropdown>
  ```python expandable
  import os
  import dashscope

  # The following is the base_url for the Beijing region.
  dashscope.base_http_api_url = 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1'

  # Configure the API key
  # If not set, replace the following line with your Model Studio API key (format: sk-xxx)
  API_KEY = os.getenv('DASHSCOPE_API_KEY')

  def call_deep_research_model(messages, step_name):
      print(f"\n=== {step_name} ===")

      try:
          responses = dashscope.Generation.call(
              api_key=API_KEY,
              model="qwen-deep-research",
              messages=messages,
              # The qwen-deep-research model currently only supports streaming output
              stream=True
              # incremental_output=True Add this parameter for incremental output
          )

          return process_responses(responses, step_name)

      except Exception as e:
          print(f"An error occurred when calling the API: {e}")
          return ""

  # Display phase content
  def display_phase_content(phase, content, status):
      if content:
          print(f"\n[{phase}] {status}: {content}")
      else:
          print(f"\n[{phase}] {status}")

  # Process the response
  def process_responses(responses, step_name):
      current_phase = None
      phase_content = ""
      research_goal = ""
      web_sites = []
      references = []
      keepalive_shown = False  # Flag to check if the KeepAlive prompt has been shown

      for response in responses:
          # Check the response status code
          if hasattr(response, 'status_code') and response.status_code != 200:
              print(f"HTTP return code: {response.status_code}")
              if hasattr(response, 'code'):
                  print(f"Error code: {response.code}")
              if hasattr(response, 'message'):
                  print(f"Error message: {response.message}")
              print("For more information, see: https://www.alibabacloud.com/help/model-studio/error-code")
              continue

          if hasattr(response, 'output') and response.output:
              message = response.output.get('message', {})
              phase = message.get('phase')
              content = message.get('content', '')
              status = message.get('status')
              extra = message.get('extra', {})

              # Phase change detection
              if phase != current_phase:
                  if current_phase and phase_content:
                      # Display different completion descriptions based on phase and step names
                      if step_name == "Step 1: Model query confirmation" and current_phase == "answer":
                          print(f"\n Query confirmation phase completed")
                      else:
                          print(f"\n {current_phase} phase completed")
                  current_phase = phase
                  phase_content = ""
                  keepalive_shown = False  # Reset KeepAlive prompt flag

                  # Display different descriptions based on phase and step names
                  if step_name == "Step 1: Model query confirmation" and phase == "answer":
                      print(f"\n Entering query confirmation phase")
                  else:
                      print(f"\n Entering {phase} phase")

              # Process reference information in the Answer phase
              if phase == "answer":
                  if extra.get('deep_research', {}).get('references'):
                      new_references = extra['deep_research']['references']
                      if new_references and new_references != references:  # Avoid duplicate display
                          references = new_references
                          print(f"\n   References ({len(references)}):")
                          for i, ref in enumerate(references, 1):
                              print(f"     {i}. {ref.get('title', 'No title')}")
                              if ref.get('url'):
                                  print(f"        URL: {ref['url']}")
                              if ref.get('description'):
                                  print(f"        Description: {ref['description'][:100]}...")
                              print()

              # Process special information in the WebResearch phase
              # Note: The qwen-deep-research-2025-12-15 model uses the streamingThinking status
              # instead of streamingQueries and streamingWebResult
              if phase == "WebResearch":
                  if extra.get('deep_research', {}).get('research'):
                      research_info = extra['deep_research']['research']

                      # Process streamingThinking (snapshot model) or streamingQueries (mainline model) status
                      if status in ("streamingThinking", "streamingQueries"):
                          if 'researchGoal' in research_info:
                              goal = research_info['researchGoal']
                              if goal:
                                  research_goal += goal
                                  print(f"\n   Research goal: {goal}", end='', flush=True)

                      # Process streamingWebResult status (mainline model)
                      # The snapshot model merges this status using streamingThinking
                      elif status == "streamingWebResult":
                          if 'webSites' in research_info:
                              sites = research_info['webSites']
                              if sites and sites != web_sites:  # Avoid duplicate display
                                  web_sites = sites
                                  print(f"\n   Found {len(sites)} relevant websites:")
                                  for i, site in enumerate(sites, 1):
                                      print(f"     {i}. {site.get('title', 'No title')}")
                                      print(f"        Description: {site.get('description', 'No description')[:100]}...")
                                      print(f"        URL: {site.get('url', 'No link')}")
                                      if site.get('favicon'):
                                          print(f"        Icon: {site['favicon']}")
                                      print()

                      # Process WebResultFinished status
                      elif status == "WebResultFinished":
                          print(f"\n   Web search completed. Found {len(web_sites)} reference sources.")
                          if research_goal:
                              print(f"   Research goal: {research_goal}")

              # Accumulate and display content
              if content:
                  phase_content += content
                  # Display content in real-time
                  print(content, end='', flush=True)

              # Display phase status changes
              if status and status != "typing":
                  print(f"\n   Status: {status}")

                  # Display status description
                  if status == "streamingThinking":
                      print("   → Decomposing research tasks and summarizing web content (WebResearch phase)")
                  elif status == "streamingQueries":
                      print("   → Generating research goals and search queries (WebResearch phase)")
                  elif status == "streamingWebResult":
                      print("   → Performing searches, web page reading, and code execution (WebResearch phase)")
                  elif status == "WebResultFinished":
                      print("   → Web search phase completed (WebResearch phase)")

              # When status is finished, display token consumption
              if status == "finished":
                  if hasattr(response, 'usage') and response.usage:
                      usage = response.usage
                      print(f"\n    Token consumption statistics:")
                      print(f"      Input tokens: {usage.get('input_tokens', 0)}")
                      print(f"      Output tokens: {usage.get('output_tokens', 0)}")
                      print(f"      Request ID: {response.get('request_id', 'Unknown')}")

              if phase == "KeepAlive":
                  # Only display the prompt the first time entering the KeepAlive phase
                  if not keepalive_shown:
                      print("Current step completed. Preparing for the next step.")
                      keepalive_shown = True
                  continue

      if current_phase and phase_content:
          if step_name == "Step 1: Model query confirmation" and current_phase == "answer":
              print(f"\n Query confirmation phase completed")
          else:
              print(f"\n {current_phase} phase completed")

      return phase_content

  def main():
      # Check API key
      if not API_KEY:
          print("Error: DASHSCOPE_API_KEY environment variable not set")
          print("Set the environment variable or modify the API_KEY variable directly in the code")
          return

      print("User initiates conversation: Research the application of artificial intelligence in education")

      # Step 1: Model query confirmation
      # The model analyzes the user's question and asks clarifying questions to define the research direction
      messages = [{'role': 'user', 'content': 'Research the application of artificial intelligence in education'}]
      step1_content = call_deep_research_model(messages, "Step 1: Model query confirmation")

      # Step 2: Deep research
      # Based on the query confirmation from Step 1, the model performs the full research process
      messages = [
          {'role': 'user', 'content': 'Research the application of artificial intelligence in education'},
          {'role': 'assistant', 'content': step1_content},  # Includes the model's query confirmation content
          {'role': 'user', 'content': 'I mainly focus on personalized learning and intelligent assessment'}
      ]

      call_deep_research_model(messages, "Step 2: Deep research")
      print("\n Research completed!")

  if __name__ == "__main__":
      main()
  ```

  ```bash expandable
  echo "Step 1: Model query confirmation"
  # The following is the base_url for the Beijing region.
  curl --location 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/text-generation/generation' \
  --header 'X-DashScope-SSE: enable' \
  --header "Authorization: Bearer $DASHSCOPE_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
      "input": {
          "messages": [
              {
                  "content": "Research the application of artificial intelligence in education",
                  "role": "user"
              }
          ]
      },
      "model": "qwen-deep-research"
  }'

  echo -e "\n\n"
  echo "Step 2: Deep research"
  # The following is the base_url for the Beijing region.
  curl --location 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/text-generation/generation' \
  --header 'X-DashScope-SSE: enable' \
  --header "Authorization: Bearer $DASHSCOPE_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
      "input": {
          "messages": [
              {
                  "content": "Research the application of artificial intelligence in education",
                  "role": "user"
              },
              {
                  "content": "Tell me which specific application scenarios of artificial intelligence in education you want to focus on?",
                  "role": "assistant"
              },
              {
                  "content": "I mainly focus on personalized learning",
                  "role": "user"
              }
          ]
      },
      "model": "qwen-deep-research"
  }'
  ```
</CodeGroup>

## Specifications <span id="caabe0e04a4pw" /> <span id="ed3b0cbe3cbss" />

<table style={{ display: "table", tableLayout: "fixed", width: "100%" }}><colgroup><col style={{ width: "24.96%" }} /><col style={{ width: "24.96%" }} /><col style={{ width: "25.04%" }} /><col style={{ width: "25.04%" }} /></colgroup><thead><tr><th><p><strong>Model</strong></p></th><th><p><strong>Context window (tokens)</strong></p></th><th><p><strong>Max input (tokens)</strong></p></th><th><p><strong>Max output (tokens)</strong></p></th></tr></thead><tbody><tr><td><p>qwen-deep-research</p></td><td rowSpan={2}><p>1,000,000</p></td><td rowSpan={2}><p>997,952</p></td><td rowSpan={2}><p>32,768</p></td></tr><tr><td><p>qwen-deep-research-2025-12-15</p></td></tr></tbody></table>

<Note>
  `qwen-deep-research`: mainline model, continuously updated. `qwen-deep-research-2025-12-15`: snapshot version with improved depth, quality, and <strong>MCP tool calling</strong>. Both support <strong>image input</strong> and are billed separately.
</Note>

## Core capabilities <span id="1995ce02edlpq" /> <span id="25692a5373mv1" />

Track progress via `phase` (current task) and `status` (task progress).

<strong>Follow-up question and report generation (phase: "answer")</strong>

Analyzes your query, asks clarifying questions to define the scope, and generates the final research report.

Status values:

- `typing`: Generating text content
- `finished`: Text content generation completed

<strong>Research planning (phase: "ResearchPlanning")</strong>

Creates a research outline from your query.

Status values:

- `typing`: Generating the research plan
- `finished`: Research plan completed

<strong>Web search (phase: "WebResearch")</strong>

Performs multiple rounds of web searches and content analysis. `WebResultFinished` signals the end of each round. `finished` signals the end of the phase.

Status values:

- `streamingThinking`: Decomposing research tasks and summarizing web content (specific to `qwen-deep-research-2025-12-15`, replaces `streamingQueries` and `streamingWebResult`)
- `streamingQueries`: Generating search queries (for `qwen-deep-research` only)
- `streamingWebResult`: Performing web searches and analyzing web content (for `qwen-deep-research` only)
- `WebResultFinished`: Search round completed
- `finished`: Web search phase completed

<strong>Connection keepalive (phase: "KeepAlive")</strong>

Maintains the connection between long-running tasks. Ignore this phase and continue processing.

## Image input <span id="s4w8x9y0z1a2b" /> <span id="r3v7w8x9y0z1a" />

Both models support image input. The model analyzes the image and incorporates that content into its research. Use array format for the `content` field, passing `image` and `text` objects together.

- Supported formats: JPEG, PNG, BMP, WEBP. Maximum 10 MB per image.
- Up to 5 images per request. Supports public URLs and Base64 encoding.
- The response format is identical to text-only requests. The model generates a report based on the image content.

<strong>Request example</strong>

<CodeGroup dropdown>
  ```python expandable
  import os
  import dashscope

  # The following is the base_url for the Beijing region.
  dashscope.base_http_api_url = 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1'

  API_KEY = os.getenv('DASHSCOPE_API_KEY')

  messages = [
      {
          "role": "user",
          "content": [
              {"image": "https://example.aliyuncs.com/example.png"},
              {"text": "Analyze the data trends in this chart and conduct in-depth research on key findings"}
          ]
      }
  ]

  responses = dashscope.Generation.call(
      api_key=API_KEY,
      model="qwen-deep-research",
      messages=messages,
      stream=True
  )

  for response in responses:
      if hasattr(response, 'output') and response.output:
          message = response.output.get('message', {})
          content = message.get('content', '')
          if content:
              print(content, end='', flush=True)
  ```

  ```bash
  curl --location 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/text-generation/generation' \
  --header 'X-DashScope-SSE: enable' \
  --header "Authorization: Bearer $DASHSCOPE_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
      "input": {
          "messages": [
              {
                  "content": [
                      {"image": "https://example.aliyuncs.com/example.png"},
                      {"text": "Analyze the data trends in this chart and conduct in-depth research on key findings"}
                  ],
                  "role": "user"
              }
          ]
      },
      "model": "qwen-deep-research"
  }'
  ```
</CodeGroup>

## MCP tool calling <span id="q3v7w8x9y0z1a" /> <span id="p2u6v7w8x9y0z" />

<Note>
  MCP tool calling is only supported by `qwen-deep-research-2025-12-15`. `qwen-deep-research` does not support this feature.
</Note>

Model Context Protocol (MCP) tool calling lets `qwen-deep-research-2025-12-15` pull from private or domain-specific data sources during the WebResearch phase—such as a knowledge base, internal documents, or a proprietary database—alongside standard web searches. Pass MCP server configuration through the `research_tools` parameter. The response format is identical to standard calls.

> For details about `research_tools` and MCP tool specifications, see [Qwen-Deep-Research](/en/model-studio/qwen-deep-research-api) .

### Request example <span id="o0s3t4u5v6w7x" /> <span id="n9r2s3t4u5v6w" />

<CodeGroup dropdown>
  ```python expandable
  import os
  import dashscope

  # The following is the base_url for the Beijing region.
  dashscope.base_http_api_url = 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1'

  API_KEY = os.getenv('DASHSCOPE_API_KEY')

  messages = [
      {
          "role": "user",
          "content": "Use the knowledge base to search for recently published product update announcements and compile them into a research report"
      }
  ]

  responses = dashscope.Generation.call(
      api_key=API_KEY,
      model="qwen-deep-research-2025-12-15",
      messages=messages,
      stream=True,
      enable_feedback=False,
      research_tools=[{
          "type": "mcp",
          "server_label": "my-server",
          "server_url": "https://your-mcp-server.example.com/sse",
          "allowed_tools": ["search", "fetch"],
          "authentication": {
              "bearer": "your_jwt_token_here"
          }
      }]
  )

  for response in responses:
      if hasattr(response, 'output') and response.output:
          message = response.output.get('message', {})
          content = message.get('content', '')
          if content:
              print(content, end='', flush=True)
  ```

  ```bash expandable
  curl --location 'https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/text-generation/generation' \
  --header 'X-DashScope-SSE: enable' \
  --header "Authorization: Bearer $DASHSCOPE_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
      "input": {
          "messages": [
              {
                  "content": "Use the knowledge base to search for recently published product update announcements and compile them into a research report",
                  "role": "user"
              }
          ]
      },
      "model": "qwen-deep-research-2025-12-15",
      "parameters": {
          "enable_feedback": false,
          "research_tools": [{
              "type": "mcp",
              "server_label": "my-server",
              "server_url": "https://your-mcp-server.example.com/sse",
              "allowed_tools": ["search", "fetch"],
              "authentication": {
                  "bearer": "your_jwt_token_here"
              }
          }]
      }
  }'
  ```
</CodeGroup>

## Billing <span id="a74bc7db2bb70" /> <span id="dbfc2681163nq" />

<table style={{ display: "table", tableLayout: "fixed", width: "100%" }}><colgroup><col style={{ width: "25%" }} /><col style={{ width: "25%" }} /><col style={{ width: "25%" }} /><col style={{ width: "25%" }} /></colgroup><thead><tr><th><p><strong>Model</strong></p></th><th><p><strong>Input cost (per 1K tokens)</strong></p></th><th><p><strong>Output cost (per 1K tokens)</strong></p></th><th><p><strong>Free quota</strong></p></th></tr></thead><tbody><tr><td><p>qwen-deep-research</p></td><td><p>\$0.007742</p></td><td><p>\$0.023367</p></td><td><p>No free quota</p></td></tr><tr><td><p>qwen-deep-research-2025-12-15</p></td><td><p>To be determined</p></td><td><p>To be determined</p></td><td><p>No free quota</p></td></tr></tbody></table>

Billing is based on input tokens (user messages and system prompts) and output tokens (follow-up questions, research plans, goals, search queries, and the final report). The two models are billed separately.

## Going live <span id="e73f7681ebe17" /> <span id="d6f7a0f0420vx" />

<strong>Use streaming output</strong>

The model only supports streaming output (`stream=True`). A single research task can run for several minutes across dozens of iterative search-and-read cycles, which exceeds the timeout of a synchronous request. Use streaming to keep the connection open and track progress via `phase` and `status` fields.

<strong>Handle errors</strong>

Check the response status code on each chunk. For non-200 status codes, read the `code` and `message` fields and handle them appropriately.

<strong>Monitor token usage</strong>

When `status` is `finished`, retrieve token usage from `response.usage` (input tokens, output tokens, and request ID).

<strong>Handle connection keepalive</strong>

The `KeepAlive` phase maintains the connection between long-running tasks. Ignore this phase and continue processing the stream.

## FAQ <span id="e030128758ws1" /> <span id="f2e7193dab3lb" />

- <strong>Why is the output field empty for some response chunks?</strong>

  Early chunks carry metadata only. Content arrives in subsequent chunks as the model generates it.
- <strong>How do I determine if a phase is complete?</strong>

  A phase completes when `status` changes to `finished`.
- <strong>Does the model support OpenAI-compatible API calls?</strong>

  No. OpenAI-compatible API calls are not supported.
- <strong>How are input and output tokens calculated?</strong>

  Input tokens: user messages and system prompts. Output tokens: follow-up questions, research plans, goals, search queries, and the final report.
- <strong>What is the difference between qwen-deep-research and qwen-deep-research-2025-12-15?</strong>

  `qwen-deep-research`: mainline model, continuously updated. `qwen-deep-research-2025-12-15`: snapshot version with improved depth, quality, and MCP support. Both support image input and are billed separately.
- <strong>How do I pass images for research?</strong>

  Use array format for `content`: pass `{"image": "URL"}` and `{"text": "description"}` as objects in the array. Both models support image input.
- <strong>How do I skip the follow-up question and go straight to research?</strong>

  Set `enable_feedback` to `false` in `parameters`. The model skips the follow-up question and starts research immediately.

## API reference <span id="7e9866a3cd2d7" /> <span id="a3bd6caa77gow" />

For input and output parameters, see [Qwen-Deep-Research](/en/model-studio/qwen-deep-research-api).

## Error codes <span id="3e8eb683f5chk" /> <span id="11c8aa2895vks" />

If the model call fails and returns an error message, see [Error codes](/en/model-studio/error-code) for resolution.

## Rate limiting <span id="78d5c053b093m" /> <span id="9272688c4evtl" />

See [Rate limiting](/en/model-studio/rate-limit).
